Skip to content

Commit

Permalink
Merge pull request #65 from Shopify/jl-auth-pt3
Browse files Browse the repository at this point in the history
Accounts, Part 3
  • Loading branch information
jplhomer authored Oct 11, 2022
2 parents 51282fd + d43138c commit 404bd2e
Show file tree
Hide file tree
Showing 11 changed files with 679 additions and 42 deletions.
96 changes: 96 additions & 0 deletions app/components/AccountAddressBook.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { Form } from "@remix-run/react";
import type {
Customer,
MailingAddress,
} from "@shopify/hydrogen-ui-alpha/storefront-api-types";
import { Button, Link, Text } from "~/components";

export function AccountAddressBook({
customer,
addresses,
}: {
customer: Customer;
addresses: MailingAddress[];
}) {
return (
<>
<div className="grid w-full gap-4 p-4 py-6 md:gap-8 md:p-8 lg:p-12">
<h3 className="font-bold text-lead">Address Book</h3>
<div>
{!addresses?.length && (
<Text className="mb-1" width="narrow" as="p" size="copy">
You haven&apos;t saved any addresses yet.
</Text>
)}
<div className="w-48">
<Button
to="address/add"
className="mt-2 text-sm w-full mb-6"
variant="secondary"
>
Add an Address
</Button>
</div>
{Boolean(addresses?.length) && (
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6">
{customer.defaultAddress && (
<Address address={customer.defaultAddress} defaultAddress />
)}
{addresses
.filter((address) => address.id !== customer.defaultAddress?.id)
.map((address) => (
<Address key={address.id} address={address} />
))}
</div>
)}
</div>
</div>
</>
);
}

function Address({
address,
defaultAddress,
}: {
address: MailingAddress;
defaultAddress?: boolean;
}) {
return (
<div className="lg:p-8 p-6 border border-gray-200 rounded flex flex-col">
{defaultAddress && (
<div className="mb-3 flex flex-row">
<span className="px-3 py-1 text-xs font-medium rounded-full bg-primary/20 text-primary/50">
Default
</span>
</div>
)}
<ul className="flex-1 flex-row">
{(address.firstName || address.lastName) && (
<li>
{"" +
(address.firstName && address.firstName + " ") +
address?.lastName}
</li>
)}
{address.formatted &&
address.formatted.map((line: string) => <li key={line}>{line}</li>)}
</ul>

<div className="flex flex-row font-medium mt-6">
<Link
to={`address/${encodeURIComponent(address.id)}`}
className="text-left underline text-sm"
>
Edit
</Link>
<Form action="address/delete" method="delete">
<input type="hidden" name="addressId" value={address.id} />
<button className="text-left text-primary/50 ml-6 text-sm">
Remove
</button>
</Form>
</div>
</div>
);
}
11 changes: 1 addition & 10 deletions app/components/AccountDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
import { Outlet, useOutlet } from "@remix-run/react";
import type { Customer } from "@shopify/hydrogen-ui-alpha/storefront-api-types";
import { Modal, Link } from "~/components";
import type { AccountDetailsOutletContext } from "~/routes/account/edit";
import { Link } from "~/components";

