Initial commit

This commit is contained in:
Rami Bitar
2026-08-08 14:21:24 -04:00
commit 58742d5d00
127 changed files with 18859 additions and 0 deletions
+177
View File
@@ -0,0 +1,177 @@
import { gql } from '@shopify/hydrogen';
// Cart Fragment for consistent cart data
const CartFragment = gql(`
fragment CartFragment on Cart {
id
checkoutUrl
totalQuantity
discountCodes {
code
applicable
}
cost {
subtotalAmount {
amount
currencyCode
}
totalAmount {
amount
currencyCode
}
totalTaxAmount {
amount
currencyCode
}
}
lines(first: 100) {
edges {
node {
id
quantity
cost {
totalAmount {
amount
currencyCode
}
}
merchandise {
... on ProductVariant {
id
title
selectedOptions {
name
value
}
price {
amount
currencyCode
}
image {
id
url
altText
width
height
}
product {
id
title
handle
vendor
}
}
}
}
}
}
}
`);
// Create a new cart
export const CREATE_CART_MUTATION = gql(
`
mutation CreateCart($lines: [CartLineInput!], $country: CountryCode, $language: LanguageCode)
@inContext(country: $country, language: $language) {
cartCreate(input: { lines: $lines }) {
cart {
...CartFragment
}
userErrors {
field
message
}
}
}
`,
[CartFragment]
);
// Add lines to cart
export const ADD_CART_LINES_MUTATION = gql(
`
mutation AddCartLines($cartId: ID!, $lines: [CartLineInput!]!, $country: CountryCode, $language: LanguageCode)
@inContext(country: $country, language: $language) {
cartLinesAdd(cartId: $cartId, lines: $lines) {
cart {
...CartFragment
}
userErrors {
field
message
}
}
}
`,
[CartFragment]
);
// Update cart lines
export const UPDATE_CART_LINES_MUTATION = gql(
`
mutation UpdateCartLines($cartId: ID!, $lines: [CartLineUpdateInput!]!, $country: CountryCode, $language: LanguageCode)
@inContext(country: $country, language: $language) {
cartLinesUpdate(cartId: $cartId, lines: $lines) {
cart {
...CartFragment
}
userErrors {
field
message
}
}
}
`,
[CartFragment]
);
// Remove lines from cart
export const REMOVE_CART_LINES_MUTATION = gql(
`
mutation RemoveCartLines($cartId: ID!, $lineIds: [ID!]!, $country: CountryCode, $language: LanguageCode)
@inContext(country: $country, language: $language) {
cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {
cart {
...CartFragment
}
userErrors {
field
message
}
}
}
`,
[CartFragment]
);
// Apply (or clear) discount codes on the cart
export const UPDATE_CART_DISCOUNT_CODES_MUTATION = gql(
`
mutation UpdateCartDiscountCodes($cartId: ID!, $discountCodes: [String!]!, $country: CountryCode, $language: LanguageCode)
@inContext(country: $country, language: $language) {
cartDiscountCodesUpdate(cartId: $cartId, discountCodes: $discountCodes) {
cart {
...CartFragment
}
userErrors {
field
message
}
}
}
`,
[CartFragment]
);
// Get cart by ID
export const GET_CART_QUERY = gql(
`
query GetCart($cartId: ID!, $country: CountryCode, $language: LanguageCode)
@inContext(country: $country, language: $language) {
cart(id: $cartId) {
...CartFragment
}
}
`,
[CartFragment]
);
+91
View File
@@ -0,0 +1,91 @@
import { gql } from '@shopify/hydrogen';
import { ProductFragment } from '@/graphql/products';
// Get all collections
export const GET_COLLECTIONS_QUERY = gql(`
query GetCollections($first: Int!, $country: CountryCode, $language: LanguageCode)
@inContext(country: $country, language: $language) {
collections(first: $first) {
edges {
node {
id
title
handle
description
descriptionHtml
image {
id
url
altText
width
height
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
`);
// Get products in a collection
export const GET_COLLECTION_PRODUCTS_QUERY = gql(
`
query GetCollectionProducts(
$handle: String!
$first: Int!
$after: String
$sortKey: ProductCollectionSortKeys
$reverse: Boolean
$filters: [ProductFilter!]
$country: CountryCode
$language: LanguageCode
) @inContext(country: $country, language: $language) {
collection(handle: $handle) {
id
title
handle
description
descriptionHtml
image {
id
url
altText
width
height
}
products(
first: $first
after: $after
sortKey: $sortKey
reverse: $reverse
filters: $filters
) {
filters {
id
label
type
values {
id
label
count
input
}
}
edges {
node {
...ProductFragment
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
`,
[ProductFragment]
);
+178
View File
@@ -0,0 +1,178 @@
// Customer account operations (classic Storefront customer accounts).
// https://shopify.dev/docs/api/storefront/latest/objects/Customer
import { gql } from '@shopify/hydrogen';
const CustomerFragment = gql(`
fragment CustomerFragment on Customer {
id
email
firstName
lastName
phone
displayName
acceptsMarketing
createdAt
defaultAddress {
id
firstName
lastName
address1
address2
city
province
zip
country
phone
}
}
`);
export const CUSTOMER_QUERY = gql(
`
query GetCustomer($customerAccessToken: String!, $orderCount: Int!) {
customer(customerAccessToken: $customerAccessToken) {
...CustomerFragment
orders(first: $orderCount, reverse: true) {
edges {
node {
id
orderNumber
processedAt
financialStatus
fulfillmentStatus
statusUrl
currentTotalPrice {
amount
currencyCode
}
lineItems(first: 5) {
edges {
node {
title
quantity
variant {
image {
url
altText
}
}
}
}
}
}
}
}
}
}
`,
[CustomerFragment]
);
export const CUSTOMER_CREATE_MUTATION = gql(`
mutation CustomerCreate($input: CustomerCreateInput!) {
customerCreate(input: $input) {
customer {
id
email
}
customerUserErrors {
code
field
message
}
}
}
`);
export const CUSTOMER_ACCESS_TOKEN_CREATE_MUTATION = gql(`
mutation CustomerAccessTokenCreate($input: CustomerAccessTokenCreateInput!) {
customerAccessTokenCreate(input: $input) {
customerAccessToken {
accessToken
expiresAt
}
customerUserErrors {
code
field
message
}
}
}
`);
export const CUSTOMER_ACCESS_TOKEN_DELETE_MUTATION = gql(`
mutation CustomerAccessTokenDelete($customerAccessToken: String!) {
customerAccessTokenDelete(customerAccessToken: $customerAccessToken) {
deletedAccessToken
userErrors {
field
message
}
}
}
`);
// Sends the "reset your password" email.
export const CUSTOMER_RECOVER_MUTATION = gql(`
mutation CustomerRecover($email: String!) {
customerRecover(email: $email) {
customerUserErrors {
code
field
message
}
}
}
`);
// Completes the reset using the id + token from the emailed link.
export const CUSTOMER_RESET_MUTATION = gql(`
mutation CustomerReset($id: ID!, $input: CustomerResetInput!) {
customerReset(id: $id, input: $input) {
customerAccessToken {
accessToken
expiresAt
}
customerUserErrors {
code
field
message
}
}
}
`);
// Activation link sent to customers created by the merchant.
export const CUSTOMER_ACTIVATE_MUTATION = gql(`
mutation CustomerActivate($id: ID!, $input: CustomerActivateInput!) {
customerActivate(id: $id, input: $input) {
customerAccessToken {
accessToken
expiresAt
}
customerUserErrors {
code
field
message
}
}
}
`);
export const CUSTOMER_UPDATE_MUTATION = gql(
`
mutation CustomerUpdate($customerAccessToken: String!, $customer: CustomerUpdateInput!) {
customerUpdate(customerAccessToken: $customerAccessToken, customer: $customer) {
customer {
...CustomerFragment
}
customerUserErrors {
code
field
message
}
}
}
`,
[CustomerFragment]
);
+45
View File
@@ -0,0 +1,45 @@
import { gql } from '@shopify/hydrogen';
// Shop policies are exposed on the `shop` object of the Storefront API.
// There is no lookup-by-handle field, so we fetch all of them and match.
export const GET_SHOP_POLICIES_QUERY = gql(`
query GetShopPolicies {
shop {
privacyPolicy {
id
title
handle
body
url
}
termsOfService {
id
title
handle
body
url
}
refundPolicy {
id
title
handle
body
url
}
shippingPolicy {
id
title
handle
body
url
}
subscriptionPolicy {
id
title
handle
body
url
}
}
}
`);
+150
View File
@@ -0,0 +1,150 @@
import { gql } from '@shopify/hydrogen';
// Product Fragment for consistent product data
export const ProductFragment = gql(`
fragment ProductFragment on Product {
id
title
handle
description
descriptionHtml
vendor
productType
tags
availableForSale
priceRange {
minVariantPrice {
amount
currencyCode
}
maxVariantPrice {
amount
currencyCode
}
}
compareAtPriceRange {
minVariantPrice {
amount
currencyCode
}
maxVariantPrice {
amount
currencyCode
}
}
images(first: 10) {
edges {
node {
id
url
altText
width
height
}
}
}
variants(first: 100) {
edges {
node {
id
title
availableForSale
quantityAvailable
selectedOptions {
name
value
}
price {
amount
currencyCode
}
compareAtPrice {
amount
currencyCode
}
image {
id
url
altText
width
height
}
}
}
}
options {
id
name
values
optionValues {
id
name
swatch {
color
image {
previewImage {
url
}
}
}
firstSelectableVariant {
id
image {
id
url
altText
width
height
}
}
}
}
}
`);
// Get multiple products
export const GET_PRODUCTS_QUERY = gql(
`
query GetProducts($first: Int!, $after: String, $query: String, $sortKey: ProductSortKeys, $reverse: Boolean, $country: CountryCode, $language: LanguageCode)
@inContext(country: $country, language: $language) {
products(first: $first, after: $after, query: $query, sortKey: $sortKey, reverse: $reverse) {
edges {
node {
...ProductFragment
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
`,
[ProductFragment]
);
// Get a single product by handle
export const GET_PRODUCT_QUERY = gql(
`
query GetProduct($handle: String!, $country: CountryCode, $language: LanguageCode)
@inContext(country: $country, language: $language) {
product(handle: $handle) {
...ProductFragment
}
}
`,
[ProductFragment]
);
// Get product recommendations
export const QUERY_PRODUCT_RECOMMENDATIONS = gql(
`
query GetProductRecommendations($productId: ID!, $country: CountryCode, $language: LanguageCode)
@inContext(country: $country, language: $language) {
productRecommendations(productId: $productId) {
...ProductFragment
}
}
`,
[ProductFragment]
);
+89
View File
@@ -0,0 +1,89 @@
import { gql } from '@shopify/hydrogen';
import { ProductFragment } from '@/graphql/products';
// Storefront search over products. `productFilters` accepts the raw `input`
// values returned in `productFilters[].values[].input`, so facets round-trip
// without the client needing to know each filter's shape.
export const SEARCH_PRODUCTS_QUERY = gql(
`
query SearchProducts(
$query: String!
$first: Int!
$after: String
$sortKey: SearchSortKeys
$reverse: Boolean
$productFilters: [ProductFilter!]
$country: CountryCode
$language: LanguageCode
) @inContext(country: $country, language: $language) {
search(
query: $query
first: $first
after: $after
types: PRODUCT
sortKey: $sortKey
reverse: $reverse
productFilters: $productFilters
) {
totalCount
pageInfo {
hasNextPage
endCursor
}
productFilters {
id
label
type
values {
id
label
count
input
}
}
edges {
node {
# types: PRODUCT already narrows the results, but the schema still
# types nodes as a union — __typename lets callers narrow too.
__typename
... on Product {
...ProductFragment
}
}
}
}
}
`,
[ProductFragment]
);
// Lightweight variant for the autocomplete dropdown — just enough to render a
// row, so the dialog stays responsive while typing.
export const SEARCH_SUGGESTIONS_QUERY = gql(`
query SearchSuggestions($query: String!, $first: Int!, $country: CountryCode, $language: LanguageCode)
@inContext(country: $country, language: $language) {
search(query: $query, first: $first, types: PRODUCT) {
totalCount
edges {
node {
__typename
... on Product {
id
title
handle
featuredImage {
url
altText
}
priceRange {
minVariantPrice {
amount
currencyCode
}
}
}
}
}
}
}
`);