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

feat: add shopLogoUrls to shop settings to allow logo to be rendered from url #5227

Merged
merged 10 commits into from
Jun 28, 2019
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ const getShop = gql`
}
}
name
shopLogoUrls {
primaryShopLogoUrl
}
}
}
`;
Expand Down Expand Up @@ -49,7 +52,8 @@ function ShopLogoWithData({ className, classes, shopId, shouldShowShopName, link
if (loading) return null;
if (data && data.shop) {
const { shop } = data;
const customLogo = shop.brandAssets && shop.brandAssets.navbarBrandImage && shop.brandAssets.navbarBrandImage.large;
const customLogoFromUpload = shop.brandAssets && shop.brandAssets.navbarBrandImage && shop.brandAssets.navbarBrandImage.large;
const customLogoFromUrlInput = shop.shopLogoUrls && shop.shopLogoUrls.primaryShopLogoUrl;
const defaultLogo = "/resources/reaction-logo-circular.svg";

return (
Expand All @@ -60,7 +64,7 @@ function ShopLogoWithData({ className, classes, shopId, shouldShowShopName, link
<img
alt={shop.name}
className={classes.logo}
src={customLogo || defaultLogo}
src={customLogoFromUrlInput || customLogoFromUpload || defaultLogo}
width={size}
/>
{shouldShowShopName &&
Expand Down
16 changes: 16 additions & 0 deletions imports/collections/schemas/shops.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,22 @@ export const ParcelSize = new SimpleSchema({

registerSchema("ParcelSize", ParcelSize);

/**
* @name ShopLogoUrls
* @memberof Schemas
* @type {SimpleSchema}
* @property {String} primaryShopLogoUrl optional
* @property {String} styles optional
*/
export const ShopLogoUrls = new SimpleSchema({
primaryShopLogoUrl: {
type: String,
optional: true
}
});

registerSchema("ShopLogoUrls", ShopLogoUrls);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see this being used anywhere. Should the field just be added to shop schema?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.


/**
* @name ShopTheme
* @memberof Schemas
Expand Down
2 changes: 2 additions & 0 deletions imports/plugins/core/core/server/no-meteor/mutations/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import updateShopLogoUrls from "./updateShopLogoUrls";
import updateShopUrls from "./updateShopUrls";

export default {
updateShopLogoUrls,
updateShopUrls
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import ReactionError from "@reactioncommerce/reaction-error";

/**
* @name shop/updateShopLogoUrls
* @memberof Mutations/Shop
* @method
* @summary Add shop logo Urls to a shop
* @param {Object} context - GraphQL execution context
* @param {Object} input - an object of all mutation arguments that were sent
* @param {String} input.shopId - The shop ID
* @param {Object} input.shopLogoUrls - An object containing the Urls to update
* @return {Promise<Object>} with updated shop
*/
export default async function updateShopLogoUrls(context, input) {
const { collections, userHasPermission } = context;
const { Shops } = collections;

const {
shopId,
shopLogoUrls
} = input;


// Only update provided fields inside `shopLogoUrls`,
// don't update the whole object
const sets = {};
Object.keys(shopLogoUrls).forEach((key) => {
sets[`shopLogoUrls.${key}`] = shopLogoUrls[key];
});

// Check permission to make sure user is allowed to do this
// Security check for admin access
if (!userHasPermission(["owner", "admin"], shopId)) {
throw new ReactionError("access-denied", "User does not have permission");
}

const { value: updatedShop } = await Shops.findOneAndUpdate(
{ _id: shopId },
{
$set: {
...sets,
updatedAt: new Date()
}
},
{
returnOriginal: false
}
);

return updatedShop;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import updateShopLogoUrls from "./updateShopLogoUrls";
import updateShopUrls from "./updateShopUrls";

export default {
updateShopLogoUrls,
updateShopUrls
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { decodeShopOpaqueId } from "@reactioncommerce/reaction-graphql-xforms/shop";

/**
* @name Mutation/updateShopLogoUrls
* @method
* @memberof Shop/GraphQL
* @summary resolver for the updateShopLogoUrls GraphQL mutation
* @param {Object} _ - unused
* @param {Object} args.input - an object of all mutation arguments that were sent by the client
* @param {String} args.input.primaryShopLogoUrl - Primariy shop logo URL
* @param {Object} context - an object containing the per-request state
* @return {Promise<Object>} ShopsPayload
*/
export default async function updateShopLogoUrls(_, { input }, context) {
const {
shopId: opaqueShopId,
shopLogoUrls,
clientMutationId = null
} = input;
const shopId = decodeShopOpaqueId(opaqueShopId);

const updatedShop = await context.mutations.updateShopLogoUrls(context, {
shopId,
shopLogoUrls
});

return {
shop: updatedShop,
clientMutationId
};
}


39 changes: 39 additions & 0 deletions imports/plugins/core/core/server/no-meteor/schemas/shop.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ type Shop implements Node {
"Returns a list of roles for this shop, as a Relay-compatible connection."
roles(after: ConnectionCursor, before: ConnectionCursor, first: ConnectionLimitInt, last: ConnectionLimitInt, sortOrder: SortOrder = asc, sortBy: RoleSortByField = name): RoleConnection

"Returns URLs for shop logos"
shopLogoUrls: ShopLogoUrls

"Returns URLs for various storefront routes"
storefrontUrls: StorefrontUrls

Expand Down Expand Up @@ -79,6 +82,12 @@ input InviteShopMemberInput {
shopId: ID!
}

"Shop Logo URLs to provide for the updateShopLogoUrls mutation"
input ShopLogoUrlsInput {
"Primary Shop logo"
primaryShopLogoUrl: String
}

"Shop URLs to provide for the updateShopUrls mutation"
input StorefrontUrlsInput {
"Storefront Account Profile URL (can include `:accountId` in string)"
Expand All @@ -97,6 +106,18 @@ input StorefrontUrlsInput {
storefrontOrdersUrl: String
}

"Input parameters for the updateShopLogoUrls mutation"
input UpdateShopLogoUrlsInput {
"An optional string identifying the mutation call, which will be returned in the response payload"
clientMutationId: String

"The ID of the shop for the logos to update"
shopId: ID!

"URL object of all URL's for updateShopLogoUrls mutation"
shopLogoUrls: ShopLogoUrlsInput!
}

"Input parameters for the updateShopUrls mutation"
input UpdateShopUrlsInput {
"An optional string identifying the mutation call, which will be returned in the response payload"
Expand Down Expand Up @@ -124,6 +145,12 @@ type ShopBrandAssets {
navbarBrandImage: ImageSizes
}

"URLs for shop logos"
type ShopLogoUrls {
"Primary shop logo"
primaryShopLogoUrl: String
}

"URLs for various routes"
type StorefrontUrls {
"Storefront Account Profile URL (can include `:accountId` in string)"
Expand All @@ -142,6 +169,15 @@ type StorefrontUrls {
storefrontOrdersUrl: String
}

"The response from the `updateShopLogoUrls` mutation"
type UpdateShopLogoUrlsPayload {
"An optional string identifying the mutation call, which will be returned in the response payload"
clientMutationId: String

"The shop which was updated with URLs"
shop: Shop!
}

"The response from the `updateShopUrls` mutation"
type UpdateShopUrlsPayload {
"An optional string identifying the mutation call, which will be returned in the response payload"
Expand All @@ -158,6 +194,9 @@ extend type Mutation {
"""
inviteShopMember(input: InviteShopMemberInput!): InviteShopMemberPayload

"Given URLs for shop logos, update the Shops collection object with URLs"
updateShopLogoUrls(input: UpdateShopLogoUrlsInput!): UpdateShopLogoUrlsPayload

"Given URLs for various shop pages, update the Shops collection object with URLs"
updateShopUrls(input: UpdateShopUrlsInput!): UpdateShopUrlsPayload
}
Expand Down
140 changes: 140 additions & 0 deletions imports/plugins/core/dashboard/client/components/ShopLogoUrls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import React, { Component, Fragment } from "react";
import PropTypes from "prop-types";
import gql from "graphql-tag";
import styled from "styled-components";
import { Form } from "reacto-form";
import { Mutation } from "react-apollo";
import Button from "@material-ui/core/Button";
import Card from "@material-ui/core/Card";
import CardHeader from "@material-ui/core/CardHeader";
import CardActions from "@material-ui/core/CardActions";
import CardContent from "@material-ui/core/CardContent";
import Grid from "@material-ui/core/Grid";
import ErrorsBlock from "@reactioncommerce/components/ErrorsBlock/v1";
import Field from "@reactioncommerce/components/Field/v1";
import TextInput from "@reactioncommerce/components/TextInput/v1";
import { i18next } from "/client/api";
import withPrimaryShop from "/imports/plugins/core/graphql/lib/hocs/withPrimaryShop";

const PaddedField = styled(Field)`
margin-bottom: 30px;
`;

const RightAlignedGrid = styled(Grid)`
text-align: right;
`;

const updateShopUrlsMutation = gql`
mutation updateShopLogoUrlsMutation($input: UpdateShopLogoUrlsInput!) {
updateShopLogoUrls(input: $input) {
clientMutationId
shop {
_id
shopLogoUrls {
primaryShopLogoUrl
}
}
}
}
`;

class ShopLogoUrls extends Component {
static propTypes = {
shop: PropTypes.shape({
shopLogoUrls: PropTypes.shape({
primaryShopLogoUrl: PropTypes.string
})
})
};

handleFormChange = (value) => {
this.formValue = value;
};

handleSubmitForm = () => {
this.form.submit();
};

handleUpdateUrls(data, mutation) {
const {
shop: { _id: shopId }
} = this.props;
const { primaryShopLogoUrl } = data;

// return null;
mutation({
variables: {
input: {
shopId,
shopLogoUrls: {
primaryShopLogoUrl
}
}
}
});
}

render() {
const { shop } = this.props;
const { shopLogoUrls } = shop;
const { primaryShopLogoUrl } = shopLogoUrls || {};

console.log("shop", shop);
console.log("shopLogoUrls", shopLogoUrls);
console.log("primaryShopLogoUrl", primaryShopLogoUrl);


return (
<Card>
<CardHeader
subheader={i18next.t(
"shopSettings.shopLogoUrls.description",
"Use these fields to provide URL's for static image files to use as store logos. These URL's will override any logos uploaded."
)}
title={i18next.t("shopSettings.shopLogoUrls.title", "Shop Logo Urls")}
/>
<Mutation mutation={updateShopUrlsMutation}>
{(mutationFunc) => (
<Fragment>
<Form
ref={(formRef) => {
this.form = formRef;
}}
onChange={this.handleFormChange}
onSubmit={(data) => this.handleUpdateUrls(data, mutationFunc)}
value={shop}
>
<CardContent>
<PaddedField
name="primaryShopLogoUrl"
label={i18next.t("shopSettings.shopLogoUrls.primaryShopLogoUrlTitle", "Primary Shop Logo")}
labelFor="primaryShopLogoUrlInput"
>
<TextInput
id="primaryShopLogoUrlInput"
name="primaryShopLogoUrl"
placeholder={i18next.t("shopSettings.shopLogoUrls.primaryShopLogoUrlDescription", "This is the primary shop logo, which is used wherever you see a logo throughout the UI")}
value={primaryShopLogoUrl || ""}
/>
<ErrorsBlock names={["primaryShopLogoUrl"]} />
</PaddedField>
</CardContent>
<CardActions>
<Grid container alignItems="center" justify="flex-end">
<RightAlignedGrid item xs={12}>
<Button color="primary" variant="contained" onClick={this.handleSubmitForm}>
{i18next.t("app.save")}
</Button>
</RightAlignedGrid>
</Grid>
</CardActions>
</Form>
</Fragment>
)}
</Mutation>
</Card>
);
}
}

export default withPrimaryShop(ShopLogoUrls);
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@
{{/each}}
</div>

<div style="margin-bottom: 20px">
{{> React component=ShopLogoUrls}}
</div>

<div style="margin-bottom: 20px">
{{> React component=StorefrontUrls}}
</div>
Expand Down
Loading