export function AccountDetails({ customer }: { customer: Customer }) {
const outlet = useOutlet();

const { firstName, lastName, email, phone } = customer;

return (
<>
{!!outlet && (
<Modal cancelLink=".">
<Outlet context={{ customer } as AccountDetailsOutletContext} />
</Modal>
)}
<div className="grid w-full gap-4 p-4 py-6 md:gap-8 md:p-8 lg:p-12">
<h3 className="font-bold text-lead">Account Details</h3>
<div className="lg:p-8 p-6 border border-gray-200 rounded">
Expand Down
1 change: 1 addition & 0 deletions app/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export { CountrySelector } from "./CountrySelector";
export { CartDetails, CartEmpty } from "./CartDetails";
export { OrderCard } from "./OrderCard";
export { AccountDetails } from "./AccountDetails";
export { AccountAddressBook } from "./AccountAddressBook";
export { Modal } from "./Modal";
export { Link } from "./Link";
// Sue me
Expand Down
209 changes: 209 additions & 0 deletions app/data/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ import type {
UserError,
Page,
ShopPolicy,
CustomerAddressUpdatePayload,
MailingAddressInput,
CustomerAddressDeletePayload,
CustomerDefaultAddressUpdatePayload,
CustomerAddressCreatePayload,
} from "@shopify/hydrogen-ui-alpha/storefront-api-types";
import {
getPublicTokenHeaders,
Expand Down Expand Up @@ -1343,6 +1348,16 @@ const CUSTOMER_QUERY = `#graphql
defaultAddress {
id
formatted
firstName
lastName
company
address1
address2
country
province
city
zip
phone
}
addresses(first: 6) {
edges {
Expand Down Expand Up @@ -1635,3 +1650,197 @@ export async function updateCustomer({
throw new Error(error);
}
}

const UPDATE_ADDRESS_MUTATION = `#graphql
mutation customerAddressUpdate(
$address: MailingAddressInput!
$customerAccessToken: String!
$id: ID!
) {
customerAddressUpdate(
address: $address
customerAccessToken: $customerAccessToken
id: $id
) {
customerUserErrors {
code
field
message
}
}
}
`;

export async function updateCustomerAddress({
customerAccessToken,
addressId,
address,
}: {
customerAccessToken: string;
addressId: string;
address: MailingAddressInput;
}): Promise<void> {
const { data, errors } = await getStorefrontData<{
customerAddressUpdate: CustomerAddressUpdatePayload;
}>({
query: UPDATE_ADDRESS_MUTATION,
variables: {
customerAccessToken,
id: addressId,
address,
},
});

const error = getApiErrorMessage(
"customerAddressUpdate",
data,
errors as UserError[]
);

if (error) {
throw new Error(error);
}
}

const DELETE_ADDRESS_MUTATION = `#graphql
mutation customerAddressDelete($customerAccessToken: String!, $id: ID!) {
customerAddressDelete(customerAccessToken: $customerAccessToken, id: $id) {
customerUserErrors {
code
field
message
}
deletedCustomerAddressId
}
}
`;

export async function deleteCustomerAddress({
customerAccessToken,
addressId,
}: {
customerAccessToken: string;
addressId: string;
}): Promise<void> {
const { data, errors } = await getStorefrontData<{
customerAddressDelete: CustomerAddressDeletePayload;
}>({
query: DELETE_ADDRESS_MUTATION,
variables: {
customerAccessToken,
id: addressId,
},
});

const error = getApiErrorMessage(
"customerAddressDelete",
data,
errors as UserError[]
);

if (error) {
throw new Error(error);
}
}

const UPDATE_DEFAULT_ADDRESS_MUTATION = `#graphql
mutation customerDefaultAddressUpdate(
$addressId: ID!
$customerAccessToken: String!
) {
customerDefaultAddressUpdate(
addressId: $addressId
customerAccessToken: $customerAccessToken
) {
customerUserErrors {
code
field
message
}
}
}
`;

export async function updateCustomerDefaultAddress({
customerAccessToken,
addressId,
}: {
customerAccessToken: string;
addressId: string;
}): Promise<void> {
const { data, errors } = await getStorefrontData<{
customerDefaultAddressUpdate: CustomerDefaultAddressUpdatePayload;
}>({
query: UPDATE_DEFAULT_ADDRESS_MUTATION,
variables: {
customerAccessToken,
addressId,
},
});

const error = getApiErrorMessage(
"customerDefaultAddressUpdate",
data,
errors as UserError[]
);

if (error) {
throw new Error(error);
}
}

const CREATE_ADDRESS_MUTATION = `#graphql
mutation customerAddressCreate(
$address: MailingAddressInput!
$customerAccessToken: String!
) {
customerAddressCreate(
address: $address
customerAccessToken: $customerAccessToken
) {
customerAddress {
id
}
customerUserErrors {
code
field
message
}
}
}
`;

export async function createCustomerAddress({
customerAccessToken,
address,
}: {
customerAccessToken: string;
address: MailingAddressInput;
}): Promise<string> {
const { data, errors } = await getStorefrontData<{
customerAddressCreate: CustomerAddressCreatePayload;
}>({
query: CREATE_ADDRESS_MUTATION,
variables: {
customerAccessToken,
address,
},
});

const error = getApiErrorMessage(
"customerAddressCreate",
data,
errors as UserError[]
);

if (error) {
throw new Error(error);
}

invariant(
data?.customerAddressCreate?.customerAddress?.id,
"Expected customer address to be created"
);

return data.customerAddressCreate.customerAddress.id;
}
10 changes: 3 additions & 7 deletions app/hooks/useCart.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import {useParentRouteData} from './useRouteData';
import { useParentRouteData } from "./useRouteData";

import type {
Cart,
} from "@shopify/hydrogen-ui-alpha/storefront-api-types";
import type { Cart } from "@shopify/hydrogen-ui-alpha/storefront-api-types";

export function useCart(): Cart | undefined {
const rootData = useParentRouteData('/');
const rootData = useParentRouteData("/");

if (rootData?.cart?._data) {
return rootData?.cart?._data;
}

throw rootData?.cart
}
8 changes: 6 additions & 2 deletions app/routes/account.login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ export default function Login() {
autoFocus
onBlur={(event) => {
setNativeEmailError(
!event.currentTarget.validity.valid
event.currentTarget.value.length &&
!event.currentTarget.validity.valid
? "Invalid email address"
: null
);
Expand All @@ -145,7 +146,10 @@ export default function Login() {
// eslint-disable-next-line jsx-a11y/no-autofocus
autoFocus
onBlur={(event) => {
if (event.currentTarget.validity.valid) {
if (
event.currentTarget.validity.valid ||
!event.currentTarget.value.length
) {
setNativePasswordError(null);
} else {
setNativePasswordError(
Expand Down
Loading

0 comments on commit 404bd2e

Please sign in to comment.