Merge pull request #135 from mirumee/add/dynamic-dashboard-settings
Add dynamic dashboard settings
This commit is contained in:
commit
a940e45a4d
37 changed files with 2867 additions and 762 deletions
|
@ -10,3 +10,4 @@ All notable, unreleased changes to this project will be documented in this file.
|
|||
- Add switch to make attribute available in product list as a column - #99 by @dominik-zeglen
|
||||
- Add tc tags for E2E testing - #134 by @dominik-zeglen
|
||||
- Use react-intl - #105 by @dominik-zeglen
|
||||
- Add dynamic dashboard settings - #135 by @benekex2
|
||||
|
|
41
package-lock.json
generated
41
package-lock.json
generated
|
@ -9409,7 +9409,8 @@
|
|||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
|
@ -9427,11 +9428,13 @@
|
|||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
@ -9444,15 +9447,18 @@
|
|||
},
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
|
@ -9555,7 +9561,8 @@
|
|||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
|
@ -9565,6 +9572,7 @@
|
|||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
|
@ -9577,17 +9585,20 @@
|
|||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
|
@ -9604,6 +9615,7 @@
|
|||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
|
@ -9676,7 +9688,8 @@
|
|||
},
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
|
@ -9686,6 +9699,7 @@
|
|||
"once": {
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
|
@ -9761,7 +9775,8 @@
|
|||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
|
@ -9791,6 +9806,7 @@
|
|||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
|
@ -9808,6 +9824,7 @@
|
|||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
|
@ -9846,11 +9863,13 @@
|
|||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -143,8 +143,8 @@ type AssignNavigation {
|
|||
|
||||
type Attribute implements Node {
|
||||
id: ID!
|
||||
productTypes(before: String, after: String, first: Int, last: Int): ProductTypeCountableConnection
|
||||
productVariantTypes(before: String, after: String, first: Int, last: Int): ProductTypeCountableConnection
|
||||
productTypes(before: String, after: String, first: Int, last: Int): ProductTypeCountableConnection!
|
||||
productVariantTypes(before: String, after: String, first: Int, last: Int): ProductTypeCountableConnection!
|
||||
privateMeta: [MetaStore]!
|
||||
meta: [MetaStore]!
|
||||
inputType: AttributeInputTypeEnum
|
||||
|
@ -228,6 +228,7 @@ input AttributeFilterInput {
|
|||
filterableInDashboard: Boolean
|
||||
availableInGrid: Boolean
|
||||
search: String
|
||||
ids: [ID]
|
||||
}
|
||||
|
||||
input AttributeInput {
|
||||
|
@ -518,7 +519,8 @@ type Checkout implements Node {
|
|||
shippingAddress: Address
|
||||
shippingMethod: ShippingMethod
|
||||
note: String!
|
||||
discountAmount: Money
|
||||
discountAmount: Money @deprecated(reason: "Use discount instead.")
|
||||
discount: Money
|
||||
discountName: String
|
||||
translatedDiscountName: String
|
||||
voucherCode: String
|
||||
|
@ -969,7 +971,6 @@ enum CountryCode {
|
|||
LT
|
||||
LU
|
||||
MO
|
||||
MK
|
||||
MG
|
||||
MW
|
||||
MY
|
||||
|
@ -1003,6 +1004,7 @@ enum CountryCode {
|
|||
NU
|
||||
NF
|
||||
KP
|
||||
MK
|
||||
MP
|
||||
NO
|
||||
OM
|
||||
|
@ -1867,6 +1869,7 @@ type Mutations {
|
|||
paymentCapture(amount: Decimal, paymentId: ID!): PaymentCapture
|
||||
paymentRefund(amount: Decimal, paymentId: ID!): PaymentRefund
|
||||
paymentVoid(paymentId: ID!): PaymentVoid
|
||||
paymentSecureConfirm(paymentId: ID!): PaymentSecureConfirm
|
||||
pageCreate(input: PageInput!): PageCreate
|
||||
pageDelete(id: ID!): PageDelete
|
||||
pageBulkDelete(ids: [ID]!): PageBulkDelete
|
||||
|
@ -1909,7 +1912,7 @@ type Mutations {
|
|||
giftCardCreate(input: GiftCardCreateInput!): GiftCardCreate
|
||||
giftCardDeactivate(id: ID!): GiftCardDeactivate
|
||||
giftCardUpdate(id: ID!, input: GiftCardUpdateInput!): GiftCardUpdate
|
||||
pluginConfigurationUpdate(id: ID!, input: PluginConfigurationUpdateInput!): PluginConfigurationUpdate
|
||||
pluginUpdate(id: ID!, input: PluginUpdateInput!): PluginUpdate
|
||||
saleCreate(input: SaleInput!): SaleCreate
|
||||
saleDelete(id: ID!): SaleDelete
|
||||
saleBulkDelete(ids: [ID]!): SaleBulkDelete
|
||||
|
@ -1946,8 +1949,9 @@ type Mutations {
|
|||
checkoutClearMetadata(id: ID!, input: MetaPath!): CheckoutClearStoredMeta
|
||||
checkoutUpdatePrivateMetadata(id: ID!, input: MetaInput!): CheckoutUpdatePrivateMeta
|
||||
checkoutClearPrivateMetadata(id: ID!, input: MetaPath!): CheckoutClearStoredPrivateMeta
|
||||
requestPasswordReset(email: String!): RequestPasswordReset
|
||||
setPassword(id: ID!, input: SetPasswordInput!): SetPassword
|
||||
requestPasswordReset(email: String!, redirectUrl: String!): RequestPasswordReset
|
||||
setPassword(token: String!, email: String!, password: String!): SetPassword
|
||||
passwordChange(newPassword: String!, oldPassword: String!): PasswordChange
|
||||
userUpdateMetadata(id: ID!, input: MetaInput!): UserUpdateMeta
|
||||
userClearStoredMetadata(id: ID!, input: MetaPath!): UserClearStoredMeta
|
||||
accountAddressCreate(input: AddressInput!, type: AddressTypeEnum): AccountAddressCreate
|
||||
|
@ -1956,7 +1960,7 @@ type Mutations {
|
|||
accountSetDefaultAddress(id: ID!, type: AddressTypeEnum!): AccountSetDefaultAddress
|
||||
accountRegister(input: AccountRegisterInput!): AccountRegister
|
||||
accountUpdate(input: AccountInput!): AccountUpdate
|
||||
accountRequestDeletion: AccountRequestDeletion
|
||||
accountRequestDeletion(redirectUrl: String!): AccountRequestDeletion
|
||||
accountDelete(token: String!): AccountDelete
|
||||
customerPasswordReset(input: CustomerPasswordResetInput!): CustomerPasswordReset
|
||||
customerAddressCreate(input: AddressInput!, type: AddressTypeEnum): CustomerAddressCreate
|
||||
|
@ -2011,12 +2015,12 @@ type Order implements Node {
|
|||
billingAddress: Address
|
||||
shippingAddress: Address
|
||||
shippingMethod: ShippingMethod
|
||||
shippingPrice: TaxedMoney
|
||||
shippingMethodName: String
|
||||
shippingPrice: TaxedMoney
|
||||
token: String!
|
||||
voucher: Voucher
|
||||
giftCards: [GiftCard]
|
||||
discountAmount: Money
|
||||
discount: Money
|
||||
discountName: String!
|
||||
translatedDiscountName: String!
|
||||
displayGrossPrices: Boolean!
|
||||
|
@ -2041,6 +2045,7 @@ type Order implements Node {
|
|||
totalBalance: Money!
|
||||
userEmail: String
|
||||
isShippingRequired: Boolean!
|
||||
discountAmount: Money! @deprecated(reason: "Deprecated: use discount instead.")
|
||||
}
|
||||
|
||||
enum OrderAction {
|
||||
|
@ -2341,6 +2346,11 @@ type PageUpdate {
|
|||
page: Page
|
||||
}
|
||||
|
||||
type PasswordChange {
|
||||
errors: [Error!]
|
||||
user: User
|
||||
}
|
||||
|
||||
type PasswordReset {
|
||||
errors: [Error!]
|
||||
}
|
||||
|
@ -2404,6 +2414,11 @@ type PaymentRefund {
|
|||
payment: Payment
|
||||
}
|
||||
|
||||
type PaymentSecureConfirm {
|
||||
errors: [Error!]
|
||||
payment: Payment
|
||||
}
|
||||
|
||||
type PaymentSource {
|
||||
gateway: String!
|
||||
creditCardInfo: CreditCard
|
||||
|
@ -2435,7 +2450,7 @@ enum PermissionEnum {
|
|||
MANAGE_TRANSLATIONS
|
||||
}
|
||||
|
||||
type PluginConfiguration implements Node {
|
||||
type Plugin implements Node {
|
||||
name: String!
|
||||
description: String!
|
||||
active: Boolean!
|
||||
|
@ -2443,23 +2458,23 @@ type PluginConfiguration implements Node {
|
|||
id: ID!
|
||||
}
|
||||
|
||||
type PluginConfigurationCountableConnection {
|
||||
type PluginCountableConnection {
|
||||
pageInfo: PageInfo!
|
||||
edges: [PluginConfigurationCountableEdge!]!
|
||||
edges: [PluginCountableEdge!]!
|
||||
totalCount: Int
|
||||
}
|
||||
|
||||
type PluginConfigurationCountableEdge {
|
||||
node: PluginConfiguration!
|
||||
type PluginCountableEdge {
|
||||
node: Plugin!
|
||||
cursor: String!
|
||||
}
|
||||
|
||||
type PluginConfigurationUpdate {
|
||||
type PluginUpdate {
|
||||
errors: [Error!]
|
||||
pluginConfiguration: PluginConfiguration
|
||||
plugin: Plugin
|
||||
}
|
||||
|
||||
input PluginConfigurationUpdateInput {
|
||||
input PluginUpdateInput {
|
||||
active: Boolean
|
||||
configuration: [ConfigurationItemInput]
|
||||
}
|
||||
|
@ -2493,6 +2508,7 @@ type Product implements Node {
|
|||
isAvailable: Boolean
|
||||
basePrice: Money
|
||||
price: Money @deprecated(reason: "Has been replaced by 'basePrice'")
|
||||
minimalVariantPrice: Money
|
||||
taxRate: TaxRateType @deprecated(reason: "taxRate is deprecated. Use taxType to obtain taxCode for given tax gateway")
|
||||
taxType: TaxType
|
||||
attributes: [SelectedAttribute!]!
|
||||
|
@ -2579,6 +2595,7 @@ input ProductFilterInput {
|
|||
stockAvailability: StockAvailability
|
||||
productType: ID
|
||||
search: String
|
||||
minimalPrice: PriceRangeInput
|
||||
}
|
||||
|
||||
type ProductImage implements Node {
|
||||
|
@ -2656,6 +2673,7 @@ input ProductOrder {
|
|||
enum ProductOrderField {
|
||||
NAME
|
||||
PRICE
|
||||
MINIMAL_PRICE
|
||||
DATE
|
||||
}
|
||||
|
||||
|
@ -2748,6 +2766,7 @@ enum ProductTypeEnum {
|
|||
}
|
||||
|
||||
input ProductTypeFilterInput {
|
||||
search: String
|
||||
configurable: ProductTypeConfigurable
|
||||
productType: ProductTypeEnum
|
||||
}
|
||||
|
@ -2925,7 +2944,7 @@ type Query {
|
|||
product(id: ID!): Product
|
||||
products(filter: ProductFilterInput, attributes: [AttributeScalar], categories: [ID], collections: [ID], priceLte: Float, priceGte: Float, sortBy: ProductOrder, stockAvailability: StockAvailability, query: String, before: String, after: String, first: Int, last: Int): ProductCountableConnection
|
||||
productType(id: ID!): ProductType
|
||||
productTypes(filter: ProductTypeFilterInput, before: String, after: String, first: Int, last: Int): ProductTypeCountableConnection
|
||||
productTypes(filter: ProductTypeFilterInput, query: String, before: String, after: String, first: Int, last: Int): ProductTypeCountableConnection
|
||||
productVariant(id: ID!): ProductVariant
|
||||
productVariants(ids: [ID], before: String, after: String, first: Int, last: Int): ProductVariantCountableConnection
|
||||
reportProductSales(period: ReportingPeriod!, before: String, after: String, first: Int, last: Int): ProductVariantCountableConnection
|
||||
|
@ -2946,8 +2965,8 @@ type Query {
|
|||
menuItems(query: String, before: String, after: String, first: Int, last: Int): MenuItemCountableConnection
|
||||
giftCard(id: ID!): GiftCard
|
||||
giftCards(before: String, after: String, first: Int, last: Int): GiftCardCountableConnection
|
||||
pluginConfiguration(id: ID!): PluginConfiguration
|
||||
pluginConfigurations(before: String, after: String, first: Int, last: Int): PluginConfigurationCountableConnection
|
||||
plugin(id: ID!): Plugin
|
||||
plugins(before: String, after: String, first: Int, last: Int): PluginCountableConnection
|
||||
sale(id: ID!): Sale
|
||||
sales(filter: SaleFilterInput, query: String, before: String, after: String, first: Int, last: Int): SaleCountableConnection
|
||||
voucher(id: ID!): Voucher
|
||||
|
@ -2957,7 +2976,7 @@ type Query {
|
|||
checkouts(before: String, after: String, first: Int, last: Int): CheckoutCountableConnection
|
||||
checkoutLine(id: ID): CheckoutLine
|
||||
checkoutLines(before: String, after: String, first: Int, last: Int): CheckoutLineCountableConnection
|
||||
addressValidationRules(countryCode: CountryCode, countryArea: String, cityArea: String): AddressValidationData
|
||||
addressValidationRules(countryCode: CountryCode!, countryArea: String, city: String, cityArea: String): AddressValidationData
|
||||
customers(filter: CustomerFilterInput, query: String, before: String, after: String, first: Int, last: Int): UserCountableConnection
|
||||
me: User
|
||||
staffUsers(filter: StaffUserInput, query: String, before: String, after: String, first: Int, last: Int): UserCountableConnection
|
||||
|
@ -3089,15 +3108,11 @@ input SeoInput {
|
|||
}
|
||||
|
||||
type SetPassword {
|
||||
errors: [Error!]
|
||||
token: String
|
||||
errors: [Error]!
|
||||
user: User
|
||||
}
|
||||
|
||||
input SetPasswordInput {
|
||||
token: String!
|
||||
password: String!
|
||||
}
|
||||
|
||||
type ShippingMethod implements Node {
|
||||
id: ID!
|
||||
name: String!
|
||||
|
@ -3418,6 +3433,7 @@ enum TransactionKind {
|
|||
REFUND
|
||||
CAPTURE
|
||||
VOID
|
||||
CONFIRM
|
||||
}
|
||||
|
||||
union TranslatableItem = Product | Category | Collection | Attribute | AttributeValue | ProductVariant | Page | ShippingMethod | Sale | Voucher | MenuItem
|
||||
|
@ -3466,7 +3482,6 @@ type User implements Node {
|
|||
firstName: String!
|
||||
lastName: String!
|
||||
isStaff: Boolean!
|
||||
token: UUID!
|
||||
isActive: Boolean!
|
||||
note: String
|
||||
dateJoined: DateTime!
|
||||
|
@ -3592,13 +3607,14 @@ type Voucher implements Node {
|
|||
applyOncePerCustomer: Boolean!
|
||||
discountValueType: DiscountValueTypeEnum!
|
||||
discountValue: Float!
|
||||
minAmountSpent: Money
|
||||
minSpent: Money
|
||||
minCheckoutItemsQuantity: Int
|
||||
categories(before: String, after: String, first: Int, last: Int): CategoryCountableConnection
|
||||
collections(before: String, after: String, first: Int, last: Int): CollectionCountableConnection
|
||||
products(before: String, after: String, first: Int, last: Int): ProductCountableConnection
|
||||
countries: [CountryDisplay]
|
||||
translation(languageCode: LanguageCodeEnum!): VoucherTranslation
|
||||
minAmountSpent: Money @deprecated(reason: "Use the minSpent field instead.")
|
||||
}
|
||||
|
||||
type VoucherAddCatalogues {
|
||||
|
|
|
@ -23,10 +23,10 @@ export interface TableHeadProps extends MuiTableHeadProps {
|
|||
colSpan: number;
|
||||
disabled: boolean;
|
||||
dragRows?: boolean;
|
||||
selected: number;
|
||||
selected?: number;
|
||||
items: Node[];
|
||||
toolbar: React.ReactNode | React.ReactNodeArray;
|
||||
toggleAll: (items: Node[], selected: number) => void;
|
||||
toolbar?: React.ReactNode | React.ReactNodeArray;
|
||||
toggleAll?: (items: Node[], selected: number) => void;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
|
@ -101,25 +101,26 @@ const TableHead = withStyles(styles, {
|
|||
})}
|
||||
/>
|
||||
)}
|
||||
{(items === undefined || items.length > 0) && (
|
||||
<TableCell
|
||||
padding="checkbox"
|
||||
className={classNames({
|
||||
[classes.checkboxSelected]: selected,
|
||||
[classes.dragRows]: dragRows
|
||||
})}
|
||||
>
|
||||
<Checkbox
|
||||
{(items === undefined || items.length > 0) &&
|
||||
(selected && (
|
||||
<TableCell
|
||||
padding="checkbox"
|
||||
className={classNames({
|
||||
[classes.checkboxPartialSelect]:
|
||||
items && items.length > selected && selected > 0
|
||||
[classes.checkboxSelected]: selected,
|
||||
[classes.dragRows]: dragRows
|
||||
})}
|
||||
checked={selected === 0 ? false : true}
|
||||
disabled={disabled}
|
||||
onChange={() => toggleAll(items, selected)}
|
||||
/>
|
||||
</TableCell>
|
||||
)}
|
||||
>
|
||||
<Checkbox
|
||||
className={classNames({
|
||||
[classes.checkboxPartialSelect]:
|
||||
items && items.length > selected && selected > 0
|
||||
})}
|
||||
checked={selected === 0 ? false : true}
|
||||
disabled={disabled}
|
||||
onChange={() => toggleAll(items, selected)}
|
||||
/>
|
||||
</TableCell>
|
||||
))}
|
||||
{selected ? (
|
||||
<>
|
||||
<TableCell
|
||||
|
@ -138,7 +139,7 @@ const TableHead = withStyles(styles, {
|
|||
</Typography>
|
||||
)}
|
||||
<div className={classes.spacer} />
|
||||
<div className={classes.toolbar}>{toolbar}</div>
|
||||
{toolbar && <div className={classes.toolbar}>{toolbar}</div>}
|
||||
</div>
|
||||
</TableCell>
|
||||
</>
|
||||
|
|
|
@ -21,6 +21,7 @@ export interface AppListViewSettings {
|
|||
[ListViews.NAVIGATION_LIST]: ListSettings;
|
||||
[ListViews.ORDER_LIST]: ListSettings;
|
||||
[ListViews.PAGES_LIST]: ListSettings;
|
||||
[ListViews.PLUGINS_LIST]: ListSettings;
|
||||
[ListViews.PRODUCT_LIST]: ListSettings<ProductListColumns>;
|
||||
[ListViews.SALES_LIST]: ListSettings;
|
||||
[ListViews.SHIPPING_METHODS_LIST]: ListSettings;
|
||||
|
@ -49,6 +50,9 @@ export const defaultListSettings: AppListViewSettings = {
|
|||
[ListViews.PAGES_LIST]: {
|
||||
rowNumber: PAGINATE_BY
|
||||
},
|
||||
[ListViews.PLUGINS_LIST]: {
|
||||
rowNumber: PAGINATE_BY
|
||||
},
|
||||
[ListViews.PRODUCT_LIST]: {
|
||||
columns: ["isPublished", "price", "productType"],
|
||||
rowNumber: PAGINATE_BY
|
||||
|
|
|
@ -7,6 +7,7 @@ import useNavigator from "@saleor/hooks/useNavigator";
|
|||
import useUser from "@saleor/hooks/useUser";
|
||||
import Navigation from "@saleor/icons/Navigation";
|
||||
import Pages from "@saleor/icons/Pages";
|
||||
import Plugins from "@saleor/icons/Plugins";
|
||||
import ProductTypes from "@saleor/icons/ProductTypes";
|
||||
import ShippingMethods from "@saleor/icons/ShippingMethods";
|
||||
import SiteSettings from "@saleor/icons/SiteSettings";
|
||||
|
@ -16,6 +17,7 @@ import { sectionNames } from "@saleor/intl";
|
|||
import { maybe } from "@saleor/misc";
|
||||
import { menuListUrl } from "@saleor/navigation/urls";
|
||||
import { pageListUrl } from "@saleor/pages/urls";
|
||||
import { pluginsListUrl } from "@saleor/plugins/urls";
|
||||
import { productTypeListUrl } from "@saleor/productTypes/urls";
|
||||
import { shippingZonesListUrl } from "@saleor/shipping/urls";
|
||||
import { siteSettingsUrl } from "@saleor/siteSettings/urls";
|
||||
|
@ -105,6 +107,22 @@ export function createConfigurationMenu(intl: IntlShape): MenuItem[] {
|
|||
permission: PermissionEnum.MANAGE_PAGES,
|
||||
title: intl.formatMessage(sectionNames.pages),
|
||||
url: pageListUrl()
|
||||
},
|
||||
{
|
||||
description: intl.formatMessage({
|
||||
defaultMessage: "View and update your plugins and their settings.",
|
||||
id: "configurationPluginsPages"
|
||||
}),
|
||||
icon: (
|
||||
<Plugins
|
||||
fontSize="inherit"
|
||||
viewBox="-8 -5 44 44"
|
||||
preserveAspectRatio="xMinYMin meet"
|
||||
/>
|
||||
),
|
||||
permission: PermissionEnum.MANAGE_SETTINGS,
|
||||
title: intl.formatMessage(sectionNames.plugins),
|
||||
url: pluginsListUrl()
|
||||
}
|
||||
];
|
||||
}
|
||||
|
|
17
src/icons/Plugins.tsx
Normal file
17
src/icons/Plugins.tsx
Normal file
|
@ -0,0 +1,17 @@
|
|||
import createSvgIcon from "@material-ui/icons/utils/createSvgIcon";
|
||||
import React from "react";
|
||||
|
||||
export const Plugins = createSvgIcon(
|
||||
<>
|
||||
<g>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M7.12891 6.10352e-05H12.9999V7.80006H18.7418V6.10352e-05H24.6128V7.80006H31.9999V14.6751H29.0967V29.7876H20.7418V33.6876H16.8708V40.0001H14.8708V33.6876H10.9999V29.7876H2.9031V14.6751H-0.00012207V7.80006H7.12891V6.10352e-05ZM9.12891 7.80006H10.9999V2.00006H9.12891V7.80006ZM4.9031 14.6751V27.7876H27.0967V14.6751H4.9031ZM12.9999 29.7876V31.6876H18.7418V29.7876H12.9999ZM22.6128 7.80006V2.00006H20.7418V7.80006H22.6128ZM1.99988 9.80006V12.6751H29.9999V9.80006H1.99988Z"
|
||||
fill="#06847B"
|
||||
/>
|
||||
</g>
|
||||
</>
|
||||
);
|
||||
Plugins.displayName = "Plugins";
|
||||
export default Plugins;
|
|
@ -39,6 +39,7 @@ import { navigationSection } from "./navigation/urls";
|
|||
import { NotFound } from "./NotFound";
|
||||
import OrdersSection from "./orders";
|
||||
import PageSection from "./pages";
|
||||
import PluginsSection from "./plugins";
|
||||
import ProductSection from "./products";
|
||||
import ProductTypesSection from "./productTypes";
|
||||
import ShippingSection from "./shipping";
|
||||
|
@ -171,6 +172,11 @@ const Routes: React.FC = () => {
|
|||
path="/pages"
|
||||
component={PageSection}
|
||||
/>
|
||||
<SectionRoute
|
||||
permissions={[PermissionEnum.MANAGE_PLUGINS]}
|
||||
path="/plugins"
|
||||
component={PluginsSection}
|
||||
/>
|
||||
<SectionRoute
|
||||
permissions={[PermissionEnum.MANAGE_ORDERS]}
|
||||
path="/orders"
|
||||
|
|
|
@ -152,6 +152,10 @@ export const sectionNames = defineMessages({
|
|||
defaultMessage: "Pages",
|
||||
description: "pages section name"
|
||||
},
|
||||
plugins: {
|
||||
defaultMessage: "Plugins",
|
||||
description: "plugins section name"
|
||||
},
|
||||
productTypes: {
|
||||
defaultMessage: "Product Types",
|
||||
description: "product types section name"
|
||||
|
|
88
src/plugins/components/PluginInfo/PluginInfo.tsx
Normal file
88
src/plugins/components/PluginInfo/PluginInfo.tsx
Normal file
|
@ -0,0 +1,88 @@
|
|||
import Card from "@material-ui/core/Card";
|
||||
import CardContent from "@material-ui/core/CardContent";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import makeStyles from "@material-ui/styles/makeStyles";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import CardTitle from "@saleor/components/CardTitle";
|
||||
import ControlledSwitch from "@saleor/components/ControlledSwitch";
|
||||
import FormSpacer from "@saleor/components/FormSpacer";
|
||||
import Hr from "@saleor/components/Hr";
|
||||
import { FormData } from "../PluginsDetailsPage";
|
||||
|
||||
interface PluginInfoProps {
|
||||
data: FormData;
|
||||
description: string;
|
||||
name: string;
|
||||
onChange: (event: React.ChangeEvent<any>) => void;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
status: {
|
||||
paddingTop: 20
|
||||
},
|
||||
title: {
|
||||
fontSize: 14,
|
||||
paddingTop: 10
|
||||
}
|
||||
}));
|
||||
|
||||
const PluginInfo: React.StatelessComponent<PluginInfoProps> = ({
|
||||
data,
|
||||
description,
|
||||
name,
|
||||
onChange
|
||||
}) => {
|
||||
const classes = useStyles({});
|
||||
const intl = useIntl();
|
||||
return (
|
||||
<Card>
|
||||
<CardTitle
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Plugin Information and Status",
|
||||
description: "plugin title"
|
||||
})}
|
||||
/>
|
||||
<CardContent>
|
||||
<Typography className={classes.title} variant="h6">
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Plugin Name",
|
||||
description: "plugin name"
|
||||
})}
|
||||
</Typography>
|
||||
<Typography>{name}</Typography>
|
||||
{description && (
|
||||
<>
|
||||
<Typography className={classes.title} variant="h6">
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Plugin Description",
|
||||
description: "plugin description"
|
||||
})}
|
||||
</Typography>
|
||||
<Typography>{description}</Typography>
|
||||
</>
|
||||
)}
|
||||
<FormSpacer />
|
||||
<Hr />
|
||||
<Typography className={classes.status}>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Status",
|
||||
description: "plugin status"
|
||||
})}
|
||||
</Typography>
|
||||
<ControlledSwitch
|
||||
checked={data.active}
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "Set plugin as Active",
|
||||
description: "plugin active label"
|
||||
})}
|
||||
name={"active" as keyof FormData}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
PluginInfo.displayName = "PluginInfo";
|
||||
export default PluginInfo;
|
2
src/plugins/components/PluginInfo/index.ts
Normal file
2
src/plugins/components/PluginInfo/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./PluginInfo";
|
||||
export * from "./PluginInfo";
|
87
src/plugins/components/PluginSettings/PluginSettings.tsx
Normal file
87
src/plugins/components/PluginSettings/PluginSettings.tsx
Normal file
|
@ -0,0 +1,87 @@
|
|||
import Card from "@material-ui/core/Card";
|
||||
import CardContent from "@material-ui/core/CardContent";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import makeStyles from "@material-ui/styles/makeStyles";
|
||||
import CardTitle from "@saleor/components/CardTitle";
|
||||
import ControlledSwitch from "@saleor/components/ControlledSwitch";
|
||||
import { FormErrors } from "@saleor/types";
|
||||
import { ConfigurationTypeFieldEnum } from "@saleor/types/globalTypes";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import { FormData } from "../PluginsDetailsPage";
|
||||
|
||||
interface PluginSettingsProps {
|
||||
data: FormData;
|
||||
errors: FormErrors<"name" | "configuration">;
|
||||
disabled: boolean;
|
||||
onChange: (event: React.ChangeEvent<any>) => void;
|
||||
fields: Array<{
|
||||
name: string;
|
||||
type: ConfigurationTypeFieldEnum | null;
|
||||
value: string;
|
||||
helpText: string | null;
|
||||
label: string | null;
|
||||
}>;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
item: {
|
||||
paddingBottom: 10,
|
||||
paddingTop: 10
|
||||
}
|
||||
}));
|
||||
|
||||
const PluginSettings: React.StatelessComponent<PluginSettingsProps> = ({
|
||||
data,
|
||||
disabled,
|
||||
errors,
|
||||
onChange,
|
||||
fields
|
||||
}) => {
|
||||
const classes = useStyles({});
|
||||
const intl = useIntl();
|
||||
return (
|
||||
<Card>
|
||||
<CardTitle
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Plugin Settings",
|
||||
description: "plugin section title"
|
||||
})}
|
||||
/>
|
||||
<CardContent>
|
||||
{data.configuration.map((configuration, index) => (
|
||||
<div className={classes.item} key={index}>
|
||||
{fields[index].type === ConfigurationTypeFieldEnum.STRING && (
|
||||
<TextField
|
||||
disabled={disabled}
|
||||
error={!!errors.name}
|
||||
helperText={fields[index].helpText}
|
||||
label={fields[index].label}
|
||||
name={configuration.name}
|
||||
fullWidth
|
||||
value={configuration.value}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)}
|
||||
{fields[index].type === ConfigurationTypeFieldEnum.BOOLEAN && (
|
||||
<ControlledSwitch
|
||||
checked={
|
||||
typeof configuration.value !== "boolean"
|
||||
? configuration.value === "true"
|
||||
: configuration.value
|
||||
}
|
||||
disabled={disabled}
|
||||
label={fields[index].label}
|
||||
name={configuration.name}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
PluginSettings.displayName = "PluginSettings";
|
||||
export default PluginSettings;
|
2
src/plugins/components/PluginSettings/index.ts
Normal file
2
src/plugins/components/PluginSettings/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./PluginSettings";
|
||||
export * from "./PluginSettings";
|
143
src/plugins/components/PluginsDetailsPage/PluginsDetailsPage.tsx
Normal file
143
src/plugins/components/PluginsDetailsPage/PluginsDetailsPage.tsx
Normal file
|
@ -0,0 +1,143 @@
|
|||
import Typography from "@material-ui/core/Typography";
|
||||
import AppHeader from "@saleor/components/AppHeader";
|
||||
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
|
||||
import Container from "@saleor/components/Container";
|
||||
import Form from "@saleor/components/Form";
|
||||
import Grid from "@saleor/components/Grid";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
import SaveButtonBar from "@saleor/components/SaveButtonBar";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { maybe } from "@saleor/misc";
|
||||
import { UserError } from "@saleor/types";
|
||||
import { ConfigurationItemInput } from "@saleor/types/globalTypes";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import { Plugin_plugin } from "../../types/Plugin";
|
||||
import PluginInfo from "../PluginInfo";
|
||||
import PluginSettings from "../PluginSettings";
|
||||
|
||||
export interface FormData {
|
||||
active: boolean;
|
||||
configuration: ConfigurationItemInput[];
|
||||
}
|
||||
|
||||
export interface PluginsDetailsPageProps {
|
||||
disabled: boolean;
|
||||
errors: UserError[];
|
||||
plugin: Plugin_plugin;
|
||||
saveButtonBarState: ConfirmButtonTransitionState;
|
||||
onBack: () => void;
|
||||
onSubmit: (data: FormData) => void;
|
||||
}
|
||||
|
||||
const PluginsDetailsPage: React.StatelessComponent<PluginsDetailsPageProps> = ({
|
||||
disabled,
|
||||
errors,
|
||||
plugin,
|
||||
saveButtonBarState,
|
||||
onBack,
|
||||
onSubmit
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const initialForm: FormData = {
|
||||
active: maybe(() => plugin.active, false),
|
||||
configuration: maybe(() => plugin.configuration, [])
|
||||
};
|
||||
|
||||
return (
|
||||
<Form errors={errors} initial={initialForm} onSubmit={onSubmit}>
|
||||
{({ data, errors, hasChanged, submit, set, triggerChange }) => {
|
||||
const onChange = event => {
|
||||
const newData = {
|
||||
active: data.active,
|
||||
configuration: data.configuration
|
||||
};
|
||||
const { name, value } = event.target;
|
||||
name === "active"
|
||||
? (newData.active = value)
|
||||
: (newData.active = data.active);
|
||||
if (newData.configuration) {
|
||||
newData.configuration.map(item => {
|
||||
if (item.name === name) {
|
||||
item.value = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
triggerChange();
|
||||
set(newData);
|
||||
};
|
||||
return (
|
||||
<Container>
|
||||
<AppHeader onBack={onBack}>
|
||||
{intl.formatMessage(sectionNames.plugins)}
|
||||
</AppHeader>
|
||||
<PageHeader
|
||||
title={`${maybe(() => plugin.name, "")} ${intl.formatMessage({
|
||||
defaultMessage: "Details",
|
||||
description: "plugin page title"
|
||||
})}`}
|
||||
/>
|
||||
<Grid variant="inverted">
|
||||
<div>
|
||||
<Typography variant="h6">
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Plugin Information and Status",
|
||||
description: "plugin section title"
|
||||
})}
|
||||
</Typography>
|
||||
<Typography>
|
||||
{intl.formatMessage({
|
||||
defaultMessage:
|
||||
"These are general information about your store. They define what is the URL of your store and what is shown in brow sers taskbar.",
|
||||
description: "plugin section description"
|
||||
})}
|
||||
</Typography>
|
||||
</div>
|
||||
<PluginInfo
|
||||
data={data}
|
||||
description={maybe(() => plugin.description, "")}
|
||||
name={maybe(() => plugin.name, "")}
|
||||
onChange={onChange}
|
||||
/>
|
||||
{data.configuration && (
|
||||
<>
|
||||
<div>
|
||||
<Typography variant="h6">
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Plugin Settings",
|
||||
description: "plugin section title"
|
||||
})}
|
||||
</Typography>
|
||||
<Typography>
|
||||
{intl.formatMessage({
|
||||
defaultMessage:
|
||||
"This adress will be used to generate invoices and calculate shipping rates. Email adress you provide here will be used as a contact adress for your customers.",
|
||||
description: "plugin section description"
|
||||
})}
|
||||
</Typography>
|
||||
</div>
|
||||
<PluginSettings
|
||||
data={data}
|
||||
fields={maybe(() => plugin.configuration, [])}
|
||||
errors={errors}
|
||||
disabled={disabled}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Grid>
|
||||
<SaveButtonBar
|
||||
disabled={disabled || !hasChanged}
|
||||
state={saveButtonBarState}
|
||||
onCancel={onBack}
|
||||
onSave={submit}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
}}
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
PluginsDetailsPage.displayName = "PluginsDetailsPage";
|
||||
export default PluginsDetailsPage;
|
2
src/plugins/components/PluginsDetailsPage/index.ts
Normal file
2
src/plugins/components/PluginsDetailsPage/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./PluginsDetailsPage";
|
||||
export * from "./PluginsDetailsPage";
|
160
src/plugins/components/PluginsList/PluginsList.tsx
Normal file
160
src/plugins/components/PluginsList/PluginsList.tsx
Normal file
|
@ -0,0 +1,160 @@
|
|||
import Card from "@material-ui/core/Card";
|
||||
import {
|
||||
createStyles,
|
||||
Theme,
|
||||
withStyles,
|
||||
WithStyles
|
||||
} from "@material-ui/core/styles";
|
||||
import Table from "@material-ui/core/Table";
|
||||
import TableBody from "@material-ui/core/TableBody";
|
||||
import TableCell from "@material-ui/core/TableCell";
|
||||
import TableFooter from "@material-ui/core/TableFooter";
|
||||
import TableRow from "@material-ui/core/TableRow";
|
||||
import EditIcon from "@material-ui/icons/Edit";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import Skeleton from "@saleor/components/Skeleton";
|
||||
import StatusLabel from "@saleor/components/StatusLabel";
|
||||
import TableHead from "@saleor/components/TableHead";
|
||||
import TablePagination from "@saleor/components/TablePagination";
|
||||
import { translateBoolean } from "@saleor/intl";
|
||||
import { maybe, renderCollection } from "@saleor/misc";
|
||||
import { ListProps } from "@saleor/types";
|
||||
import { Plugins_plugins_edges_node } from "../../types/Plugins";
|
||||
|
||||
export interface PluginListProps extends ListProps {
|
||||
plugins: Plugins_plugins_edges_node[];
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
[theme.breakpoints.up("lg")]: {
|
||||
colAction: {
|
||||
"& svg": {
|
||||
color: theme.palette.primary.main
|
||||
},
|
||||
textAlign: "right"
|
||||
},
|
||||
colActive: {},
|
||||
colName: {}
|
||||
},
|
||||
colAction: {},
|
||||
colActive: {},
|
||||
colName: {},
|
||||
link: {
|
||||
cursor: "pointer"
|
||||
}
|
||||
});
|
||||
|
||||
const numberOfColumns = 4;
|
||||
|
||||
const PluginList = withStyles(styles, { name: "PluginList" })(
|
||||
({
|
||||
classes,
|
||||
settings,
|
||||
plugins,
|
||||
disabled,
|
||||
onNextPage,
|
||||
pageInfo,
|
||||
onRowClick,
|
||||
onUpdateListSettings,
|
||||
onPreviousPage
|
||||
}: PluginListProps & WithStyles<typeof styles>) => {
|
||||
const intl = useIntl();
|
||||
return (
|
||||
<Card>
|
||||
<Table>
|
||||
<TableHead
|
||||
colSpan={numberOfColumns}
|
||||
disabled={disabled}
|
||||
items={plugins}
|
||||
>
|
||||
<TableCell className={classes.colName} padding="dense">
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Name",
|
||||
description: "plugin list table header"
|
||||
})}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colActive} padding="dense">
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Active",
|
||||
description: "plugin list table header"
|
||||
})}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colAction} padding="dense">
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Action",
|
||||
description: "plugin list table header"
|
||||
})}
|
||||
</TableCell>
|
||||
</TableHead>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TablePagination
|
||||
colSpan={numberOfColumns}
|
||||
settings={settings}
|
||||
hasNextPage={
|
||||
pageInfo && !disabled ? pageInfo.hasNextPage : false
|
||||
}
|
||||
onNextPage={onNextPage}
|
||||
onUpdateListSettings={onUpdateListSettings}
|
||||
hasPreviousPage={
|
||||
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
||||
}
|
||||
onPreviousPage={onPreviousPage}
|
||||
/>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
plugins,
|
||||
plugin => {
|
||||
return (
|
||||
<TableRow
|
||||
hover={!!plugin}
|
||||
className={!!plugin ? classes.link : undefined}
|
||||
onClick={plugin ? onRowClick(plugin.id) : undefined}
|
||||
key={plugin ? plugin.id : "skeleton"}
|
||||
>
|
||||
<TableCell className={classes.colName}>
|
||||
{maybe<React.ReactNode>(() => plugin.name, <Skeleton />)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colActive}>
|
||||
{maybe<React.ReactNode>(
|
||||
() => (
|
||||
<StatusLabel
|
||||
label={translateBoolean(plugin.active, intl)}
|
||||
status={plugin.active ? "success" : "error"}
|
||||
/>
|
||||
),
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colAction}>
|
||||
<div onClick={plugin ? onRowClick(plugin.id) : undefined}>
|
||||
<EditIcon />
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
},
|
||||
() => (
|
||||
<TableRow>
|
||||
<TableCell colSpan={numberOfColumns}>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "No plugins found",
|
||||
description: "plugin no found"
|
||||
})}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
);
|
||||
PluginList.displayName = "PluginList";
|
||||
export default PluginList;
|
2
src/plugins/components/PluginsList/index.ts
Normal file
2
src/plugins/components/PluginsList/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./PluginsList";
|
||||
export * from "./PluginsList";
|
52
src/plugins/components/PluginsListPage/PluginsListPage.tsx
Normal file
52
src/plugins/components/PluginsListPage/PluginsListPage.tsx
Normal file
|
@ -0,0 +1,52 @@
|
|||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import AppHeader from "@saleor/components/AppHeader";
|
||||
import Container from "@saleor/components/Container";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { PageListProps } from "@saleor/types";
|
||||
import { Plugins_plugins_edges_node } from "../../types/Plugins";
|
||||
import PluginsList from "../PluginsList/PluginsList";
|
||||
|
||||
export interface PluginsListPageProps extends PageListProps {
|
||||
plugins: Plugins_plugins_edges_node[];
|
||||
onBack: () => void;
|
||||
}
|
||||
|
||||
const PluginsListPage: React.StatelessComponent<PluginsListPageProps> = ({
|
||||
disabled,
|
||||
settings,
|
||||
onBack,
|
||||
onNextPage,
|
||||
onPreviousPage,
|
||||
onRowClick,
|
||||
onUpdateListSettings,
|
||||
pageInfo,
|
||||
plugins
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
return (
|
||||
<Container>
|
||||
<AppHeader onBack={onBack}>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Configuration",
|
||||
description: "plugin back button"
|
||||
})}
|
||||
</AppHeader>
|
||||
<PageHeader title={intl.formatMessage(sectionNames.plugins)} />
|
||||
<PluginsList
|
||||
disabled={disabled}
|
||||
settings={settings}
|
||||
plugins={plugins}
|
||||
onNextPage={onNextPage}
|
||||
onPreviousPage={onPreviousPage}
|
||||
onUpdateListSettings={onUpdateListSettings}
|
||||
onRowClick={onRowClick}
|
||||
pageInfo={pageInfo}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
PluginsListPage.displayName = "PluginsListPage";
|
||||
export default PluginsListPage;
|
2
src/plugins/components/PluginsListPage/index.ts
Normal file
2
src/plugins/components/PluginsListPage/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./PluginsListPage";
|
||||
export * from "./PluginsListPage";
|
57
src/plugins/fixtures.ts
Normal file
57
src/plugins/fixtures.ts
Normal file
|
@ -0,0 +1,57 @@
|
|||
import { ConfigurationTypeFieldEnum } from "@saleor/types/globalTypes";
|
||||
import { Plugin_plugin } from "./types/Plugin";
|
||||
import { Plugins_plugins_edges_node } from "./types/Plugins";
|
||||
|
||||
export const pluginList: Plugins_plugins_edges_node[] = [
|
||||
{
|
||||
__typename: "Plugin",
|
||||
active: true,
|
||||
description:
|
||||
"Lorem ipsum dolor sit amet enim. Etiam ullamcorper. Suspendisse a pellentesque dui, non felis. Maecenas malesuada elit lectus felis, malesuada ultricies. Curabitur et ligula. Ut molestie a, ultricies porta urna. Vestibulum commodo volutpat a, convallis ac, laoreet enim. Phasellus fermentum in, dolor. Pellentesque facilisis. Nulla imperdiet sit amet magna.",
|
||||
id: "Jzx123sEt==",
|
||||
name: "Avalara"
|
||||
},
|
||||
{
|
||||
__typename: "Plugin",
|
||||
active: false,
|
||||
description:
|
||||
"Lorem ipsum dolor sit amet enim. Etiam ullamcorper. Suspendisse a pellentesque dui, non felis. Maecenas malesuada elit lectus felis, malesuada ultricies. Curabitur et ligula. Ut molestie a, ultricies porta urna. Vestibulum commodo volutpat a, convallis ac, laoreet enim. Phasellus fermentum in, dolor. Pellentesque facilisis. Nulla imperdiet sit amet magna.",
|
||||
id: "Jzx123sEt==",
|
||||
name: "VatLayer"
|
||||
}
|
||||
];
|
||||
export const plugin: Plugin_plugin = {
|
||||
__typename: "Plugin",
|
||||
active: true,
|
||||
configuration: [
|
||||
{
|
||||
__typename: "ConfigurationItem",
|
||||
helpText: "Provide user or account details",
|
||||
label: "Username or account",
|
||||
name: "Username or account",
|
||||
type: ConfigurationTypeFieldEnum.STRING,
|
||||
value: ""
|
||||
},
|
||||
{
|
||||
__typename: "ConfigurationItem",
|
||||
helpText: "Provide password or license details",
|
||||
label: "Password or license",
|
||||
name: "Password or license",
|
||||
type: ConfigurationTypeFieldEnum.STRING,
|
||||
value: ""
|
||||
},
|
||||
{
|
||||
__typename: "ConfigurationItem",
|
||||
helpText: "Determines if Saleor should use Avatax sandbox API.",
|
||||
label: "Use sandbox",
|
||||
name: "Use sandbox",
|
||||
type: ConfigurationTypeFieldEnum.BOOLEAN,
|
||||
value: "true"
|
||||
}
|
||||
],
|
||||
description:
|
||||
"Lorem ipsum dolor sit amet enim. Etiam ullamcorper. Suspendisse a pellentesque dui, non felis. Maecenas malesuada elit lectus felis, malesuada ultricies. Curabitur et ligula. Ut molestie a, ultricies porta urna. Vestibulum commodo volutpat a, convallis ac, laoreet enim. Phasellus fermentum in, dolor. Pellentesque facilisis. Nulla imperdiet sit amet magna.",
|
||||
|
||||
id: "UGx1Z2luQ29uZmlndXJhdGlvbjoy",
|
||||
name: "Username or account"
|
||||
};
|
51
src/plugins/index.tsx
Normal file
51
src/plugins/index.tsx
Normal file
|
@ -0,0 +1,51 @@
|
|||
import { parse as parseQs } from "qs";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
||||
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { WindowTitle } from "../components/WindowTitle";
|
||||
import {
|
||||
pluginsListPath,
|
||||
PluginsListUrlQueryParams,
|
||||
pluginsPath
|
||||
} from "./urls";
|
||||
import PluginsDetailsComponent from "./views/PluginsDetails";
|
||||
import PluginsListComponent from "./views/PluginsList";
|
||||
|
||||
const PluginList: React.StatelessComponent<RouteComponentProps<any>> = ({
|
||||
location
|
||||
}) => {
|
||||
const qs = parseQs(location.search.substr(1));
|
||||
const params: PluginsListUrlQueryParams = qs;
|
||||
return <PluginsListComponent params={params} />;
|
||||
};
|
||||
|
||||
const PageDetails: React.StatelessComponent<RouteComponentProps<any>> = ({
|
||||
match
|
||||
}) => {
|
||||
const qs = parseQs(location.search.substr(1));
|
||||
const params: PluginsListUrlQueryParams = qs;
|
||||
|
||||
return (
|
||||
<PluginsDetailsComponent
|
||||
id={decodeURIComponent(match.params.id)}
|
||||
params={params}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const Component = () => {
|
||||
const intl = useIntl();
|
||||
return (
|
||||
<>
|
||||
<WindowTitle title={intl.formatMessage(sectionNames.plugins)} />
|
||||
<Switch>
|
||||
<Route exact path={pluginsListPath} component={PluginList} />
|
||||
<Route path={pluginsPath(":id")} component={PageDetails} />
|
||||
</Switch>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Component;
|
24
src/plugins/mutations.ts
Normal file
24
src/plugins/mutations.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import gql from "graphql-tag";
|
||||
|
||||
import { TypedMutation } from "../mutations";
|
||||
import { pluginsDetailsFragment } from "./queries";
|
||||
import { PluginUpdate, PluginUpdateVariables } from "./types/PluginUpdate";
|
||||
|
||||
const pluginUpdate = gql`
|
||||
${pluginsDetailsFragment}
|
||||
mutation PluginUpdate($id: ID!, $input: PluginUpdateInput!) {
|
||||
pluginUpdate(id: $id, input: $input) {
|
||||
errors {
|
||||
field
|
||||
message
|
||||
}
|
||||
plugin {
|
||||
...PluginsDetailsFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const TypedPluginUpdate = TypedMutation<
|
||||
PluginUpdate,
|
||||
PluginUpdateVariables
|
||||
>(pluginUpdate);
|
62
src/plugins/queries.ts
Normal file
62
src/plugins/queries.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
import gql from "graphql-tag";
|
||||
|
||||
import { TypedQuery } from "../queries";
|
||||
import { Plugin, PluginVariables } from "./types/Plugin";
|
||||
import { Plugins, PluginsVariables } from "./types/Plugins";
|
||||
|
||||
export const pluginsFragment = gql`
|
||||
fragment PluginFragment on Plugin {
|
||||
id
|
||||
name
|
||||
description
|
||||
active
|
||||
}
|
||||
`;
|
||||
|
||||
export const pluginsDetailsFragment = gql`
|
||||
${pluginsFragment}
|
||||
fragment PluginsDetailsFragment on Plugin {
|
||||
...PluginFragment
|
||||
configuration {
|
||||
name
|
||||
type
|
||||
value
|
||||
helpText
|
||||
label
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const pluginsList = gql`
|
||||
${pluginsFragment}
|
||||
query Plugins($first: Int, $after: String, $last: Int, $before: String) {
|
||||
plugins(before: $before, after: $after, first: $first, last: $last) {
|
||||
edges {
|
||||
node {
|
||||
...PluginFragment
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
hasPreviousPage
|
||||
hasNextPage
|
||||
startCursor
|
||||
endCursor
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const TypedPluginsListQuery = TypedQuery<Plugins, PluginsVariables>(
|
||||
pluginsList
|
||||
);
|
||||
|
||||
const pluginsDetails = gql`
|
||||
${pluginsDetailsFragment}
|
||||
query Plugin($id: ID!) {
|
||||
plugin(id: $id) {
|
||||
...PluginsDetailsFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const TypedPluginsDetailsQuery = TypedQuery<Plugin, PluginVariables>(
|
||||
pluginsDetails
|
||||
);
|
35
src/plugins/types/Plugin.ts
Normal file
35
src/plugins/types/Plugin.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { ConfigurationTypeFieldEnum } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: Plugin
|
||||
// ====================================================
|
||||
|
||||
export interface Plugin_plugin_configuration {
|
||||
__typename: "ConfigurationItem";
|
||||
name: string;
|
||||
type: ConfigurationTypeFieldEnum | null;
|
||||
value: string;
|
||||
helpText: string | null;
|
||||
label: string | null;
|
||||
}
|
||||
|
||||
export interface Plugin_plugin {
|
||||
__typename: "Plugin";
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
active: boolean;
|
||||
configuration: (Plugin_plugin_configuration | null)[] | null;
|
||||
}
|
||||
|
||||
export interface Plugin {
|
||||
plugin: Plugin_plugin | null;
|
||||
}
|
||||
|
||||
export interface PluginVariables {
|
||||
id: string;
|
||||
}
|
48
src/plugins/types/PluginUpdate.ts
Normal file
48
src/plugins/types/PluginUpdate.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { PluginUpdateInput, ConfigurationTypeFieldEnum } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL mutation operation: PluginUpdate
|
||||
// ====================================================
|
||||
|
||||
export interface PluginUpdate_pluginUpdate_errors {
|
||||
__typename: "Error";
|
||||
field: string | null;
|
||||
message: string | null;
|
||||
}
|
||||
|
||||
export interface PluginUpdate_pluginUpdate_plugin_configuration {
|
||||
__typename: "ConfigurationItem";
|
||||
name: string;
|
||||
type: ConfigurationTypeFieldEnum | null;
|
||||
value: string;
|
||||
helpText: string | null;
|
||||
label: string | null;
|
||||
}
|
||||
|
||||
export interface PluginUpdate_pluginUpdate_plugin {
|
||||
__typename: "Plugin";
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
active: boolean;
|
||||
configuration: (PluginUpdate_pluginUpdate_plugin_configuration | null)[] | null;
|
||||
}
|
||||
|
||||
export interface PluginUpdate_pluginUpdate {
|
||||
__typename: "PluginUpdate";
|
||||
errors: PluginUpdate_pluginUpdate_errors[] | null;
|
||||
plugin: PluginUpdate_pluginUpdate_plugin | null;
|
||||
}
|
||||
|
||||
export interface PluginUpdate {
|
||||
pluginUpdate: PluginUpdate_pluginUpdate | null;
|
||||
}
|
||||
|
||||
export interface PluginUpdateVariables {
|
||||
id: string;
|
||||
input: PluginUpdateInput;
|
||||
}
|
45
src/plugins/types/Plugins.ts
Normal file
45
src/plugins/types/Plugins.ts
Normal file
|
@ -0,0 +1,45 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: Plugins
|
||||
// ====================================================
|
||||
|
||||
export interface Plugins_plugins_edges_node {
|
||||
__typename: "Plugin";
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
export interface Plugins_plugins_edges {
|
||||
__typename: "PluginCountableEdge";
|
||||
node: Plugins_plugins_edges_node;
|
||||
}
|
||||
|
||||
export interface Plugins_plugins_pageInfo {
|
||||
__typename: "PageInfo";
|
||||
hasPreviousPage: boolean;
|
||||
hasNextPage: boolean;
|
||||
startCursor: string | null;
|
||||
endCursor: string | null;
|
||||
}
|
||||
|
||||
export interface Plugins_plugins {
|
||||
__typename: "PluginCountableConnection";
|
||||
edges: Plugins_plugins_edges[];
|
||||
pageInfo: Plugins_plugins_pageInfo;
|
||||
}
|
||||
|
||||
export interface Plugins {
|
||||
plugins: Plugins_plugins | null;
|
||||
}
|
||||
|
||||
export interface PluginsVariables {
|
||||
first?: number | null;
|
||||
after?: string | null;
|
||||
last?: number | null;
|
||||
before?: string | null;
|
||||
}
|
15
src/plugins/types/pluginFragment.ts
Normal file
15
src/plugins/types/pluginFragment.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
// ====================================================
|
||||
// GraphQL fragment: pluginFragment
|
||||
// ====================================================
|
||||
|
||||
export interface pluginFragment {
|
||||
__typename: "Plugin";
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
active: boolean;
|
||||
}
|
27
src/plugins/types/pluginsDetailsFragment.ts
Normal file
27
src/plugins/types/pluginsDetailsFragment.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { ConfigurationTypeFieldEnum } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL fragment: pluginsDetailsFragment
|
||||
// ====================================================
|
||||
|
||||
export interface pluginsDetailsFragment_configuration {
|
||||
__typename: "ConfigurationItem";
|
||||
name: string;
|
||||
type: ConfigurationTypeFieldEnum | null;
|
||||
value: string;
|
||||
helpText: string | null;
|
||||
label: string | null;
|
||||
}
|
||||
|
||||
export interface pluginsDetailsFragment {
|
||||
__typename: "Plugin";
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
active: boolean;
|
||||
configuration: (pluginsDetailsFragment_configuration | null)[] | null;
|
||||
}
|
16
src/plugins/urls.ts
Normal file
16
src/plugins/urls.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { stringify as stringifyQs } from "qs";
|
||||
import urlJoin from "url-join";
|
||||
|
||||
import { Pagination, SingleAction } from "../types";
|
||||
|
||||
export const pluginsSection = "/plugins/";
|
||||
|
||||
export const pluginsListPath = pluginsSection;
|
||||
export type PluginsListUrlQueryParams = Pagination & SingleAction;
|
||||
export const pluginsListUrl = (params?: PluginsListUrlQueryParams) =>
|
||||
pluginsListPath + "?" + stringifyQs(params);
|
||||
|
||||
export const pluginsPath = (id: string) => urlJoin(pluginsSection, id);
|
||||
export type PluginsUrlQueryParams = SingleAction;
|
||||
export const pluginsUrl = (id: string, params?: PluginsUrlQueryParams) =>
|
||||
pluginsPath(encodeURIComponent(id)) + "?" + stringifyQs(params);
|
100
src/plugins/views/PluginsDetails.tsx
Normal file
100
src/plugins/views/PluginsDetails.tsx
Normal file
|
@ -0,0 +1,100 @@
|
|||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import { getMutationState, maybe } from "../../misc";
|
||||
import PluginsDetailsPage from "../components/PluginsDetailsPage";
|
||||
import { TypedPluginUpdate } from "../mutations";
|
||||
import { TypedPluginsDetailsQuery } from "../queries";
|
||||
import { pluginsListUrl, PluginsListUrlQueryParams } from "../urls";
|
||||
|
||||
export interface PluginsDetailsProps {
|
||||
id: string;
|
||||
params: PluginsListUrlQueryParams;
|
||||
}
|
||||
|
||||
export const PluginsDetails: React.StatelessComponent<PluginsDetailsProps> = ({
|
||||
id
|
||||
}) => {
|
||||
const navigate = useNavigator();
|
||||
const notify = useNotifier();
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<TypedPluginUpdate>
|
||||
{(pluginUpdate, pluginUpdateOpts) => (
|
||||
<TypedPluginsDetailsQuery variables={{ id }}>
|
||||
{PluginDetails => {
|
||||
const formTransitionState = getMutationState(
|
||||
pluginUpdateOpts.called,
|
||||
pluginUpdateOpts.loading,
|
||||
maybe(() => pluginUpdateOpts.data.pluginUpdate.errors)
|
||||
);
|
||||
|
||||
const formErrors = maybe(
|
||||
() => pluginUpdateOpts.data.pluginUpdate.errors,
|
||||
[]
|
||||
);
|
||||
|
||||
if (formErrors.length) {
|
||||
formErrors.map(error => {
|
||||
notify({
|
||||
text: error.message
|
||||
});
|
||||
});
|
||||
} else {
|
||||
if (pluginUpdateOpts.data) {
|
||||
notify({
|
||||
text: intl.formatMessage({
|
||||
defaultMessage: "Succesfully updated plugin settings",
|
||||
description: "plugin success message"
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<WindowTitle
|
||||
title={maybe(() => PluginDetails.data.plugin.name)}
|
||||
/>
|
||||
<PluginsDetailsPage
|
||||
disabled={PluginDetails.loading}
|
||||
errors={formErrors}
|
||||
saveButtonBarState={formTransitionState}
|
||||
plugin={maybe(() => PluginDetails.data.plugin)}
|
||||
onBack={() => navigate(pluginsListUrl())}
|
||||
onSubmit={formData => {
|
||||
const configurationInput =
|
||||
formData.configuration &&
|
||||
formData.configuration.map(item => {
|
||||
return {
|
||||
name: item.name,
|
||||
value: item.value.toString()
|
||||
};
|
||||
});
|
||||
pluginUpdate({
|
||||
variables: {
|
||||
id,
|
||||
input: {
|
||||
active: formData.active,
|
||||
configuration: configurationInput
|
||||
? configurationInput
|
||||
: null
|
||||
}
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</TypedPluginsDetailsQuery>
|
||||
)}
|
||||
</TypedPluginUpdate>
|
||||
);
|
||||
};
|
||||
PluginsDetails.displayName = "PluginsDetails";
|
||||
export default PluginsDetails;
|
58
src/plugins/views/PluginsList.tsx
Normal file
58
src/plugins/views/PluginsList.tsx
Normal file
|
@ -0,0 +1,58 @@
|
|||
import { configurationMenuUrl } from "@saleor/configuration";
|
||||
import useListSettings from "@saleor/hooks/useListSettings";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import usePaginator, {
|
||||
createPaginationState
|
||||
} from "@saleor/hooks/usePaginator";
|
||||
import { maybe } from "@saleor/misc";
|
||||
import { ListViews } from "@saleor/types";
|
||||
import React from "react";
|
||||
|
||||
import PluginsListPage from "../components/PluginsListPage/PluginsListPage";
|
||||
import { TypedPluginsListQuery } from "../queries";
|
||||
import { PluginsListUrlQueryParams, pluginsUrl } from "../urls";
|
||||
|
||||
interface PluginsListProps {
|
||||
params: PluginsListUrlQueryParams;
|
||||
}
|
||||
|
||||
export const PluginsList: React.StatelessComponent<PluginsListProps> = ({
|
||||
params
|
||||
}) => {
|
||||
const navigate = useNavigator();
|
||||
const paginate = usePaginator();
|
||||
const { updateListSettings, settings } = useListSettings(
|
||||
ListViews.PLUGINS_LIST
|
||||
);
|
||||
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||
|
||||
return (
|
||||
<TypedPluginsListQuery displayLoader variables={paginationState}>
|
||||
{({ data, loading }) => {
|
||||
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
|
||||
maybe(() => data.plugins.pageInfo),
|
||||
paginationState,
|
||||
params
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<PluginsListPage
|
||||
disabled={loading}
|
||||
settings={settings}
|
||||
plugins={maybe(() => data.plugins.edges.map(edge => edge.node))}
|
||||
pageInfo={pageInfo}
|
||||
onAdd={() => navigate(configurationMenuUrl)}
|
||||
onBack={() => navigate(configurationMenuUrl)}
|
||||
onNextPage={loadNextPage}
|
||||
onPreviousPage={loadPreviousPage}
|
||||
onUpdateListSettings={updateListSettings}
|
||||
onRowClick={id => () => navigate(pluginsUrl(id))}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</TypedPluginsListQuery>
|
||||
);
|
||||
};
|
||||
|
||||
export default PluginsList;
|
File diff suppressed because it is too large
Load diff
|
@ -101,6 +101,10 @@ function loadStories() {
|
|||
require("./stories/pages/PageDetailsPage");
|
||||
require("./stories/pages/PageListPage");
|
||||
|
||||
// Plugins
|
||||
require("./stories/plugins/PluginDetailsPage");
|
||||
require("./stories/plugins/PluginsListPage");
|
||||
|
||||
// Products
|
||||
require("./stories/products/ProductCreatePage");
|
||||
require("./stories/products/ProductImagePage");
|
||||
|
|
36
src/storybook/stories/plugins/PluginDetailsPage.tsx
Normal file
36
src/storybook/stories/plugins/PluginDetailsPage.tsx
Normal file
|
@ -0,0 +1,36 @@
|
|||
import { storiesOf } from "@storybook/react";
|
||||
import React from "react";
|
||||
|
||||
import PluginsDetailsPage, {
|
||||
FormData,
|
||||
PluginsDetailsPageProps
|
||||
} from "../../../plugins/components/PluginsDetailsPage";
|
||||
import { plugin } from "../../../plugins/fixtures";
|
||||
import Decorator from "../../Decorator";
|
||||
import { formError } from "../../misc";
|
||||
|
||||
const props: PluginsDetailsPageProps = {
|
||||
disabled: false,
|
||||
errors: [],
|
||||
onBack: () => undefined,
|
||||
onSubmit: () => undefined,
|
||||
plugin,
|
||||
saveButtonBarState: "default"
|
||||
};
|
||||
|
||||
storiesOf("Views / Plugins / Plugin details", module)
|
||||
.addDecorator(Decorator)
|
||||
.add("default", () => <PluginsDetailsPage {...props} />)
|
||||
.add("loading", () => (
|
||||
<PluginsDetailsPage {...props} disabled={true} plugin={undefined} />
|
||||
))
|
||||
.add("form errors", () => (
|
||||
<PluginsDetailsPage
|
||||
{...props}
|
||||
errors={([
|
||||
"active",
|
||||
"Username or account",
|
||||
"Password or license"
|
||||
] as Array<keyof FormData>).map(formError)}
|
||||
/>
|
||||
));
|
23
src/storybook/stories/plugins/PluginsListPage.tsx
Normal file
23
src/storybook/stories/plugins/PluginsListPage.tsx
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { storiesOf } from "@storybook/react";
|
||||
import React from "react";
|
||||
|
||||
import { pageListProps } from "../../../fixtures";
|
||||
import PluginsListPage, {
|
||||
PluginsListPageProps
|
||||
} from "../../../plugins/components/PluginsListPage";
|
||||
import { pluginList } from "../../../plugins/fixtures";
|
||||
import Decorator from "../../Decorator";
|
||||
|
||||
const props: PluginsListPageProps = {
|
||||
...pageListProps.default,
|
||||
onBack: () => undefined,
|
||||
plugins: pluginList
|
||||
};
|
||||
|
||||
storiesOf("Views / Plugins / Plugin list", module)
|
||||
.addDecorator(Decorator)
|
||||
.add("default", () => <PluginsListPage {...props} />)
|
||||
.add("loading", () => (
|
||||
<PluginsListPage {...props} disabled={true} plugins={undefined} />
|
||||
))
|
||||
.add("no data", () => <PluginsListPage {...props} plugins={[]} />);
|
|
@ -23,6 +23,7 @@ export enum ListViews {
|
|||
NAVIGATION_LIST = "NAVIGATION_LIST",
|
||||
ORDER_LIST = "ORDER_LIST",
|
||||
PAGES_LIST = "PAGES_LIST",
|
||||
PLUGINS_LIST = "PLUGIN_LIST",
|
||||
PRODUCT_LIST = "PRODUCT_LIST",
|
||||
SALES_LIST = "SALES_LIST",
|
||||
SHIPPING_METHODS_LIST = "SHIPPING_METHODS_LIST",
|
||||
|
|
|
@ -33,6 +33,11 @@ export enum AuthorizationKeyType {
|
|||
GOOGLE_OAUTH2 = "GOOGLE_OAUTH2",
|
||||
}
|
||||
|
||||
export enum ConfigurationTypeFieldEnum {
|
||||
BOOLEAN = "BOOLEAN",
|
||||
STRING = "STRING",
|
||||
}
|
||||
|
||||
export enum DiscountValueTypeEnum {
|
||||
FIXED = "FIXED",
|
||||
PERCENTAGE = "PERCENTAGE",
|
||||
|
@ -340,6 +345,11 @@ export interface CollectionInput {
|
|||
publicationDate?: any | null;
|
||||
}
|
||||
|
||||
export interface ConfigurationItemInput {
|
||||
name: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface CustomerInput {
|
||||
defaultBillingAddress?: AddressInput | null;
|
||||
defaultShippingAddress?: AddressInput | null;
|
||||
|
@ -466,6 +476,11 @@ export interface PageTranslationInput {
|
|||
contentJson?: any | null;
|
||||
}
|
||||
|
||||
export interface PluginUpdateInput {
|
||||
active?: boolean | null;
|
||||
configuration?: (ConfigurationItemInput | null)[] | null;
|
||||
}
|
||||
|
||||
export interface PriceRangeInput {
|
||||
gte?: number | null;
|
||||
lte?: number | null;
|
||||
|
@ -480,6 +495,7 @@ export interface ProductFilterInput {
|
|||
stockAvailability?: StockAvailability | null;
|
||||
productType?: string | null;
|
||||
search?: string | null;
|
||||
minimalPrice?: PriceRangeInput | null;
|
||||
}
|
||||
|
||||
export interface ProductTypeInput {
|
||||
|
|
Loading…
Reference in a new issue