Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement site settings #3071

Merged
merged 26 commits into from
Oct 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4c744dd
Add site settings page
dominik-zeglen Oct 8, 2018
5496dc0
Add site settings to configuratio menu
dominik-zeglen Oct 8, 2018
7148877
wip
dominik-zeglen Oct 9, 2018
0f832de
Add key adding dialog
dominik-zeglen Oct 9, 2018
6aedca5
Add trash icon to remove keys
dominik-zeglen Oct 9, 2018
6acf5ee
Update snapshots
dominik-zeglen Oct 9, 2018
7077d50
Add mutations
dominik-zeglen Oct 10, 2018
49394fe
Use API type
dominik-zeglen Oct 10, 2018
c2db63c
Return Shop object in authorization keys mutations
maarcingebala Oct 10, 2018
a892550
Add key add handler, update settings handler
dominik-zeglen Oct 10, 2018
49f94aa
Update cache after shop mutations
dominik-zeglen Oct 10, 2018
ad61554
Handle form errors
dominik-zeglen Oct 10, 2018
a34e9f7
Add explanation to some magic ✨
dominik-zeglen Oct 10, 2018
63a853f
Fix required field validation in shop mutations
maarcingebala Oct 10, 2018
e811558
Update schma
maarcingebala Oct 10, 2018
9b7fa5a
Add stories with form errors
dominik-zeglen Oct 11, 2018
f840daa
Evaluate translation only once per render
dominik-zeglen Oct 11, 2018
83fc537
Extract constant to theme.ts
dominik-zeglen Oct 11, 2018
86a2f49
Update snapshots
dominik-zeglen Oct 11, 2018
2cdb79e
Fix cancel button
dominik-zeglen Oct 11, 2018
a307c25
Simplify logic
dominik-zeglen Oct 11, 2018
bee308e
Simplify code
dominik-zeglen Oct 11, 2018
d142585
Update contexts
dominik-zeglen Oct 12, 2018
a0384ad
Update snapshots
dominik-zeglen Oct 12, 2018
a6e1796
Drop context
dominik-zeglen Oct 12, 2018
258362e
Improve labels
dominik-zeglen Oct 12, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion saleor/graphql/core/mutations.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def clean_instance(cls, instance, errors):
except ValidationError as validation_errors:
message_dict = validation_errors.message_dict
for field in message_dict:
if field in cls._meta.exclude:
if hasattr(cls._meta, 'exclude') and field in cls._meta.exclude:
continue
for message in message_dict[field]:
field = snake_to_camel_case(field)
Expand Down
6 changes: 4 additions & 2 deletions saleor/graphql/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -209,16 +209,18 @@ type AuthorizationKey {
type AuthorizationKeyAdd {
errors: [Error]
authorizationKey: AuthorizationKey
shop: Shop
}

type AuthorizationKeyDelete {
errors: [Error]
authorizationKey: AuthorizationKey
shop: Shop
}

input AuthorizationKeyInput {
key: String
password: String
key: String!
password: String!
}

enum AuthorizationKeyType {
Expand Down
30 changes: 16 additions & 14 deletions saleor/graphql/shop/mutations.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ class SiteDomainInput(graphene.InputObjectType):


class ShopSettingsUpdate(BaseMutation):
shop = graphene.Field(Shop, description='Updated Shop')

class Arguments:
input = ShopSettingsInput(
description='Fields required to update shop settings.',
Expand All @@ -34,9 +36,6 @@ class Arguments:
class Meta:
description = 'Updates shop settings'

shop = graphene.Field(
Shop, description='Updated Shop')

@classmethod
@permission_required('site.manage_settings')
def mutate(cls, root, info, input):
Expand All @@ -56,24 +55,24 @@ def mutate(cls, root, info, input):


class ShopDomainUpdate(BaseMutation):
shop = graphene.Field(Shop, description='Updated Shop')

class Arguments:
input = SiteDomainInput(description='Fields required to update site')

class Meta:
description = 'Updates site domain of the shop'

shop = graphene.Field(Shop, description='Updated Shop')

@classmethod
@permission_required('site.manage_settings')
def mutate(cls, root, info, input):
errors = []
site = info.context.site
domain = input.get('domain')
name = input.get('name')
if domain:
if domain is not None:
site.domain = domain
if name:
if name is not None:
site.name = name
cls.clean_instance(site, errors)
if errors:
Expand All @@ -83,16 +82,15 @@ def mutate(cls, root, info, input):


class HomepageCollectionUpdate(BaseMutation):
shop = graphene.Field(Shop, description='Updated Shop')

class Arguments:
collection = graphene.ID(
description='Collection displayed on homepage')

class Meta:
description = 'Updates homepage collection of the shop'

shop = graphene.Field(
Shop, description='Updated Shop')

@classmethod
@permission_required('site.manage_settings')
def mutate(cls, root, info, collection):
Expand All @@ -111,13 +109,16 @@ def mutate(cls, root, info, collection):


class AuthorizationKeyInput(graphene.InputObjectType):
key = graphene.String(description='Client authorization key (client ID).')
password = graphene.String(description='Client secret.')
key = graphene.String(
required=True, description='Client authorization key (client ID).')
password = graphene.String(
required=True, description='Client secret.')


class AuthorizationKeyAdd(BaseMutation):
authorization_key = graphene.Field(
AuthorizationKey, description='Newly added authorization key.')
shop = graphene.Field(Shop, description='Updated Shop')

class Meta:
description = 'Adds an authorization key.'
Expand Down Expand Up @@ -146,12 +147,13 @@ def mutate(cls, root, info, key_type, input):
return AuthorizationKeyAdd(errors=errors)

instance.save()
return AuthorizationKeyAdd(authorization_key=instance)
return AuthorizationKeyAdd(authorization_key=instance, shop=Shop())


class AuthorizationKeyDelete(BaseMutation):
authorization_key = graphene.Field(
AuthorizationKey, description='Auhtorization key that was deleted.')
shop = graphene.Field(Shop, description='Updated Shop')

class Arguments:
key_type = AuthorizationKeyType(
Expand All @@ -174,4 +176,4 @@ def mutate(cls, root, info, key_type):
return AuthorizationKeyDelete(errors=errors)

instance.delete()
return AuthorizationKeyDelete(authorization_key=instance)
return AuthorizationKeyDelete(authorization_key=instance, shop=Shop())
4 changes: 3 additions & 1 deletion saleor/static/dashboard-next/configuration/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Navigation from "../icons/Navigation";
import Pages from "../icons/Pages";
import StoreMall from "../icons/StoreMall";
import { productTypeListUrl } from "../productTypes";
import { siteSettingsUrl } from "../siteSettings";
import { staffListUrl } from "../staff";
import { PermissionEnum } from "../types/globalTypes";
import ConfigurationPage, { MenuItem } from "./ConfigurationPage";
Expand Down Expand Up @@ -59,7 +60,8 @@ export const configurationMenu: MenuItem[] = [
description: i18n.t("View and update your site settings"),
icon: <StoreMall fontSize="inherit" />,
permission: PermissionEnum.MANAGE_SETTINGS,
title: i18n.t("Site Settings")
title: i18n.t("Site Settings"),
url: siteSettingsUrl
},
{
description: i18n.t("Manage and add additional pages"),
Expand Down
22 changes: 18 additions & 4 deletions saleor/static/dashboard-next/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import CssBaseline from "@material-ui/core/CssBaseline";
import MuiThemeProvider from "@material-ui/core/styles/MuiThemeProvider";
import { InMemoryCache } from "apollo-cache-inmemory";
import { defaultDataIdFromObject, InMemoryCache } from "apollo-cache-inmemory";
import { ApolloClient, ApolloError } from "apollo-client";
import { setContext } from "apollo-link-context";
import { ErrorResponse, onError } from "apollo-link-error";
Expand Down Expand Up @@ -28,10 +28,10 @@ import OrdersSection from "./orders";
import PageSection from "./pages";
import ProductSection from "./products";
import ProductTypesSection from "./productTypes";
import SiteSettingsSection from "./siteSettings";
import StaffSection from "./staff";
import theme from "./theme";
import { PermissionEnum } from './types/globalTypes';

import { PermissionEnum } from "./types/globalTypes";

const cookies = new Cookies();

Expand Down Expand Up @@ -68,7 +68,16 @@ const uploadLink = createUploadLink({
});

const apolloClient = new ApolloClient({
cache: new InMemoryCache(),
cache: new InMemoryCache({
dataIdFromObject: (obj: any) => {
// We need to set manually shop's ID, since it is singleton and
// API does not return its ID
if (obj.__typename === "Shop") {
return "shop";
}
return defaultDataIdFromObject(obj);
}
}),
link: invalidTokenLink.concat(authLink.concat(uploadLink))
});

Expand Down Expand Up @@ -125,6 +134,11 @@ render(
path="/staff"
component={StaffSection}
/>
<SectionRoute
permissions={[PermissionEnum.MANAGE_SETTINGS]}
path="/siteSettings"
component={SiteSettingsSection}
/>
{configurationMenu.filter(menuItem =>
hasPermission(menuItem.permission, user)
).length > 0 && (
Expand Down
7 changes: 6 additions & 1 deletion saleor/static/dashboard-next/misc.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { stringify } from "qs";
import i18n from "./i18n";
import { TaxRateType } from "./types/globalTypes";
import { AuthorizationKeyType, TaxRateType } from "./types/globalTypes";

export interface PageInfo {
endCursor: string;
Expand Down Expand Up @@ -145,6 +145,11 @@ export const translatedTaxRates = () => ({
[TaxRateType.WATER]: i18n.t("Water")
});

export const translatedAuthorizationKeyTypes = () => ({
[AuthorizationKeyType.FACEBOOK]: i18n.t("Facebook"),
[AuthorizationKeyType.GOOGLE_OAUTH2]: i18n.t("Google OAuth2")
});

export function maybe<T>(exp: () => T, d?: T) {
try {
const result = exp();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import TextField from "@material-ui/core/TextField";
import * as React from "react";

import CardTitle from "../../../components/CardTitle";
import FormSpacer from "../../../components/FormSpacer";
import i18n from "../../../i18n";
import { SiteSettingsPageFormData } from "../SiteSettingsPage";

interface SiteSettingsDetailsProps {
data: SiteSettingsPageFormData;
errors: Partial<{
description: string;
domain: string;
name: string;
}>;
disabled: boolean;
onChange: (event: React.ChangeEvent<any>) => void;
}

const SiteSettingsDetails: React.StatelessComponent<
SiteSettingsDetailsProps
> = ({ data, disabled, errors, onChange }) => (
<Card>
<CardTitle
title={i18n.t("General Information", {
context: "store configuration"
})}
/>
<CardContent>
<TextField
disabled={disabled}
error={!!errors.name}
fullWidth
name="name"
label={i18n.t("Name of your store")}
helperText={
errors.name ||
i18n.t("Name of your store is shown on tab in web browser")
}
value={data.name}
onChange={onChange}
/>
<FormSpacer />
<TextField
disabled={disabled}
error={!!errors.domain}
fullWidth
name="domain"
label={i18n.t("URL of your online store")}
helperText={errors.domain}
value={data.domain}
onChange={onChange}
/>
<FormSpacer />
<TextField
disabled={disabled}
error={!!errors.domain}
fullWidth
name="description"
label={i18n.t("Store Description", {
context: "field label"
})}
helperText={
errors.description ||
i18n.t(
"Store description is shown on taskbar after your store name",
{
context: "help text"
}
)
}
value={data.description}
onChange={onChange}
/>
</CardContent>
</Card>
);
SiteSettingsDetails.displayName = "SiteSettingsDetails";
export default SiteSettingsDetails;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default } from "./SiteSettingsDetails";
export * from "./SiteSettingsDetails";