diff --git a/app/(pages)/shopify/(components)/customer/index.js b/app/(pages)/shopify/(components)/customer/index.js
new file mode 100644
index 00000000..e352951b
--- /dev/null
+++ b/app/(pages)/shopify/(components)/customer/index.js
@@ -0,0 +1,77 @@
+'use client'
+
+import { Form, SubmitButton } from 'libs/form'
+import { InputField } from 'libs/form/fields'
+
+// import {
+// LoginCustomerAction,
+// LogoutCustomerAction,
+// CreateCustomerAction,
+// } from 'libs/shopify/customer/actions'
+
+// function InputField({ type, id, placeholder, required, value, onChange }) {
+// return (
+//
+//
+//
+// )
+// }
+
+export function LoginForm() {
+ return (
+
+ )
+}
+
+export function RegisterForm() {
+ return (
+
+ )
+}
+
+export function LogoutButton() {
+ return (
+
+ )
+}
diff --git a/app/(pages)/shopify/account/account.module.scss b/app/(pages)/shopify/account/account.module.scss
new file mode 100644
index 00000000..e1a19c70
--- /dev/null
+++ b/app/(pages)/shopify/account/account.module.scss
@@ -0,0 +1,17 @@
+.page {
+ text-transform: uppercase;
+ font-family: var(--font-mono);
+ overflow: clip;
+}
+
+.inner {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-direction: column;
+ flex-grow: 1;
+
+ @include mobile {
+ padding: 0 mobile-vw(16px);
+ }
+}
diff --git a/app/(pages)/shopify/account/page.js b/app/(pages)/shopify/account/page.js
new file mode 100644
index 00000000..58baa36f
--- /dev/null
+++ b/app/(pages)/shopify/account/page.js
@@ -0,0 +1,48 @@
+import { getCustomer } from 'libs/shopify/customer/actions'
+import { Suspense } from 'react'
+import { LoginForm, LogoutButton, RegisterForm } from '../(components)/customer'
+import { Wrapper } from '../../(components)/wrapper'
+import s from './account.module.scss'
+
+export default async function AccountPage() {
+ const customer = await getCustomer()
+
+ return (
+
+
+ My Account
+ {customer ? (
+ Loading...}>
+
+
+
+ ) : (
+
+
Login
+
+ Register
+
+
+ )}
+
+
+ )
+}
+
+function CustomerInfo({ customer }) {
+ return (
+ <>
+ Welcome, {customer.firstName}!
+ Email: {customer.email}
+ Recent Orders
+
+ {customer.orders.edges.map(({ node }) => (
+ -
+ Order #{node.orderNumber} - {node.totalPrice.amount}{' '}
+ {node.totalPrice.currencyCode}
+
+ ))}
+
+ >
+ )
+}
diff --git a/libs/form/hook.js b/libs/form/hook.js
index 6a1bcc02..6300c9a7 100644
--- a/libs/form/hook.js
+++ b/libs/form/hook.js
@@ -27,7 +27,7 @@ export const useForm = ({
function onSubmit(event) {
event.preventDefault()
const formData = new FormData(event.currentTarget)
- formData.append('formId', formId)
+ formId && formData.append('formId', formId)
startTransition(async () => {
await formAction(formData)
diff --git a/libs/form/index.js b/libs/form/index.js
index 8f43537a..a9ea5ca2 100644
--- a/libs/form/index.js
+++ b/libs/form/index.js
@@ -2,6 +2,11 @@
import cn from 'clsx'
import { HubspotNewsletterAction } from 'libs/hubspot-forms/action'
+import {
+ CreateCustomerAction,
+ LoginCustomerAction,
+ LogoutCustomerAction,
+} from 'libs/shopify/customer/actions'
import { createContext, useContext, useEffect, useState } from 'react'
import s from './form.module.scss'
import { useForm } from './hook'
@@ -32,7 +37,7 @@ export const FormProvider = ({
const { formAction, onSubmit, ...helpers } = useForm({
action: formsActions[action],
formId,
- initalState: null,
+ initialState: null,
dependencies: [],
})
@@ -115,4 +120,7 @@ export const Messages = ({ className }) => {
const formsActions = {
HubspotNewsletterAction: HubspotNewsletterAction,
+ LoginCustomerAction: LoginCustomerAction,
+ LogoutCustomerAction: LogoutCustomerAction,
+ CreateCustomerAction: CreateCustomerAction,
}
diff --git a/libs/shopify/customer/actions.js b/libs/shopify/customer/actions.js
new file mode 100644
index 00000000..0b4422c3
--- /dev/null
+++ b/libs/shopify/customer/actions.js
@@ -0,0 +1,124 @@
+'use server'
+
+import { shopifyFetch } from 'libs/shopify'
+import { cookies } from 'next/headers'
+import {
+ customerAccessTokenCreateMutation,
+ customerAccessTokenDeleteMutation,
+ customerCreateMutation,
+} from '../mutations/customer'
+import { getCustomerQuery } from '../queries/customer'
+
+export async function LoginCustomerAction(prevState, formData) {
+ const email = formData.get('email')
+ const password = formData.get('password')
+
+ try {
+ const res = await shopifyFetch({
+ query: customerAccessTokenCreateMutation,
+ variables: {
+ input: {
+ email,
+ password,
+ },
+ },
+ cache: 'no-store',
+ })
+
+ const { customerAccessToken, customerUserErrors } =
+ res.body.data.customerAccessTokenCreate
+
+ if (customerUserErrors.length) {
+ return { error: customerUserErrors[0].message }
+ }
+
+ if (customerAccessToken) {
+ cookies().set('customerAccessToken', customerAccessToken.accessToken, {
+ expires: new Date(customerAccessToken.expiresAt),
+ httpOnly: true,
+ secure: process.env.NODE_ENV === 'production',
+ })
+ }
+
+ return { success: true }
+ } catch (error) {
+ return { error: 'An unexpected error occurred. Please try again.' }
+ }
+}
+
+export async function LogoutCustomerAction() {
+ const customerAccessToken = cookies().get('customerAccessToken')?.value
+
+ if (customerAccessToken) {
+ try {
+ await shopifyFetch({
+ query: customerAccessTokenDeleteMutation,
+ variables: {
+ customerAccessToken,
+ },
+ cache: 'no-store',
+ })
+ } catch (error) {
+ console.error('Error during logout:', error)
+ }
+
+ cookies().delete('customerAccessToken')
+ }
+
+ return { success: true }
+}
+
+export async function CreateCustomerAction(prevState, formData) {
+ const firstName = formData.get('firstName')
+ const lastName = formData.get('lastName')
+ const email = formData.get('email')
+ const password = formData.get('password')
+
+ try {
+ const res = await shopifyFetch({
+ query: customerCreateMutation,
+ variables: {
+ input: {
+ firstName,
+ lastName,
+ email,
+ password,
+ },
+ },
+ cache: 'no-store',
+ })
+
+ const { customer, customerUserErrors } = res.body.data.customerCreate
+
+ if (customerUserErrors.length) {
+ return { error: customerUserErrors[0].message }
+ }
+
+ return { success: true, customer }
+ } catch (error) {
+ return { error: 'An unexpected error occurred. Please try again.' }
+ }
+}
+
+export async function getCustomer() {
+ const customerAccessToken = cookies().get('customerAccessToken')?.value
+
+ if (!customerAccessToken) {
+ return null
+ }
+
+ try {
+ const res = await shopifyFetch({
+ query: getCustomerQuery,
+ variables: {
+ customerAccessToken,
+ },
+ cache: 'no-store',
+ })
+
+ return res.body.data.customer
+ } catch (error) {
+ console.error('Error fetching customer data:', error)
+ return null
+ }
+}
diff --git a/libs/shopify/mutations/customer.js b/libs/shopify/mutations/customer.js
new file mode 100644
index 00000000..490999e1
--- /dev/null
+++ b/libs/shopify/mutations/customer.js
@@ -0,0 +1,46 @@
+export const customerAccessTokenCreateMutation = /* GraphQL */ `
+ mutation customerAccessTokenCreate($input: CustomerAccessTokenCreateInput!) {
+ customerAccessTokenCreate(input: $input) {
+ customerAccessToken {
+ accessToken
+ expiresAt
+ }
+ customerUserErrors {
+ code
+ field
+ message
+ }
+ }
+ }
+`
+
+export const customerAccessTokenDeleteMutation = /* GraphQL */ `
+ mutation customerAccessTokenDelete($customerAccessToken: String!) {
+ customerAccessTokenDelete(customerAccessToken: $customerAccessToken) {
+ deletedAccessToken
+ deletedCustomerAccessTokenId
+ userErrors {
+ field
+ message
+ }
+ }
+ }
+`
+
+export const customerCreateMutation = /* GraphQL */ `
+ mutation customerCreate($input: CustomerCreateInput!) {
+ customerCreate(input: $input) {
+ customer {
+ id
+ email
+ firstName
+ lastName
+ }
+ customerUserErrors {
+ code
+ field
+ message
+ }
+ }
+ }
+`
diff --git a/libs/shopify/queries/customer.js b/libs/shopify/queries/customer.js
new file mode 100644
index 00000000..93812277
--- /dev/null
+++ b/libs/shopify/queries/customer.js
@@ -0,0 +1,38 @@
+export const getCustomerQuery = /* GraphQL */ `
+ query getCustomer($customerAccessToken: String!) {
+ customer(customerAccessToken: $customerAccessToken) {
+ id
+ firstName
+ lastName
+ email
+ phone
+ addresses(first: 5) {
+ edges {
+ node {
+ id
+ address1
+ address2
+ city
+ province
+ country
+ zip
+ }
+ }
+ }
+ orders(first: 5) {
+ edges {
+ node {
+ id
+ orderNumber
+ totalPrice {
+ amount
+ currencyCode
+ }
+ processedAt
+ fulfillmentStatus
+ }
+ }
+ }
+ }
+ }
+`