From febe81f8123f16226664716a0a644165c7125388 Mon Sep 17 00:00:00 2001 From: dangthang1903 Date: Thu, 26 Oct 2023 10:10:29 +0700 Subject: [PATCH 01/26] feat: add section promotion grid --- app/sections/promotion-grid/item.tsx | 216 ++++++++++++++++++ .../promotion-grid/promotion-grid.tsx | 89 ++++++++ app/weaverse/components.ts | 6 + 3 files changed, 311 insertions(+) create mode 100644 app/sections/promotion-grid/item.tsx create mode 100644 app/sections/promotion-grid/promotion-grid.tsx diff --git a/app/sections/promotion-grid/item.tsx b/app/sections/promotion-grid/item.tsx new file mode 100644 index 00000000..06b72af9 --- /dev/null +++ b/app/sections/promotion-grid/item.tsx @@ -0,0 +1,216 @@ +import type { + HydrogenComponentProps, + HydrogenComponentSchema, +} from '@weaverse/hydrogen'; +import { forwardRef } from 'react'; +import { Image } from '@shopify/hydrogen'; +import { CSSProperties } from 'react'; + +interface PromotionItemProps extends HydrogenComponentProps { + backgroundImage: { + url: string; + altText: string; + width?: number; + height?: number; + }; + subHeading: string; + subHeadingSize: string; + subHeadingColor: string; + heading: string; + headingSize: string; + headingColor: string; + descriptionText: string; + descriptionSize: string; + descriptionColor: string; + buttonLabel1: string; + buttonLink1: string; + buttonLabel2: string; + buttonLink2: string; + enableNewtab: boolean; +} + +let PromotionGridItem = forwardRef((props, ref) => { + let { backgroundImage, subHeading, subHeadingSize, subHeadingColor, heading, headingSize, headingColor, descriptionText, descriptionSize, descriptionColor, buttonLabel1, buttonLink1, buttonLabel2, buttonLink2, enableNewtab, ...rest } = props; + return ( +
+
+ {backgroundImage ? : +
+ + + + +
} +
+
+
+ {subHeading &&

{subHeading}

} + {heading &&

{heading}

} + {descriptionText &&

{descriptionText}

} +
+ {buttonLabel1 && {buttonLabel1}} + {buttonLabel2 && {buttonLabel2}} +
+
+
+
+ ); +}); + +export default PromotionGridItem; + +export let schema: HydrogenComponentSchema = { + type: 'promotion-item', + title: 'Promotion item', + inspector: [ + { + group: 'Promotion item', + inputs: [ + { + type: 'image', + name: 'backgroundImage', + label: 'Background image', + }, + { + type: 'text', + name: 'subHeading', + label: 'Subheading', + defaultValue: 'Subheading', + placeholder: 'Subheading', + }, + { + type: 'toggle-group', + label: 'Subheading size', + name: 'subHeadingSize', + configs: { + options: [ + { label: 'XS', value: '14px' }, + { label: 'S', value: '16px' }, + { label: 'M', value: '18px' }, + { label: 'L', value: '20px' }, + { label: 'XL', value: '22px' }, + ], + }, + defaultValue: '16px', + }, + { + type: 'color', + name: 'subHeadingColor', + label: 'Subheading color', + defaultValue: '#333333', + }, + { + type: 'text', + name: 'heading', + label: 'Heading', + defaultValue: 'Heading for Image', + placeholder: 'Heading for image section', + }, + { + type: 'toggle-group', + label: 'Heading size', + name: 'headingSize', + configs: { + options: [ + { label: 'XS', value: '22px' }, + { label: 'S', value: '24px' }, + { label: 'M', value: '26px' }, + { label: 'L', value: '28px' }, + { label: 'XL', value: '30px' }, + ], + }, + defaultValue: '24px', + }, + { + type: 'color', + name: 'headingColor', + label: 'Heading color', + defaultValue: '#333333', + }, + { + type: 'textarea', + name: 'descriptionText', + label: 'Text', + defaultValue: 'Include the smaller details of your promotion in text below the title.', + }, + { + type: 'toggle-group', + label: 'Description size', + name: 'descriptionSize', + configs: { + options: [ + { label: 'XS', value: '14px' }, + { label: 'S', value: '16px' }, + { label: 'M', value: '18px' }, + { label: 'L', value: '20px' }, + { label: 'XL', value: '22px' }, + ], + }, + defaultValue: '16px', + }, + { + type: 'color', + name: 'descriptionColor', + label: 'Description color', + defaultValue: '#333333', + }, + { + type: 'text', + name: 'buttonLabel1', + label: 'Button label 1', + defaultValue: 'Button', + }, + { + type: 'text', + name: 'buttonLink1', + label: 'Button #1 link', + placeholder: 'https://', + }, + { + type: 'text', + name: 'buttonLabel2', + label: 'Button label 2', + defaultValue: 'Button', + }, + { + type: 'text', + name: 'buttonLink2', + label: 'Button #2 link', + placeholder: 'https://', + }, + { + type: 'switch', + name: 'enableNewtab', + label: 'Open in new tab', + defaultValue: true, + }, + { + type: 'toggle-group', + label: 'Button #1 style', + name: 'buttonStyle1', + configs: { + options: [ + { label: '1', value: '1' }, + { label: '2', value: '2' }, + { label: '3', value: '3' }, + ], + }, + defaultValue: '1', + }, + { + type: 'toggle-group', + label: 'Button #2 style', + name: 'buttonStyle2', + configs: { + options: [ + { label: '1', value: '1' }, + { label: '2', value: '2' }, + { label: '3', value: '3' }, + ], + }, + defaultValue: '1', + }, + ], + }, + ], +}; diff --git a/app/sections/promotion-grid/promotion-grid.tsx b/app/sections/promotion-grid/promotion-grid.tsx new file mode 100644 index 00000000..d9a3c7d7 --- /dev/null +++ b/app/sections/promotion-grid/promotion-grid.tsx @@ -0,0 +1,89 @@ +import type { + HydrogenComponentProps, + HydrogenComponentSchema, +} from '@weaverse/hydrogen'; +import { forwardRef } from 'react'; +import { CSSProperties } from 'react'; + +interface PromotionProps extends HydrogenComponentProps { + gap: number; + topPadding: number; + bottomPadding: number; +} + +let PromotionGrid = forwardRef((props, ref) => { + let {gap, topPadding, bottomPadding, children, ...rest } = props; + let spacingStyle: CSSProperties = { + gap: `${gap}px`, + paddingTop: `${topPadding}px`, + paddingBottom: `${bottomPadding}px`, + } as CSSProperties; + return ( +
+
+
+ {children} +
+
+
+ ); +}); + +export default PromotionGrid; + +export let schema: HydrogenComponentSchema = { + type: 'promotion-grid', + title: 'Promotion grid', + toolbar: ['general-settings', ['duplicate', 'delete']], + inspector: [ + { + group: 'Promotion', + inputs: [ + { + type: 'range', + name: 'gap', + label: 'Gap', + defaultValue: 30, + configs: { + min: 20, + max: 50, + step: 10, + unit: 'px', + }, + }, + { + type: 'range', + name: 'topPadding', + label: 'Top padding', + defaultValue: 40, + configs: { + min: 10, + max: 100, + step: 10, + unit: 'px', + }, + }, + { + type: 'range', + name: 'bottomPadding', + label: 'Bottom padding', + defaultValue: 40, + configs: { + min: 10, + max: 100, + step: 10, + unit: 'px', + }, + }, + ], + }, + ], + childTypes: ['promotion-item'], + presets: { + children: [ + { + type: 'promotion-item', + }, + ], + }, +}; diff --git a/app/weaverse/components.ts b/app/weaverse/components.ts index 8ad12e53..3b32cf9a 100644 --- a/app/weaverse/components.ts +++ b/app/weaverse/components.ts @@ -36,6 +36,8 @@ import * as RichText from '~/sections/rich-text/index'; import * as RichTextHeadingItem from '~/sections/rich-text/headings-item'; import * as RichTextDescriptionItem from '~/sections/rich-text/descriptions-item'; import * as RichTextButtonItem from '~/sections/rich-text/buttons-item'; +import * as PromotionGrid from '~/sections/promotion-grid/promotion-grid'; +import * as PromotionGridItem from '~/sections/promotion-grid/item'; export let components: HydrogenComponent[] = [ Main, @@ -60,6 +62,10 @@ export let components: HydrogenComponent[] = [ RichTextHeadingItem, RichTextDescriptionItem, RichTextButtonItem, + ImageHotspot, + ImageHotspotItem, + PromotionGrid, + PromotionGridItem, Blogs, BlogPost, AllProducts, From 9cde60b3db9f22b88d3482704ed2cee0db74509f Mon Sep 17 00:00:00 2001 From: dangthang1903 Date: Thu, 26 Oct 2023 10:18:13 +0700 Subject: [PATCH 02/26] refactor: update component --- app/weaverse/components.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/weaverse/components.ts b/app/weaverse/components.ts index 3b32cf9a..f0840f1f 100644 --- a/app/weaverse/components.ts +++ b/app/weaverse/components.ts @@ -62,8 +62,6 @@ export let components: HydrogenComponent[] = [ RichTextHeadingItem, RichTextDescriptionItem, RichTextButtonItem, - ImageHotspot, - ImageHotspotItem, PromotionGrid, PromotionGridItem, Blogs, From e15c128c564ed36a5ba779cac471379088c1cf68 Mon Sep 17 00:00:00 2001 From: dangthang1903 Date: Thu, 26 Oct 2023 11:43:55 +0700 Subject: [PATCH 03/26] refactor: add icon and update variable --- app/components/Icon.tsx | 9 +++++++++ app/sections/promotion-grid/item.tsx | 25 +++++++++---------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/app/components/Icon.tsx b/app/components/Icon.tsx index be9114ee..19bee27f 100644 --- a/app/components/Icon.tsx +++ b/app/components/Icon.tsx @@ -313,3 +313,12 @@ export function IconFacebook(props: IconProps) { ); } + +export function IconImageBlank(props: IconProps) { + return ( + + + + + ); +} diff --git a/app/sections/promotion-grid/item.tsx b/app/sections/promotion-grid/item.tsx index 06b72af9..3d04f793 100644 --- a/app/sections/promotion-grid/item.tsx +++ b/app/sections/promotion-grid/item.tsx @@ -1,18 +1,14 @@ import type { HydrogenComponentProps, HydrogenComponentSchema, + WeaverseImage, } from '@weaverse/hydrogen'; import { forwardRef } from 'react'; import { Image } from '@shopify/hydrogen'; -import { CSSProperties } from 'react'; +import {IconImageBlank} from '~/components'; interface PromotionItemProps extends HydrogenComponentProps { - backgroundImage: { - url: string; - altText: string; - width?: number; - height?: number; - }; + backgroundImage: WeaverseImage; subHeading: string; subHeadingSize: string; subHeadingColor: string; @@ -26,20 +22,17 @@ interface PromotionItemProps extends HydrogenComponentProps { buttonLink1: string; buttonLabel2: string; buttonLink2: string; - enableNewtab: boolean; + openInNewTab: boolean; } let PromotionGridItem = forwardRef((props, ref) => { - let { backgroundImage, subHeading, subHeadingSize, subHeadingColor, heading, headingSize, headingColor, descriptionText, descriptionSize, descriptionColor, buttonLabel1, buttonLink1, buttonLabel2, buttonLink2, enableNewtab, ...rest } = props; + let { backgroundImage, subHeading, subHeadingSize, subHeadingColor, heading, headingSize, headingColor, descriptionText, descriptionSize, descriptionColor, buttonLabel1, buttonLink1, buttonLabel2, buttonLink2, openInNewTab, ...rest } = props; return (
{backgroundImage ? :
- - - - +
}
@@ -48,8 +41,8 @@ let PromotionGridItem = forwardRef((props, r {heading &&

{heading}

} {descriptionText &&

{descriptionText}

}
- {buttonLabel1 && {buttonLabel1}} - {buttonLabel2 && {buttonLabel2}} + {buttonLabel1 && {buttonLabel1}} + {buttonLabel2 && {buttonLabel2}}
@@ -180,7 +173,7 @@ export let schema: HydrogenComponentSchema = { }, { type: 'switch', - name: 'enableNewtab', + name: 'openInNewTab', label: 'Open in new tab', defaultValue: true, }, From eb87a7ea50888bb059fffc9e78779eb8526d139e Mon Sep 17 00:00:00 2001 From: dangthang1903 Date: Sun, 29 Oct 2023 21:09:55 +0700 Subject: [PATCH 04/26] refactor: update setting button style --- app/sections/promotion-grid/item.tsx | 29 +++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/app/sections/promotion-grid/item.tsx b/app/sections/promotion-grid/item.tsx index 3d04f793..16df1f10 100644 --- a/app/sections/promotion-grid/item.tsx +++ b/app/sections/promotion-grid/item.tsx @@ -6,6 +6,7 @@ import type { import { forwardRef } from 'react'; import { Image } from '@shopify/hydrogen'; import {IconImageBlank} from '~/components'; +import clsx from 'clsx'; interface PromotionItemProps extends HydrogenComponentProps { backgroundImage: WeaverseImage; @@ -23,10 +24,12 @@ interface PromotionItemProps extends HydrogenComponentProps { buttonLabel2: string; buttonLink2: string; openInNewTab: boolean; + buttonStyle1: string; + buttonStyle2: string; } let PromotionGridItem = forwardRef((props, ref) => { - let { backgroundImage, subHeading, subHeadingSize, subHeadingColor, heading, headingSize, headingColor, descriptionText, descriptionSize, descriptionColor, buttonLabel1, buttonLink1, buttonLabel2, buttonLink2, openInNewTab, ...rest } = props; + let { backgroundImage, subHeading, subHeadingSize, subHeadingColor, heading, headingSize, headingColor, descriptionText, descriptionSize, descriptionColor, buttonLabel1, buttonLink1, buttonLabel2, buttonLink2, openInNewTab, buttonStyle1, buttonStyle2, ...rest } = props; return (
@@ -41,8 +44,8 @@ let PromotionGridItem = forwardRef((props, r {heading &&

{heading}

} {descriptionText &&

{descriptionText}

}
- {buttonLabel1 && {buttonLabel1}} - {buttonLabel2 && {buttonLabel2}} + {buttonLabel1 && {buttonLabel1}} + {buttonLabel2 && {buttonLabel2}}
@@ -179,29 +182,29 @@ export let schema: HydrogenComponentSchema = { }, { type: 'toggle-group', - label: 'Button #1 style', + label: 'Button style #1', name: 'buttonStyle1', configs: { options: [ - { label: '1', value: '1' }, - { label: '2', value: '2' }, - { label: '3', value: '3' }, + { label: '1', value: 'transition hover:bg-white border-2 border-solid hover:border-gray-900 hover:text-black bg-black text-white' }, + { label: '2', value: 'transition bg-white border-2 border-solid border-gray-900 text-black hover:bg-black hover:text-white' }, + { label: '3', value: 'transition hover:bg-white border-2 border-solid border-white hover:text-black bg-gray-200 text-white' }, ], }, - defaultValue: '1', + defaultValue: 'transition hover:bg-white border-2 border-solid hover:border-gray-900 hover:text-black bg-black text-white', }, { type: 'toggle-group', - label: 'Button #2 style', + label: 'Button style #2', name: 'buttonStyle2', configs: { options: [ - { label: '1', value: '1' }, - { label: '2', value: '2' }, - { label: '3', value: '3' }, + { label: '1', value: 'transition hover:bg-white border-2 border-solid hover:border-gray-900 hover:text-black bg-black text-white' }, + { label: '2', value: 'transition bg-white border-2 border-solid border-gray-900 text-black hover:bg-black hover:text-white' }, + { label: '3', value: 'transition hover:bg-white border-2 border-solid border-white hover:text-black bg-gray-200 text-white' }, ], }, - defaultValue: '1', + defaultValue: 'transition hover:bg-white border-2 border-solid hover:border-gray-900 hover:text-black bg-black text-white', }, ], }, From 108c21bab7bd9100c16716289d672e6d7a2a2f2c Mon Sep 17 00:00:00 2001 From: dangthang1903 Date: Thu, 2 Nov 2023 10:06:31 +0700 Subject: [PATCH 05/26] chore: update settings for image section --- .../header-image/button-image-item.tsx | 68 +++++++++++++ .../header-image/description-text-item.tsx | 34 +++++-- app/sections/header-image/header-image.tsx | 99 ++++++++----------- app/sections/header-image/heading-item.tsx | 29 ++---- app/sections/header-image/subheading-item.tsx | 16 ++- app/weaverse/components.ts | 2 + 6 files changed, 160 insertions(+), 88 deletions(-) create mode 100644 app/sections/header-image/button-image-item.tsx diff --git a/app/sections/header-image/button-image-item.tsx b/app/sections/header-image/button-image-item.tsx new file mode 100644 index 00000000..59061fb6 --- /dev/null +++ b/app/sections/header-image/button-image-item.tsx @@ -0,0 +1,68 @@ +import type { + HydrogenComponentProps, + HydrogenComponentSchema, +} from '@weaverse/hydrogen'; +import { forwardRef } from 'react'; +import clsx from 'clsx'; + + +interface ButtonItemProps extends HydrogenComponentProps { + buttonLink: string; + buttonLabel: string; + openInNewTab: boolean; + buttonStyle: string; +} + +let buttonItem = forwardRef((props, ref) => { + let { buttonLink, buttonLabel, openInNewTab, buttonStyle, ...rest } = props; + return ( +
+ {buttonLabel && {buttonLabel}} +
+ ); +}); + +export default buttonItem; + +export let schema: HydrogenComponentSchema = { + type: 'button-image--item', + title: 'Button item', + inspector: [ + { + group: 'Button', + inputs: [ + { + type: 'text', + name: 'buttonLabel', + label: 'Button label', + defaultValue: 'Button', + }, + { + type: 'text', + name: 'buttonLink', + label: 'Button link', + placeholder: 'https://', + }, + { + type: 'switch', + name: 'openInNewTab', + label: 'Open in new tab', + defaultValue: true, + }, + { + type: 'toggle-group', + label: 'Button style', + name: 'buttonStyle', + configs: { + options: [ + { label: '1', value: 'transition hover:bg-white border-2 border-solid hover:border-gray-900 hover:text-black bg-black text-white' }, + { label: '2', value: 'transition bg-white border-2 border-solid border-gray-900 text-black hover:bg-black hover:text-white' }, + { label: '3', value: 'transition hover:bg-white border-2 border-solid border-white hover:text-black bg-gray-200 text-white' }, + ], + }, + defaultValue: 'transition bg-white border-2 border-solid border-gray-900 text-black hover:bg-black hover:text-white', + }, + ], + }, + ], +} diff --git a/app/sections/header-image/description-text-item.tsx b/app/sections/header-image/description-text-item.tsx index db9ef633..6d3b5983 100644 --- a/app/sections/header-image/description-text-item.tsx +++ b/app/sections/header-image/description-text-item.tsx @@ -6,17 +6,18 @@ import {forwardRef} from 'react'; interface DescriptionTextItemProps extends HydrogenComponentProps { descriptionText: string; + descriptionSize: string; + descriptionColor: string; } let DescriptionTextItem = forwardRef( (props, ref) => { - let {descriptionText, ...rest} = props; + let {descriptionText, descriptionSize, descriptionColor, ...rest} = props; return (
-

+

{descriptionText}

); }, @@ -32,11 +33,32 @@ export let schema: HydrogenComponentSchema = { group: 'Description text', inputs: [ { - type: 'richtext', + type: 'textarea', label: 'Text', name: 'descriptionText', defaultValue: 'Pair large text with an image to tell a story.', }, + { + type: 'toggle-group', + label: 'Text size', + name: 'descriptionSize', + configs: { + options: [ + { label: 'XS', value: '14px' }, + { label: 'S', value: '16px' }, + { label: 'M', value: '18px' }, + { label: 'L', value: '20px' }, + { label: 'XL', value: '22px' }, + ], + }, + defaultValue: '16px', + }, + { + type: 'color', + name: 'descriptionColor', + label: 'Description color', + defaultValue: '#333333', + }, ], }, ], diff --git a/app/sections/header-image/header-image.tsx b/app/sections/header-image/header-image.tsx index 9f8b68f1..aa375171 100644 --- a/app/sections/header-image/header-image.tsx +++ b/app/sections/header-image/header-image.tsx @@ -1,34 +1,30 @@ import type { HydrogenComponentProps, HydrogenComponentSchema, + WeaverseImage, } from '@weaverse/hydrogen'; import { forwardRef } from 'react'; import { CSSProperties } from 'react'; import clsx from 'clsx'; import { Image } from '@shopify/hydrogen'; +import { IconImageBlank } from '~/components'; interface HeaderImageProps extends HydrogenComponentProps { - backgroundImage: { - url: string; - altText: string; - width?: number; - height?: number; - }; + backgroundImage: WeaverseImage; contentAlignment: string; enableOverlay: boolean; overlayColor: string; overlayOpacity: number; - sectionHeight: string; - buttonLabel: string; - buttonLink: string; - loading: HTMLImageElement['loading']; + sectionHeightDesktop: number; + sectionHeightMobile: number; } let HeaderImage = forwardRef((props, ref) => { - let { backgroundImage, contentAlignment, enableOverlay, overlayColor, overlayOpacity, sectionHeight, buttonLabel, buttonLink, loading, children, ...rest } = props; + let { backgroundImage, contentAlignment, enableOverlay, overlayColor, overlayOpacity, sectionHeightDesktop, sectionHeightMobile, children, ...rest } = props; let sectionStyle: CSSProperties = { justifyContent: `${contentAlignment}`, - '--section-height': `${sectionHeight}`, + '--section-height-desktop': `${sectionHeightDesktop}px`, + '--section-height-mobile': `${sectionHeightMobile}px`, '--overlay-opacity': `${overlayOpacity}%`, '--overlay-color': `${overlayColor}` } as CSSProperties; @@ -36,18 +32,18 @@ let HeaderImage = forwardRef((props, ref) => { return (
- {backgroundImage && } - {enableOverlay && backgroundImage &&
} + {backgroundImage ? : +
+ +
+ } + {enableOverlay &&
}
-
- {children?.map((child) => { - return child; - })} - {buttonLabel && {buttonLabel}} +
+ {children}
); @@ -108,58 +104,47 @@ export let schema: HydrogenComponentSchema = { condition: `enableOverlay.eq.true` }, { - type: 'text', - name: 'sectionHeight', - label: 'Section height', - defaultValue: '480px', - placeholder: 'Example: 100px', - }, - { - type: 'text', - name: 'buttonLabel', - label: 'Button label', - defaultValue: 'Button', - }, - { - type: 'text', - name: 'buttonLink', - label: 'Button link', - placeholder: 'https://', + type: 'range', + name: 'sectionHeightDesktop', + label: 'Section height desktop', + defaultValue: 450, + configs: { + min: 400, + max: 700, + step: 10, + unit: 'px', + }, }, { - type: 'toggle-group', - name: 'loading', - label: 'Background image loading', - defaultValue: 'eager', + type: 'range', + name: 'sectionHeightMobile', + label: 'Section height mobile', + defaultValue: 350, configs: { - options: [ - { label: 'Eager', value: 'eager', icon: 'Lightning' }, - { - label: 'Lazy', - value: 'lazy', - icon: 'SpinnerGap', - weight: 'light', - }, - ], + min: 300, + max: 600, + step: 10, + unit: 'px', }, - helpText: - 'Learn more about image loading strategies.', }, ], }, ], - childTypes: ['heading--item', 'subheading--item', 'description-text--item'], + childTypes: ['subheading--item', 'heading--item', 'description-text--item', 'button-image--item'], presets: { children: [ { - type: 'heading--item', + type: 'subheading--item', }, { - type: 'subheading--item', + type: 'heading--item', }, { type: 'description-text--item', - } + }, + { + type: 'button-image--item', + }, ], }, }; diff --git a/app/sections/header-image/heading-item.tsx b/app/sections/header-image/heading-item.tsx index 095893f7..a9d893d4 100644 --- a/app/sections/header-image/heading-item.tsx +++ b/app/sections/header-image/heading-item.tsx @@ -3,22 +3,18 @@ import type { HydrogenComponentSchema, } from '@weaverse/hydrogen'; import { forwardRef } from 'react'; -import { CSSProperties } from 'react'; interface HeadingItemProps extends HydrogenComponentProps { heading: string; - headingSize: string; + headingColor: string; } let HeadingItem = forwardRef((props, ref) => { - let {heading, headingSize, ...rest } = props; - let headingStyle: CSSProperties = { - '--font-size-display': `${headingSize}`, - } as CSSProperties; + let { heading, headingColor, ...rest } = props; return ( -
-

{heading}

+
+

{heading}

); }); @@ -40,19 +36,10 @@ export let schema: HydrogenComponentSchema = { placeholder: 'Heading for image section', }, { - type: 'toggle-group', - label: 'Heading size', - name: 'headingSize', - configs: { - options: [ - { label: 'XS', value: '22px' }, - { label: 'S', value: '24px' }, - { label: 'M', value: '26px' }, - { label: 'L', value: '28px' }, - { label: 'XL', value: '30px' }, - ], - }, - defaultValue: '24px', + type: 'color', + name: 'headingColor', + label: 'Heading color', + defaultValue: '#333333', }, ], }, diff --git a/app/sections/header-image/subheading-item.tsx b/app/sections/header-image/subheading-item.tsx index 3916d8aa..53ca0126 100644 --- a/app/sections/header-image/subheading-item.tsx +++ b/app/sections/header-image/subheading-item.tsx @@ -9,16 +9,18 @@ import { CSSProperties } from 'react'; interface SubHeadingItemProps extends HydrogenComponentProps { subHeading: string; subHeadingSize: string; + subHeadingColor: string; } let SubHeadingItem = forwardRef((props, ref) => { - let { subHeading, subHeadingSize, ...rest } = props; + let { subHeading, subHeadingSize, subHeadingColor, ...rest } = props; let headingStyle: CSSProperties = { - '--font-size-heading': `${subHeadingSize}`, + fontSize: subHeadingSize, + color: subHeadingColor, } as CSSProperties; return ( -
-

{subHeading}

+
+

{subHeading}

); }); @@ -54,6 +56,12 @@ export let schema: HydrogenComponentSchema = { }, defaultValue: '16px', }, + { + type: 'color', + name: 'subHeadingColor', + label: 'Subheading color', + defaultValue: '#333333', + }, ], }, ], diff --git a/app/weaverse/components.ts b/app/weaverse/components.ts index 75e76d06..710e20d6 100644 --- a/app/weaverse/components.ts +++ b/app/weaverse/components.ts @@ -23,6 +23,7 @@ import * as HeaderImage from '~/sections/header-image/header-image'; import * as HeadingItem from '~/sections/header-image/heading-item'; import * as SubHeadingItem from '~/sections/header-image/subheading-item'; import * as DescriptionTextItem from '~/sections/header-image/description-text-item'; +import * as ButtonItem from '~/sections/header-image/button-image-item'; import * as ImageWithText from '~/sections/image-with-text/image-with-text'; import * as ImageWTextHeadingItem from '~/sections/image-with-text/content-item/heading-item'; import * as ImageWTextDescriptionItem from '~/sections/image-with-text/content-item/description-item'; @@ -53,6 +54,7 @@ export let components: HydrogenComponent[] = [ HeadingItem, SubHeadingItem, DescriptionTextItem, + ButtonItem, ImageWithText, ContentComponent, ImageWTextHeadingItem, From c8489f96325b5b7cd1f9baaf7441885a1843e560 Mon Sep 17 00:00:00 2001 From: dangthang1903 Date: Thu, 2 Nov 2023 22:34:31 +0700 Subject: [PATCH 06/26] chore: update setting for image with text section --- .../content-item/button-item.tsx | 26 +++++- .../content-item/description-item.tsx | 32 ++++++- .../content-item/heading-item.tsx | 29 ++----- .../{index.tsx => subheading-item.tsx} | 43 ++++------ .../image-with-text/image-with-text.tsx | 83 +++++++++++++++++-- .../image-with-text/images-item/images.tsx | 55 ------------ .../image-with-text/images-item/item.tsx | 65 --------------- app/weaverse/components.ts | 8 +- 8 files changed, 156 insertions(+), 185 deletions(-) rename app/sections/image-with-text/content-item/{index.tsx => subheading-item.tsx} (55%) delete mode 100644 app/sections/image-with-text/images-item/images.tsx delete mode 100644 app/sections/image-with-text/images-item/item.tsx diff --git a/app/sections/image-with-text/content-item/button-item.tsx b/app/sections/image-with-text/content-item/button-item.tsx index 1c1a0095..e12c6cf0 100644 --- a/app/sections/image-with-text/content-item/button-item.tsx +++ b/app/sections/image-with-text/content-item/button-item.tsx @@ -3,18 +3,21 @@ import type { HydrogenComponentSchema, } from '@weaverse/hydrogen'; import { forwardRef } from 'react'; +import clsx from 'clsx'; interface ButtonItemProps extends HydrogenComponentProps { buttonLabel: string; buttonLink: string; + openInNewTab: boolean; + buttonStyle: string; } let ImageWTextButtonItem = forwardRef((props, ref) => { - let { buttonLabel, buttonLink, ...rest } = props; + let { buttonLabel, buttonLink, openInNewTab, buttonStyle, ...rest } = props; return ( ); }); @@ -41,6 +44,25 @@ export let schema: HydrogenComponentSchema = { label: 'Button link', placeholder: 'https://' }, + { + type: 'switch', + name: 'openInNewTab', + label: 'Open in new tab', + defaultValue: true, + }, + { + type: 'toggle-group', + label: 'Button style', + name: 'buttonStyle', + configs: { + options: [ + { label: '1', value: 'transition hover:bg-white border-2 border-solid hover:border-gray-900 hover:text-black bg-black text-white' }, + { label: '2', value: 'transition bg-white border-2 border-solid border-gray-900 text-black hover:bg-black hover:text-white' }, + { label: '3', value: 'transition hover:bg-white border-2 border-solid border-white hover:text-black bg-gray-200 text-white' }, + ], + }, + defaultValue: 'transition bg-white border-2 border-solid border-gray-900 text-black hover:bg-black hover:text-white', + }, ], } ], diff --git a/app/sections/image-with-text/content-item/description-item.tsx b/app/sections/image-with-text/content-item/description-item.tsx index 1137a1c3..09cff17e 100644 --- a/app/sections/image-with-text/content-item/description-item.tsx +++ b/app/sections/image-with-text/content-item/description-item.tsx @@ -3,17 +3,24 @@ import type { HydrogenComponentSchema, } from '@weaverse/hydrogen'; import { forwardRef } from 'react'; +import { CSSProperties } from 'react'; interface DescriptionItemProps extends HydrogenComponentProps { descriptionText: string; + descriptionSize: string; + descriptionColor: string; } let ImageWTextDescriptionItem = forwardRef((props, ref) => { - let { descriptionText, ...rest } = props; + let { descriptionText, descriptionSize, descriptionColor, ...rest } = props; + let styleDescription: CSSProperties = { + fontSize: descriptionSize, + color: descriptionColor, + } as CSSProperties; return (
-

{descriptionText}

+

{descriptionText}

); }); @@ -34,6 +41,27 @@ export let schema: HydrogenComponentSchema = { name: 'descriptionText', defaultValue: 'Pair large text with an image to tell a story, explain a detail about your product, or describe a new promotion.', }, + { + type: 'toggle-group', + label: 'Text size', + name: 'descriptionSize', + configs: { + options: [ + { label: 'XS', value: '14px' }, + { label: 'S', value: '16px' }, + { label: 'M', value: '18px' }, + { label: 'L', value: '20px' }, + { label: 'XL', value: '22px' }, + ], + }, + defaultValue: '16px', + }, + { + type: 'color', + name: 'descriptionColor', + label: 'Description color', + defaultValue: '#333333', + }, ], } ], diff --git a/app/sections/image-with-text/content-item/heading-item.tsx b/app/sections/image-with-text/content-item/heading-item.tsx index 14dc971f..a3954211 100644 --- a/app/sections/image-with-text/content-item/heading-item.tsx +++ b/app/sections/image-with-text/content-item/heading-item.tsx @@ -3,22 +3,18 @@ import type { HydrogenComponentSchema, } from '@weaverse/hydrogen'; import { forwardRef } from 'react'; -import { CSSProperties } from 'react'; interface HeadingItemProps extends HydrogenComponentProps { heading: string; - headingSize: string; + headingColor: string; } let ImageWTextHeadingItem = forwardRef((props, ref) => { - let { heading, headingSize, ...rest } = props; - let styleSubheading: CSSProperties = { - fontSize: headingSize, - } as CSSProperties; + let { heading, headingColor, ...rest } = props; return ( -
-

{heading}

+
+

{heading}

); }); @@ -41,19 +37,10 @@ export let schema: HydrogenComponentSchema = { placeholder: 'Heading for image section', }, { - type: 'toggle-group', - label: 'Heading size', - name: 'headingSize', - configs: { - options: [ - { label: 'XS', value: '22px' }, - { label: 'S', value: '24px' }, - { label: 'M', value: '26px' }, - { label: 'L', value: '28px' }, - { label: 'XL', value: '30px' }, - ], - }, - defaultValue: '24px', + type: 'color', + name: 'headingColor', + label: 'Heading color', + defaultValue: '#333333', }, ], } diff --git a/app/sections/image-with-text/content-item/index.tsx b/app/sections/image-with-text/content-item/subheading-item.tsx similarity index 55% rename from app/sections/image-with-text/content-item/index.tsx rename to app/sections/image-with-text/content-item/subheading-item.tsx index 949898b2..ae7ea148 100644 --- a/app/sections/image-with-text/content-item/index.tsx +++ b/app/sections/image-with-text/content-item/subheading-item.tsx @@ -5,33 +5,34 @@ import type { import { forwardRef } from 'react'; import { CSSProperties } from 'react'; -interface ContentsProps extends HydrogenComponentProps { +interface SubheadingProps extends HydrogenComponentProps { subHeading: string; subHeadingSize: string; + subHeadingColor: string; } -let ContentComponent = forwardRef((props, ref) => { - let { subHeading, subHeadingSize, children, ...rest } = props; +let ImageWTextSubheadingItem = forwardRef((props, ref) => { + let { subHeading, subHeadingSize, subHeadingColor, ...rest } = props; let styleContent: CSSProperties = { - '--font-size-heading': `${subHeadingSize}`, + fontSize: subHeadingSize, + color: subHeadingColor, } as CSSProperties; return ( -
- {subHeading &&

{subHeading}

} - {children} +
+ {subHeading &&

{subHeading}

}
); }); -export default ContentComponent; +export default ImageWTextSubheadingItem; export let schema: HydrogenComponentSchema = { - type: 'Content--Item', - title: 'Content', + type: 'subheading-image--Item', + title: 'Subheading', limit: 1, inspector: [ { - group: 'Content', + group: 'Subheading', inputs: [ { type: 'text', @@ -55,21 +56,13 @@ export let schema: HydrogenComponentSchema = { }, defaultValue: '16px', }, + { + type: 'color', + name: 'subHeadingColor', + label: 'Subheading color', + defaultValue: '#333333', + }, ], } ], - childTypes: ['Heading--Item', 'Description--Item', 'Button--Item'], - presets: { - children: [ - { - type: 'Heading--Item', - }, - { - type: 'Description--Item', - }, - { - type: 'Button--Item', - }, - ], - }, }; diff --git a/app/sections/image-with-text/image-with-text.tsx b/app/sections/image-with-text/image-with-text.tsx index 992b7518..24e19fdb 100644 --- a/app/sections/image-with-text/image-with-text.tsx +++ b/app/sections/image-with-text/image-with-text.tsx @@ -1,26 +1,43 @@ import type { HydrogenComponentProps, HydrogenComponentSchema, + WeaverseImage, } from '@weaverse/hydrogen'; import { forwardRef } from 'react'; import { CSSProperties } from 'react'; +import { Image } from '@shopify/hydrogen'; +import { IconImageBlank } from '~/components'; interface ImageWithTextProps extends HydrogenComponentProps { + image: WeaverseImage, textAlignment: string; + sectionHeight: number; + backgroundColor: string; + loading: HTMLImageElement['loading']; } let ImageWithText = forwardRef((props, ref) => { - let { textAlignment, children, ...rest } = props; + let { textAlignment, image, sectionHeight, backgroundColor, loading, children, ...rest } = props; let styleSection: CSSProperties = { - '--section-height': '410px', + '--section-height': `${sectionHeight}px`, + backgroundColor: backgroundColor, textAlign: `${textAlignment}`, } as CSSProperties; return ( -
-
-
- {children} +
+
+
+
+ {children} +
+
+ {image ? : +
+ +
+ } +
@@ -37,6 +54,11 @@ export let schema: HydrogenComponentSchema = { { group: 'Image', inputs: [ + { + type: 'image', + name: 'image', + label: 'Image', + }, { type: 'toggle-group', label: 'Text alignment', @@ -50,17 +72,60 @@ export let schema: HydrogenComponentSchema = { }, defaultValue: 'left', }, + { + type: 'range', + name: 'sectionHeight', + label: 'Section height', + defaultValue: 450, + configs: { + min: 400, + max: 700, + step: 10, + unit: 'px', + }, + }, + { + type: 'color', + name: 'backgroundColor', + label: 'Background color', + defaultValue: '#f4f4f4', + }, + { + type: 'toggle-group', + name: 'loading', + label: 'Image loading', + defaultValue: 'eager', + configs: { + options: [ + { label: 'Eager', value: 'eager', icon: 'Lightning' }, + { + label: 'Lazy', + value: 'lazy', + icon: 'SpinnerGap', + weight: 'light', + }, + ], + }, + helpText: + 'Learn more about image loading strategies.', + }, ], }, ], - childTypes: ['Content--Item','Image--Component'], + childTypes: ['subheading-image--Item', 'Heading--Item', 'Description--Item', 'Button--Item'], presets: { children: [ { - type: 'Content--Item', + type: 'subheading-image--Item', + }, + { + type: 'Heading--Item', + }, + { + type: 'Description--Item', }, { - type: 'Image--Component', + type: 'Button--Item', }, ], }, diff --git a/app/sections/image-with-text/images-item/images.tsx b/app/sections/image-with-text/images-item/images.tsx deleted file mode 100644 index 080c5523..00000000 --- a/app/sections/image-with-text/images-item/images.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import type { - HydrogenComponentProps, - HydrogenComponentSchema, -} from '@weaverse/hydrogen'; -import { forwardRef } from 'react'; - -interface ImagesProps extends HydrogenComponentProps { - -} - -let ImageComponent = forwardRef((props, ref) => { - let {children, ...rest } = props; - return ( -
-
- {children?.map((child, index) => { - if (index === 1) { - return ( - child - ); - } - })} -
- {children?.map((child, index) => { - if (index === 0) { - return ( - child - ); - } - })} -
- ); -}); - -export default ImageComponent; - -export let schema: HydrogenComponentSchema = { - type: 'Image--Component', - title: 'Images', - limit: 1, - inspector: [ - { - group: 'Image', - inputs: [], - }, - ], - childTypes: ['Image--Item'], - presets: { - children: [ - { - type: 'Image--Item', - }, - ], - }, -}; diff --git a/app/sections/image-with-text/images-item/item.tsx b/app/sections/image-with-text/images-item/item.tsx deleted file mode 100644 index cdc14c4f..00000000 --- a/app/sections/image-with-text/images-item/item.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import type { - HydrogenComponentProps, - HydrogenComponentSchema, -} from '@weaverse/hydrogen'; -import { forwardRef } from 'react'; -import { Image } from '@shopify/hydrogen'; - - -interface ImageItemProps extends HydrogenComponentProps { - image: { - url: string; - altText: string; - width?: number; - height?: number; - } - loading: HTMLImageElement['loading']; -} - -let ImageItems = forwardRef((props, ref) => { - let { image, loading, ...rest } = props; - return ( -
- {image && } -
- ); -}); - -export default ImageItems; - -export let schema: HydrogenComponentSchema = { - type: 'Image--Item', - title: 'Image item', - limit: 2, - inspector: [ - { - group: 'Image', - inputs: [ - { - type: 'image', - name: 'image', - label: 'Image', - }, - { - type: 'toggle-group', - name: 'loading', - label: 'Image loading', - defaultValue: 'eager', - configs: { - options: [ - { label: 'Eager', value: 'eager', icon: 'Lightning' }, - { - label: 'Lazy', - value: 'lazy', - icon: 'SpinnerGap', - weight: 'light', - }, - ], - }, - helpText: - 'Learn more about image loading strategies.', - }, - ], - } - ], -}; diff --git a/app/weaverse/components.ts b/app/weaverse/components.ts index 8ad12e53..06a48d6a 100644 --- a/app/weaverse/components.ts +++ b/app/weaverse/components.ts @@ -20,6 +20,7 @@ import * as TestimonialItem from '~/sections/testimonial/item'; import * as Video from '~/sections/video'; import * as CollectionHeader from '~/sections/collection-header'; import * as HeaderImage from '~/sections/header-image/header-image'; +import * as ImageWTextSubheadingItem from '~/sections/image-with-text/content-item/subheading-item'; import * as HeadingItem from '~/sections/header-image/heading-item'; import * as SubHeadingItem from '~/sections/header-image/subheading-item'; import * as DescriptionTextItem from '~/sections/header-image/description-text-item'; @@ -27,9 +28,6 @@ import * as ImageWithText from '~/sections/image-with-text/image-with-text'; import * as ImageWTextHeadingItem from '~/sections/image-with-text/content-item/heading-item'; import * as ImageWTextDescriptionItem from '~/sections/image-with-text/content-item/description-item'; import * as ImageWTextButtonItem from '~/sections/image-with-text/content-item/button-item'; -import * as ImageComponent from '~/sections/image-with-text/images-item/images'; -import * as ImageItems from '~/sections/image-with-text/images-item/item'; -import * as ContentComponent from '~/sections/image-with-text/content-item/index'; import * as ContentColumnWithImage from '~/sections/column-with-text/index'; import * as ContentColumnItem from '~/sections/column-with-text/item'; import * as RichText from '~/sections/rich-text/index'; @@ -48,12 +46,10 @@ export let components: HydrogenComponent[] = [ SubHeadingItem, DescriptionTextItem, ImageWithText, - ContentComponent, + ImageWTextSubheadingItem, ImageWTextHeadingItem, ImageWTextDescriptionItem, ImageWTextButtonItem, - ImageComponent, - ImageItems, ContentColumnWithImage, ContentColumnItem, RichText, From a07eb6182061518d7a2c513f46c3bee65cb91a72 Mon Sep 17 00:00:00 2001 From: dangthang1903 Date: Fri, 3 Nov 2023 14:42:47 +0700 Subject: [PATCH 07/26] refactor: update section --- app/sections/image-with-text/content-item/subheading-item.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/sections/image-with-text/content-item/subheading-item.tsx b/app/sections/image-with-text/content-item/subheading-item.tsx index ae7ea148..a93ce8bf 100644 --- a/app/sections/image-with-text/content-item/subheading-item.tsx +++ b/app/sections/image-with-text/content-item/subheading-item.tsx @@ -28,7 +28,7 @@ export default ImageWTextSubheadingItem; export let schema: HydrogenComponentSchema = { type: 'subheading-image--Item', - title: 'Subheading', + title: 'Subheading item', limit: 1, inspector: [ { From 54481029edc963d1516077dc31c4a4135a19c6f8 Mon Sep 17 00:00:00 2001 From: dangthang1903 Date: Fri, 3 Nov 2023 17:24:46 +0700 Subject: [PATCH 08/26] chore: update video with text section --- app/components/Icon.tsx | 8 ++++++++ .../video-with-text/video-button-item.tsx | 14 +++++++------ .../video-with-text/video-heading-item.tsx | 20 ++----------------- .../video-with-text/video-with-text.tsx | 10 ++++------ 4 files changed, 22 insertions(+), 30 deletions(-) diff --git a/app/components/Icon.tsx b/app/components/Icon.tsx index 96a4bf1d..8957bd9d 100644 --- a/app/components/Icon.tsx +++ b/app/components/Icon.tsx @@ -320,3 +320,11 @@ export function IconMapBlank(props: IconProps) { ); } +export function IconVideoBlank(props: IconProps) { + return( + + + + + ); +} diff --git a/app/sections/video-with-text/video-button-item.tsx b/app/sections/video-with-text/video-button-item.tsx index 6f963cc2..ed8b98a2 100644 --- a/app/sections/video-with-text/video-button-item.tsx +++ b/app/sections/video-with-text/video-button-item.tsx @@ -3,18 +3,20 @@ import type { HydrogenComponentSchema, } from '@weaverse/hydrogen'; import { forwardRef } from 'react'; +import clsx from 'clsx'; interface VideoButtonProps extends HydrogenComponentProps { buttonLabel: string; buttonLink: string; enableNewtab: boolean; + buttonStyle: string; } let VideoButtonItem = forwardRef((props, ref) => { - let {buttonLabel, buttonLink, enableNewtab, ...rest} = props; + let {buttonLabel, buttonLink, enableNewtab, buttonStyle, ...rest} = props; return ( ); }); @@ -53,12 +55,12 @@ export let schema: HydrogenComponentSchema = { name: 'buttonStyle', configs: { options: [ - { label: '1', value: '1' }, - { label: '2', value: '2' }, - { label: '3', value: '3' }, + { label: '1', value: 'transition hover:bg-white border-2 border-solid hover:border-gray-900 hover:text-black bg-black text-white' }, + { label: '2', value: 'transition bg-white border-2 border-solid border-gray-900 text-black hover:bg-black hover:text-white' }, + { label: '3', value: 'transition hover:bg-white border-2 border-solid border-white hover:text-black bg-gray-200 text-white' }, ], }, - defaultValue: '1', + defaultValue: 'transition bg-white border-2 border-solid border-gray-900 text-black hover:bg-black hover:text-white', }, ], }, diff --git a/app/sections/video-with-text/video-heading-item.tsx b/app/sections/video-with-text/video-heading-item.tsx index 77aabac5..a3b73d11 100644 --- a/app/sections/video-with-text/video-heading-item.tsx +++ b/app/sections/video-with-text/video-heading-item.tsx @@ -6,15 +6,14 @@ import { forwardRef } from 'react'; interface VideoHeadingProps extends HydrogenComponentProps { heading: string; - headingSize: string; headingColor: string; } let VideoHeadingItem = forwardRef((props, ref) => { - let {heading, headingSize, headingColor, ...rest} = props; + let {heading, headingColor, ...rest} = props; return (
-

{heading}

+

{heading}

); }); @@ -36,21 +35,6 @@ export let schema: HydrogenComponentSchema = { defaultValue: 'Heading for Video', placeholder: 'Heading for video section', }, - { - type: 'toggle-group', - label: 'Heading size', - name: 'headingSize', - configs: { - options: [ - { label: 'XS', value: '22px' }, - { label: 'S', value: '24px' }, - { label: 'M', value: '26px' }, - { label: 'L', value: '28px' }, - { label: 'XL', value: '30px' }, - ], - }, - defaultValue: '24px', - }, { type: 'color', name: 'headingColor', diff --git a/app/sections/video-with-text/video-with-text.tsx b/app/sections/video-with-text/video-with-text.tsx index 2e152586..700c93a0 100644 --- a/app/sections/video-with-text/video-with-text.tsx +++ b/app/sections/video-with-text/video-with-text.tsx @@ -5,6 +5,7 @@ import type { import { forwardRef } from 'react'; import { CSSProperties } from 'react'; import ReactPlayer from 'react-player/youtube'; +import { IconVideoBlank } from '~/components'; interface VideoWithTextProps extends HydrogenComponentProps { videoLink: string; @@ -39,10 +40,7 @@ let VideoWithText = forwardRef((props, ref) => className='absolute aspect-video' controls={false} /> :
- - - - +
} {enableOverlay &&
}
@@ -157,10 +155,10 @@ export let schema: HydrogenComponentSchema = { presets: { children: [ { - type: 'video-heading--item', + type: 'video-subheading--item', }, { - type: 'video-subheading--item', + type: 'video-heading--item', }, { type: 'video-description--item', From 7a5083d0dea94f56382c825606b26a008bd8afe6 Mon Sep 17 00:00:00 2001 From: dangthang1903 Date: Mon, 6 Nov 2023 16:36:03 +0700 Subject: [PATCH 09/26] chore: update setting column with image section --- app/sections/column-with-text/index.tsx | 77 ++++++++++++++++++------- app/sections/column-with-text/item.tsx | 70 +++++++++++++--------- 2 files changed, 99 insertions(+), 48 deletions(-) diff --git a/app/sections/column-with-text/index.tsx b/app/sections/column-with-text/index.tsx index 37fea50f..14a3d8b7 100644 --- a/app/sections/column-with-text/index.tsx +++ b/app/sections/column-with-text/index.tsx @@ -7,24 +7,32 @@ import { CSSProperties } from 'react'; interface ContentColumnWithImageProps extends HydrogenComponentProps { heading: string; - headingSize: string; + textColor: string; + gap: number; headingAlignment: string; + topPadding: number; + bottomPadding: number; } let ContentColumnWithImage = forwardRef((props, ref) => { - let {heading, headingSize, headingAlignment, children, ...rest } = props; + let {heading, textColor, headingAlignment, gap, topPadding, bottomPadding, children, ...rest } = props; let headingStyle: CSSProperties = { justifyContent: `${headingAlignment}`, - fontSize: `${headingSize}`, + } as CSSProperties; + let sectionStyle: CSSProperties = { + paddingTop: `${topPadding}px`, + paddingBottom: `${bottomPadding}px`, + '--text-color': `${textColor}`, + '--gap-item': `${gap}px`, } as CSSProperties; return ( -
-
-
-

{heading}

+
+
+
+

{heading}

-
+
{children}
@@ -50,19 +58,10 @@ export let schema: HydrogenComponentSchema = { placeholder: 'Heading for Image section', }, { - type: 'toggle-group', - label: 'Heading size', - name: 'headingSize', - configs: { - options: [ - { label: 'XS', value: '20px' }, - { label: 'S', value: '24px' }, - { label: 'M', value: '30px' }, - { label: 'L', value: '36px' }, - { label: 'XL', value: '40px' }, - ], - }, - defaultValue: '24px', + type: 'color', + name: 'textColor', + label: 'Text color', + defaultValue: '#000000', }, { type: 'toggle-group', @@ -77,6 +76,42 @@ export let schema: HydrogenComponentSchema = { }, defaultValue: 'center', }, + { + type: 'range', + name: 'gap', + label: 'Gap', + defaultValue: 20, + configs: { + min: 10, + max: 50, + step: 1, + unit: 'px', + }, + }, + { + type: 'range', + name: 'topPadding', + label: 'Top padding', + defaultValue: 0, + configs: { + min: 0, + max: 100, + step: 5, + unit: 'px', + }, + }, + { + type: 'range', + name: 'bottomPadding', + label: 'Bottom padding', + defaultValue: 0, + configs: { + min: 0, + max: 100, + step: 5, + unit: 'px', + }, + }, ], }, ], diff --git a/app/sections/column-with-text/item.tsx b/app/sections/column-with-text/item.tsx index 0b19ea84..47b47bfb 100644 --- a/app/sections/column-with-text/item.tsx +++ b/app/sections/column-with-text/item.tsx @@ -1,24 +1,23 @@ import type { HydrogenComponentProps, HydrogenComponentSchema, + WeaverseImage, } from '@weaverse/hydrogen'; -import {forwardRef} from 'react'; -import {Image} from '@shopify/hydrogen'; +import { forwardRef } from 'react'; +import { Image } from '@shopify/hydrogen'; import clsx from 'clsx'; -import {CSSProperties} from 'react'; +import { CSSProperties } from 'react'; +import { IconImageBlank } from '~/components'; interface ContentColumnItemProps extends HydrogenComponentProps { - imageSrc: { - url: string; - altText: string; - width?: number; - height?: number; - }; + imageSrc: WeaverseImage; titleText: string; contentAlignment: string; descriptionText: string; buttonLabel: string; buttonLink: string; + openInNewTab: boolean; + buttonStyle: string; hideOnMobile: boolean; } @@ -31,6 +30,8 @@ let ContentColumnItem = forwardRef( descriptionText, buttonLabel, buttonLink, + openInNewTab, + buttonStyle, hideOnMobile, ...rest } = props; @@ -42,27 +43,30 @@ let ContentColumnItem = forwardRef( ref={ref} {...rest} className={clsx( - 'flex flex-col items-center pt-8 sm-max:w-full sm-max:pt-0', + 'flex flex-col items-center sm-max:w-full sm-max:pt-0', hideOnMobile && 'hidden sm:block', )} >
- + {imageSrc ? : +
+ +
}
{titleText && ( -

{titleText}

+

{titleText}

)} {descriptionText && (

+ className="text-sm font-normal mt-2 text-[var(--text-color)]" + >{descriptionText}

)} {buttonLabel && ( {buttonLabel} @@ -86,13 +90,6 @@ export let schema: HydrogenComponentSchema = { type: 'image', name: 'imageSrc', label: 'Image', - defaultValue: { - id: 'image-placeholder', - url: 'https://cdn.shopify.com/s/files/1/0728/0410/6547/files/pilot-image-placeholder.svg', - altText: 'Image index', - width: 0, - height: 0, - }, }, { type: 'text', @@ -107,15 +104,15 @@ export let schema: HydrogenComponentSchema = { name: 'contentAlignment', configs: { options: [ - {label: 'Left', value: 'left'}, - {label: 'Center', value: 'center'}, - {label: 'Right', value: 'right'}, + { label: 'Left', value: 'left' }, + { label: 'Center', value: 'center' }, + { label: 'Right', value: 'right' }, ], }, defaultValue: 'center', }, { - type: 'richtext', + type: 'textarea', label: 'Text', name: 'descriptionText', placeholder: 'Brief description', @@ -134,6 +131,25 @@ export let schema: HydrogenComponentSchema = { name: 'buttonLink', placeholder: 'Button link', }, + { + type: 'switch', + name: 'openInNewTab', + label: 'Open in new tab', + defaultValue: true, + }, + { + type: 'toggle-group', + label: 'Button style', + name: 'buttonStyle', + configs: { + options: [ + { label: '1', value: 'transition hover:bg-white border-2 border-solid hover:border-gray-900 hover:text-black bg-black text-white' }, + { label: '2', value: 'transition bg-white border-2 border-solid border-gray-900 text-black hover:bg-black hover:text-white' }, + { label: '3', value: 'transition hover:bg-white border-2 border-solid border-white hover:text-black bg-gray-200 text-white' }, + ], + }, + defaultValue: 'transition hover:bg-white border-2 border-solid hover:border-gray-900 hover:text-black bg-black text-white', + }, { type: 'switch', label: 'Hide on Mobile', From 2b58f17b128a4842998da92b638bfaa798f05026 Mon Sep 17 00:00:00 2001 From: dangthang1903 Date: Tue, 7 Nov 2023 16:04:21 +0700 Subject: [PATCH 10/26] chore: update seting for richtext map section --- app/sections/rich-text/buttons-item.tsx | 30 +++++++++++++--- app/sections/rich-text/descriptions-item.tsx | 2 +- app/sections/rich-text/headings-item.tsx | 2 +- app/sections/rich-text/index.tsx | 38 +++++++++++++++++++- app/weaverse/create-weaverse.server.ts | 1 + 5 files changed, 66 insertions(+), 7 deletions(-) diff --git a/app/sections/rich-text/buttons-item.tsx b/app/sections/rich-text/buttons-item.tsx index 76017285..0810d341 100644 --- a/app/sections/rich-text/buttons-item.tsx +++ b/app/sections/rich-text/buttons-item.tsx @@ -3,17 +3,20 @@ import type { HydrogenComponentSchema, } from '@weaverse/hydrogen'; import { forwardRef } from 'react'; +import clsx from 'clsx'; interface ButtonItemProps extends HydrogenComponentProps { buttonLabel: string; buttonLink: string; + openInNewTab: boolean; + buttonStyle: string; } let RichTextButtonItem = forwardRef((props, ref) => { - let { buttonLabel, buttonLink, ...rest } = props; + let { buttonLabel, buttonLink, openInNewTab, buttonStyle, ...rest } = props; return ( -
- {buttonLabel} + ); }); @@ -39,7 +42,26 @@ export let schema: HydrogenComponentSchema = { label: 'Button link', name: 'buttonLink', placeholder: 'Button link', - } + }, + { + type: 'switch', + name: 'openInNewTab', + label: 'Open in new tab', + defaultValue: true, + }, + { + type: 'toggle-group', + label: 'Button style', + name: 'buttonStyle', + configs: { + options: [ + { label: '1', value: 'transition hover:bg-white border-2 border-solid hover:border-gray-900 hover:text-black bg-black text-white' }, + { label: '2', value: 'transition bg-white border-2 border-solid border-gray-900 text-black hover:bg-black hover:text-white' }, + { label: '3', value: 'transition hover:bg-white border-2 border-solid border-white hover:text-black bg-gray-200 text-white' }, + ], + }, + defaultValue: 'transition bg-white border-2 border-solid border-gray-900 text-black hover:bg-black hover:text-white', + }, ], }, ], diff --git a/app/sections/rich-text/descriptions-item.tsx b/app/sections/rich-text/descriptions-item.tsx index 4337de1d..5bb63824 100644 --- a/app/sections/rich-text/descriptions-item.tsx +++ b/app/sections/rich-text/descriptions-item.tsx @@ -11,7 +11,7 @@ interface DescriptionItemProps extends HydrogenComponentProps { let RichTextDescriptionItem = forwardRef((props, ref) => { let { descriptionText, ...rest } = props; return ( -

+

); }); diff --git a/app/sections/rich-text/headings-item.tsx b/app/sections/rich-text/headings-item.tsx index 269922eb..4888e1d3 100644 --- a/app/sections/rich-text/headings-item.tsx +++ b/app/sections/rich-text/headings-item.tsx @@ -11,7 +11,7 @@ interface HeadingItemProps extends HydrogenComponentProps { let RichTextHeadingItem = forwardRef((props, ref) => { let { heading, ...rest } = props; return ( -

{heading}

+

{heading}

); }); diff --git a/app/sections/rich-text/index.tsx b/app/sections/rich-text/index.tsx index 7106cb34..0fdb359c 100644 --- a/app/sections/rich-text/index.tsx +++ b/app/sections/rich-text/index.tsx @@ -9,14 +9,20 @@ interface RichTextProps extends HydrogenComponentProps { contentAlignment: string; sectionHeight: string; backgroundColor: string; + textColor: string; + topPadding: string; + bottomPadding: string; } let RichText = forwardRef((props, ref) => { - let { contentAlignment, sectionHeight, backgroundColor, children, ...rest } = props; + let { contentAlignment, sectionHeight, backgroundColor, textColor, topPadding, bottomPadding, children, ...rest } = props; let sectionStyle: CSSProperties = { alignItems: `${contentAlignment}`, '--section-height': `${sectionHeight}px`, backgroundColor: `${backgroundColor}`, + '--text-color': `${textColor}`, + paddingTop: `${topPadding}px`, + paddingBottom: `${bottomPadding}px`, } as CSSProperties; return (
@@ -43,6 +49,12 @@ export let schema: HydrogenComponentSchema = { label: 'Background color', defaultValue: '#F7F7F7', }, + { + type: 'color', + name: 'textColor', + label: 'Text color', + defaultValue: '#000000', + }, { type: 'toggle-group', label: 'Content alignment', @@ -68,6 +80,30 @@ export let schema: HydrogenComponentSchema = { unit: 'px', }, }, + { + type: 'range', + name: 'topPadding', + label: 'Top padding', + defaultValue: 40, + configs: { + min: 10, + max: 100, + step: 10, + unit: 'px', + }, + }, + { + type: 'range', + name: 'bottomPadding', + label: 'Bottom padding', + defaultValue: 40, + configs: { + min: 10, + max: 100, + step: 10, + unit: 'px', + }, + }, ], }, ], diff --git a/app/weaverse/create-weaverse.server.ts b/app/weaverse/create-weaverse.server.ts index d9714c56..24b658a7 100644 --- a/app/weaverse/create-weaverse.server.ts +++ b/app/weaverse/create-weaverse.server.ts @@ -37,6 +37,7 @@ export function getWeaverseCsp(request: Request) { 'https://shopify.com', 'https://*.youtube.com', 'https://fonts.gstatic.com', + 'https://*.google.com', ...weaverseHosts, ], imgSrc: [ From 4d698bc51a58636547465486fc7fed91e256b291 Mon Sep 17 00:00:00 2001 From: Paul Date: Thu, 9 Nov 2023 15:50:38 +0700 Subject: [PATCH 11/26] feat: add support for get parent, children items --- app/root.tsx | 1 + app/sections/testimonial/index.tsx | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/app/root.tsx b/app/root.tsx index a541cbb8..e05f8b79 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -3,6 +3,7 @@ import { type LoaderFunctionArgs, type AppLoadContext, type SerializeFrom, + LinksFunction, } from '@shopify/remix-oxygen'; import { isRouteErrorResponse, diff --git a/app/sections/testimonial/index.tsx b/app/sections/testimonial/index.tsx index c0e1021e..7a22471c 100644 --- a/app/sections/testimonial/index.tsx +++ b/app/sections/testimonial/index.tsx @@ -1,6 +1,9 @@ import { type HydrogenComponentProps, type HydrogenComponentSchema, + useChildInstances, + useItemInstance, + useParentInstance, } from '@weaverse/hydrogen'; import {forwardRef} from 'react'; @@ -11,6 +14,10 @@ interface TestimonialProps extends HydrogenComponentProps { let Testimonial = forwardRef((props, ref) => { let {heading, description, children, ...rest} = props; + + let itemInstance = useItemInstance(); + let parentInstance = useParentInstance(); + let childInstances = useChildInstances(); return (
From 1bd48cd4baa1ee168889a8050ebf148995049850 Mon Sep 17 00:00:00 2001 From: Paul Date: Thu, 9 Nov 2023 15:56:48 +0700 Subject: [PATCH 12/26] chore: fix typos --- app/weaverse/schema.server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/weaverse/schema.server.ts b/app/weaverse/schema.server.ts index c8d6d6af..49a61ead 100644 --- a/app/weaverse/schema.server.ts +++ b/app/weaverse/schema.server.ts @@ -21,7 +21,7 @@ export let themeSchema: HydrogenThemeSchema = { label: 'Logo', defaultValue: { id: 'gid://shopify/MediaImage/34144817938616', - alt: '', + altText: 'Logo', url: 'https://cdn.shopify.com/s/files/1/0623/5095/0584/files/Pilot_logo_b04f1938-06e5-414d-8a47-d5fcca424000.png?v=1697101908', width: 320, height: 116, From fd8db1917f09fa7f124a1665f970bc88f22ca7c8 Mon Sep 17 00:00:00 2001 From: Paul Date: Thu, 9 Nov 2023 18:07:02 +0700 Subject: [PATCH 13/26] upgrade: Weaverse SDKs v2.5.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 04fbe715..6d97dc52 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "@shopify/cli-hydrogen": "^6.0.2", "@shopify/hydrogen": "^2023.10.2", "@shopify/remix-oxygen": "^2.0.1", - "@weaverse/hydrogen": "^2.4.3", + "@weaverse/hydrogen": "2.5.0", "clsx": "2.0.0", "cross-env": "7.0.3", "graphql": "16.8.1", From f6acb55c80651ba193e6946a88256ac6e675a0e4 Mon Sep 17 00:00:00 2001 From: ken Date: Fri, 10 Nov 2023 10:14:57 +0700 Subject: [PATCH 14/26] feat: add judgeme review --- .../($locale).api.review.$productHandle.tsx | 39 +++++++++ app/sections/judgeme-review.tsx | 83 +++++++++++++++++++ app/sections/single-product.tsx | 6 +- app/weaverse/components.ts | 3 +- package.json | 1 + 5 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 app/routes/($locale).api.review.$productHandle.tsx create mode 100644 app/sections/judgeme-review.tsx diff --git a/app/routes/($locale).api.review.$productHandle.tsx b/app/routes/($locale).api.review.$productHandle.tsx new file mode 100644 index 00000000..304031fc --- /dev/null +++ b/app/routes/($locale).api.review.$productHandle.tsx @@ -0,0 +1,39 @@ +import {type RouteLoaderArgs} from '@weaverse/hydrogen'; +import invariant from 'tiny-invariant'; +import {json} from '@shopify/remix-oxygen'; + +async function getInternalIdByHandle(api_token: string,shop_domain: string, handle: string) { + let api = `https://judge.me/api/v1/products/-1?` + new URLSearchParams({ + api_token, + shop_domain, + handle: handle + }) + let data = await fetch(api).then(res => res.json()) + return data?.product?.id + } + +export async function loader(args: RouteLoaderArgs) { + let { params, context} = args; + let env = context.env + let handle = params.productHandle + invariant(handle, 'Missing product handle'); + let api_token = env.JUDGEME_PUBLIC_TOKEN + let shop_domain = env.PUBLIC_STORE_DOMAIN + + let internalId = await getInternalIdByHandle(api_token, shop_domain, handle) + if (internalId) { + let data = await fetch(`https://judge.me/api/v1/reviews?`+ new URLSearchParams({ + api_token, + shop_domain, + product_id: internalId + })).then(res => res.json()) + let reviews = data.reviews + let rating = reviews.reduce((acc, review) => acc + review.rating, 0) / reviews.length + return { + rating + }; + } + return { + rating: 0 + } +} diff --git a/app/sections/judgeme-review.tsx b/app/sections/judgeme-review.tsx new file mode 100644 index 00000000..a3574c23 --- /dev/null +++ b/app/sections/judgeme-review.tsx @@ -0,0 +1,83 @@ +import type { + HydrogenComponentProps, + HydrogenComponentSchema, +} from '@weaverse/hydrogen'; +import StarsRating from "react-star-rate"; +import {useParentInstance} from '@weaverse/hydrogen'; +import {useFetcher} from '@remix-run/react'; +import { useEffect } from 'react'; +import { usePrefixPathWithLocale } from '~/lib/utils'; + +let JudgemeReview = (props: HydrogenComponentProps) => { + const {load, data} = useFetcher(); + let context = useParentInstance() + + let rating = Math.round((data?.rating || 0) * 100) / 100 + let handle = context.data?.product?.handle + const api = handle && usePrefixPathWithLocale( + `/api/review/${handle}` + ); + useEffect( () => { + if (api) { + load(api) + } + }, [load, api]) + if (!data?.rating) return null + return
+ ({rating}) +
; +}; +export default JudgemeReview; + + + +// export let loader = async (args) => { +// let {env} = args.weaverse +// let api_token = env.JUDGEME_PUBLIC_TOKEN +// let shop_domain = env.PUBLIC_STORE_DOMAIN +// console.log("🚀 ~ api_token:", env) +// async function getInternalIdByHandle(handle: string) { +// let api = `https://judge.me/api/v1/products/-1?` + new URLSearchParams({ +// api_token, +// shop_domain, +// handle: handle +// }) +// let data = await fetch(api).then(res => res.json()) +// return data?.product?.id +// } +// let handle = 'adidas-classic-backpack' +// let internalId = await getInternalIdByHandle(handle) +// if (internalId) { +// let data = await fetch(`https://judge.me/api/v1/reviews?`+ new URLSearchParams({ +// api_token, +// shop_domain, +// product_id: internalId +// })).then(res => res.json()) +// let reviews = data.reviews +// let rating = reviews.reduce((acc, review) => acc + review.rating, 0) / reviews.length +// // console.log("🚀 ~ data:", rating) +// return { +// rating +// }; +// } +// return { +// rating: 0 +// } +// } + + +export let schema: HydrogenComponentSchema = { + type: 'judgeme-review', + title: 'Judgeme review', + toolbar: ['general-settings', ['duplicate', 'delete']], + inspector: [ + { + group: 'Judgeme', + inputs: [], + }, + ], +}; diff --git a/app/sections/single-product.tsx b/app/sections/single-product.tsx index fec05af4..b2838d70 100644 --- a/app/sections/single-product.tsx +++ b/app/sections/single-product.tsx @@ -23,7 +23,7 @@ type SingleProductProps = HydrogenComponentProps< let SingleProduct = forwardRef( (props, ref) => { - let {loaderData, product, ...rest} = props; + let {loaderData, product, children, ...rest} = props; let productTitle = loaderData?.product?.title; return (
@@ -44,6 +44,8 @@ let SingleProduct = forwardRef(

$99.99

+ {children} +

Product description goes here. It explains the key features and benefits of the product. @@ -88,6 +90,8 @@ export let loader = async (args: ComponentLoaderArgs) => { export let schema: HydrogenComponentSchema = { type: 'single-product', title: 'Single product', + childTypes: ['judgeme-review',], + limit: 1, inspector: [ { diff --git a/app/weaverse/components.ts b/app/weaverse/components.ts index 0137a77a..77a7c067 100644 --- a/app/weaverse/components.ts +++ b/app/weaverse/components.ts @@ -43,7 +43,7 @@ import * as VideoButtonItem from '~/sections/video-with-text/video-button-item'; import * as Map from '~/sections/map'; import * as PromotionGrid from '~/sections/promotion-grid/promotion-grid'; import * as PromotionGridItem from '~/sections/promotion-grid/item'; - +import * as Judgeme from '~/sections/judgeme-review'; export let components: HydrogenComponent[] = [ Main, Hero, @@ -89,4 +89,5 @@ export let components: HydrogenComponent[] = [ CollectionFilters, CollectionList, SingleProduct, + Judgeme, ]; diff --git a/package.json b/package.json index 04fbe715..7e5f3b89 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "react-dom": "18.2.0", "react-intersection-observer": "9.5.2", "react-player": "^2.13.0", + "react-star-rate": "^0.2.0", "react-use": "17.4.0", "schema-dts": "1.1.2", "tiny-invariant": "1.3.1", From 20bef6da8b2407a800e9f9ddb1226f6abb2c184a Mon Sep 17 00:00:00 2001 From: hta218 Date: Wed, 15 Nov 2023 08:21:14 +0700 Subject: [PATCH 15/26] Refactor Testimonial section's heading and description --- app/sections/testimonial/index.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/sections/testimonial/index.tsx b/app/sections/testimonial/index.tsx index 7a22471c..15a38412 100644 --- a/app/sections/testimonial/index.tsx +++ b/app/sections/testimonial/index.tsx @@ -22,11 +22,9 @@ let Testimonial = forwardRef((props, ref) => {

-

- {heading} -

+

{heading}

{description && ( -

+

{description}

)} From 8fba36f590749ae7dfe186d7e8b48e037407eeef Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 15 Nov 2023 10:27:53 +0700 Subject: [PATCH 16/26] chore: update SDKs v2.6.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6d97dc52..df6ba598 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "@shopify/cli-hydrogen": "^6.0.2", "@shopify/hydrogen": "^2023.10.2", "@shopify/remix-oxygen": "^2.0.1", - "@weaverse/hydrogen": "2.5.0", + "@weaverse/hydrogen": "2.6.0", "clsx": "2.0.0", "cross-env": "7.0.3", "graphql": "16.8.1", From 408e718335bb74d17c47c565006f5df025de6285 Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 15 Nov 2023 11:16:07 +0700 Subject: [PATCH 17/26] chore: use npm registry only --- .npmrc | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.npmrc b/.npmrc index 2050a657..e23cdfd9 100644 --- a/.npmrc +++ b/.npmrc @@ -1,2 +1,3 @@ +@weaverse:registry=https://registry.npmjs.com @shopify:registry=https://registry.npmjs.com progress=false diff --git a/package.json b/package.json index df6ba598..36baa66e 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "@shopify/cli-hydrogen": "^6.0.2", "@shopify/hydrogen": "^2023.10.2", "@shopify/remix-oxygen": "^2.0.1", - "@weaverse/hydrogen": "2.6.0", + "@weaverse/hydrogen": "2.6.1", "clsx": "2.0.0", "cross-env": "7.0.3", "graphql": "16.8.1", From 5c0f906b92861ab2e6b23ae7930e98fc125ec9d6 Mon Sep 17 00:00:00 2001 From: ken Date: Thu, 16 Nov 2023 17:09:58 +0700 Subject: [PATCH 18/26] chore: add judgeme review --- app/sections/judgeme-review.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/sections/judgeme-review.tsx b/app/sections/judgeme-review.tsx index a3574c23..b880a2ff 100644 --- a/app/sections/judgeme-review.tsx +++ b/app/sections/judgeme-review.tsx @@ -71,7 +71,7 @@ export default JudgemeReview; export let schema: HydrogenComponentSchema = { - type: 'judgeme-review', + type: 'judgeme', title: 'Judgeme review', toolbar: ['general-settings', ['duplicate', 'delete']], inspector: [ From 870d2f7cb09253218531e120848f2daae0299034 Mon Sep 17 00:00:00 2001 From: ken Date: Thu, 16 Nov 2023 17:10:12 +0700 Subject: [PATCH 19/26] chore: update single-product --- app/sections/single-product.tsx | 110 --------------- app/sections/single-product/index.tsx | 164 +++++++++++++++++++++++ app/sections/single-product/quantity.tsx | 40 ++++++ app/sections/single-product/variants.tsx | 158 ++++++++++++++++++++++ 4 files changed, 362 insertions(+), 110 deletions(-) delete mode 100644 app/sections/single-product.tsx create mode 100644 app/sections/single-product/index.tsx create mode 100644 app/sections/single-product/quantity.tsx create mode 100644 app/sections/single-product/variants.tsx diff --git a/app/sections/single-product.tsx b/app/sections/single-product.tsx deleted file mode 100644 index b2838d70..00000000 --- a/app/sections/single-product.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import {Link} from '@remix-run/react'; -import { - HydrogenComponentProps, - HydrogenComponentSchema, - ComponentLoaderArgs, - getSelectedProductOptions, - WeaverseProduct, -} from '@weaverse/hydrogen'; -import {forwardRef} from 'react'; -import {ProductInfoQuery} from 'storefrontapi.generated'; -import {PRODUCT_QUERY} from '~/data/queries'; - -type SingleProductData = { - heading: string; - productsCount: number; - product: WeaverseProduct; -}; - -type SingleProductProps = HydrogenComponentProps< - Awaited> -> & - SingleProductData; - -let SingleProduct = forwardRef( - (props, ref) => { - let {loaderData, product, children, ...rest} = props; - let productTitle = loaderData?.product?.title; - return ( -
-
-
- Product Image -
-
-

- {productTitle} -

-

- $99.99 -

- {children} - -

- Product description goes here. It explains the key features - and benefits of the product. -

-
- - Add to Cart - -
-
-
-
- ); - }, -); - -export let loader = async (args: ComponentLoaderArgs) => { - let {weaverse, data} = args; - let {storefront} = weaverse; - let selectedOptions = getSelectedProductOptions(weaverse.request); - - if (data?.product) { - let product = await storefront.query(PRODUCT_QUERY, { - variables: { - handle: data.product.handle, - // Should not get from request since this section could be used everywhere - // TODO: update the query to not require `selectedOptions` or create a new query for this component - selectedOptions, - language: storefront.i18n.language, - country: storefront.i18n.country, - }, - }); - - return product; - } - return null; -}; - -export let schema: HydrogenComponentSchema = { - type: 'single-product', - title: 'Single product', - childTypes: ['judgeme-review',], - - limit: 1, - inspector: [ - { - group: 'Single product', - inputs: [ - { - label: 'Choose product', - type: 'product', - name: 'product', - }, - ], - }, - ], -}; - -export default SingleProduct; diff --git a/app/sections/single-product/index.tsx b/app/sections/single-product/index.tsx new file mode 100644 index 00000000..661f21e3 --- /dev/null +++ b/app/sections/single-product/index.tsx @@ -0,0 +1,164 @@ +import {Link} from '@remix-run/react'; +import { + HydrogenComponentProps, + HydrogenComponentSchema, + ComponentLoaderArgs, + getSelectedProductOptions, + WeaverseProduct, +} from '@weaverse/hydrogen'; +import {forwardRef, useState} from 'react'; +import {ProductQuery} from 'storefrontapi.generated'; +import {PRODUCT_QUERY, VARIANTS_QUERY} from '~/data/queries'; +import {defer, redirect} from '@shopify/remix-oxygen'; +import {ProductForm} from '../product-information/product-form'; +import {ProductVariants} from './variants'; +import {CartForm, Image, Money} from '@shopify/hydrogen'; +import {AddToCartButton, Button, ProductGallery} from '~/components'; +import { Quantity } from './quantity'; + +type SingleProductData = { + heading: string; + productsCount: number; + product: WeaverseProduct; +}; + +type SingleProductProps = HydrogenComponentProps< + Awaited> +> & + SingleProductData; + +let SingleProduct = forwardRef( + (props, ref) => { + let {loaderData, children, ...rest} = props; + let product = loaderData?.data.product.product; + let productTitle = product?.title; + let variants = loaderData?.data?.variants; + let [selectedVariant, setSelectedVariant] = useState(variants?.nodes[0]); + let [quantity, setQuantity] = useState(1); + return ( +
+
+
+ +
+
+

+ {productTitle} +

+

+ +

+ {children} +

+ {product?.descriptionHtml} +

+ +
+ + + {/* + + */} + + Add to Cart + +
+
+
+
+ ); + }, +); + +export let loader = async (args: ComponentLoaderArgs) => { + let {weaverse, data} = args; + let {storefront} = weaverse; + let selectedOptions = getSelectedProductOptions(weaverse.request); + if (!data?.product) { + return null; + } + let productHandle = data.product.handle; + let product = await storefront.query(PRODUCT_QUERY, { + variables: { + handle: productHandle, + // Should not get from request since this section could be used everywhere + // TODO: update the query to not require `selectedOptions` or create a new query for this component + selectedOptions, + language: storefront.i18n.language, + country: storefront.i18n.country, + }, + }); + // In order to show which variants are available in the UI, we need to query + // all of them. But there might be a *lot*, so instead separate the variants + // into it's own separate query that is deferred. So there's a brief moment + // where variant options might show as available when they're not, but after + // this deferred query resolves, the UI will update. + let variants = await storefront.query(VARIANTS_QUERY, { + variables: { + handle: productHandle, + language: storefront.i18n.language, + country: storefront.i18n.country, + }, + }); + return defer({ + product, + variants: variants?.product?.variants, + }); +}; + +export let schema: HydrogenComponentSchema = { + type: 'single-product', + title: 'Single product', + childTypes: ['judgeme'], + + limit: 1, + inspector: [ + { + group: 'Single product', + inputs: [ + { + label: 'Choose product', + type: 'product', + name: 'product', + }, + ], + }, + ], +}; + +export default SingleProduct; diff --git a/app/sections/single-product/quantity.tsx b/app/sections/single-product/quantity.tsx new file mode 100644 index 00000000..d5a76df7 --- /dev/null +++ b/app/sections/single-product/quantity.tsx @@ -0,0 +1,40 @@ +interface QuantityProps { + value: number; + onChange: (value: number) => void; +} +export function Quantity(props: QuantityProps) { + let {value, onChange} = props; + let handleKeyDown = (e: React.KeyboardEvent) => { + // Prevent the user from entering non-numeric characters + if (e.key !== 'Backspace' && e.key !== 'Delete' && e.key !== 'ArrowLeft' && e.key !== 'ArrowRight' && isNaN(Number(e.key))) { + e.preventDefault(); + } + } + return ( +
+ + onChange(Number(e.currentTarget.value))} + /> + +
+ ); +} diff --git a/app/sections/single-product/variants.tsx b/app/sections/single-product/variants.tsx new file mode 100644 index 00000000..e748bbc4 --- /dev/null +++ b/app/sections/single-product/variants.tsx @@ -0,0 +1,158 @@ +import {VariantSelector} from '@shopify/hydrogen'; +import { + ProductQuery, + ProductVariantFragmentFragment, +} from 'storefrontapi.generated'; +import {Listbox} from '@headlessui/react'; +import {Heading, IconCaret, IconCheck, Link} from '~/components'; +import clsx from 'clsx'; +import {useRef} from 'react'; + +interface ProductVariantsProps { + selectedVariant: ProductVariantFragmentFragment; + onSelectedVariantChange: (variant: ProductVariantFragmentFragment) => void; + variants: { + nodes: ProductVariantFragmentFragment[]; + }; + handle: string; + product: NonNullable; + options: NonNullable['options']; +} +export function ProductVariants(props: ProductVariantsProps) { + let {selectedVariant, onSelectedVariantChange, options, variants, handle} = props; + const closeRef = useRef(null); + let selectedOptions = selectedVariant?.selectedOptions + let nodes = variants?.nodes; + let handleSelectOption = (optionName: string, value: string) => { + let newSelectedOptions = selectedOptions?.map((opt) => { + if (opt.name === optionName) { + return { + ...opt, + value, + } + } + return opt + }) + let newSelectedVariant = nodes?.find((variant) => { + let variantOptions = variant.selectedOptions + let isMatch = true + for (let i = 0; i < variantOptions.length; i++) { + if (variantOptions[i].value !== newSelectedOptions?.[i].value) { + isMatch = false + break + } + } + return isMatch + }) + if (newSelectedVariant) { + onSelectedVariantChange(newSelectedVariant) + } + + } + return ( +
+ + {({option}) => { + let optionName = option.name; + let selectedValue = selectedOptions?.find((opt) => opt.name === optionName)?.value + console.log("🚀 ~ selectedValue:", selectedValue) + return ( +
+ + {option.name} + +
+ {option.values.length > 7 ? ( +
+ + {({open}) => ( + <> + + {option.value} + + + + {option.values + .filter((value) => value.isAvailable) + .map(({value, to, isActive}) => ( + + {({active}) => ( + { + if (!closeRef?.current) return; + closeRef.current.click(); + }} + > + {value} + {isActive && ( + + + + )} + + )} + + ))} + + + )} + +
+ ) : ( + option.values.map(({value, isAvailable, to}) => ( + { + handleSelectOption(optionName, value) + }} + > + {value} + + )) + )} +
+
+ ); + }} +
+
+ ); +} From 97a29b9b82b4d92f7c2bed41c7e2cb8f2e1ac2dc Mon Sep 17 00:00:00 2001 From: ken Date: Thu, 16 Nov 2023 17:42:36 +0700 Subject: [PATCH 20/26] chore: update single-product --- app/sections/single-product/index.tsx | 53 ++++++++---------- app/sections/single-product/quantity.tsx | 69 +++++++++++++----------- 2 files changed, 61 insertions(+), 61 deletions(-) diff --git a/app/sections/single-product/index.tsx b/app/sections/single-product/index.tsx index 661f21e3..3ac56556 100644 --- a/app/sections/single-product/index.tsx +++ b/app/sections/single-product/index.tsx @@ -1,20 +1,18 @@ -import {Link} from '@remix-run/react'; +import { Money, ShopPayButton } from '@shopify/hydrogen'; +import { defer } from '@shopify/remix-oxygen'; import { + ComponentLoaderArgs, HydrogenComponentProps, HydrogenComponentSchema, - ComponentLoaderArgs, - getSelectedProductOptions, WeaverseProduct, + getSelectedProductOptions, } from '@weaverse/hydrogen'; -import {forwardRef, useState} from 'react'; -import {ProductQuery} from 'storefrontapi.generated'; -import {PRODUCT_QUERY, VARIANTS_QUERY} from '~/data/queries'; -import {defer, redirect} from '@shopify/remix-oxygen'; -import {ProductForm} from '../product-information/product-form'; -import {ProductVariants} from './variants'; -import {CartForm, Image, Money} from '@shopify/hydrogen'; -import {AddToCartButton, Button, ProductGallery} from '~/components'; +import { forwardRef, useState } from 'react'; +import { ProductQuery } from 'storefrontapi.generated'; +import { AddToCartButton, ProductGallery } from '~/components'; +import { PRODUCT_QUERY, VARIANTS_QUERY } from '~/data/queries'; import { Quantity } from './quantity'; +import { ProductVariants } from './variants'; type SingleProductData = { heading: string; @@ -30,9 +28,8 @@ type SingleProductProps = HydrogenComponentProps< let SingleProduct = forwardRef( (props, ref) => { let {loaderData, children, ...rest} = props; - let product = loaderData?.data.product.product; + let {storeDomain, product, variants} = loaderData?.data!; let productTitle = product?.title; - let variants = loaderData?.data?.variants; let [selectedVariant, setSelectedVariant] = useState(variants?.nodes[0]); let [quantity, setQuantity] = useState(1); return ( @@ -68,23 +65,6 @@ let SingleProduct = forwardRef( />
- - {/* - - */} ( > Add to Cart +
@@ -113,7 +103,7 @@ export let loader = async (args: ComponentLoaderArgs) => { return null; } let productHandle = data.product.handle; - let product = await storefront.query(PRODUCT_QUERY, { + let {product, shop} = await storefront.query(PRODUCT_QUERY, { variables: { handle: productHandle, // Should not get from request since this section could be used everywhere @@ -138,6 +128,7 @@ export let loader = async (args: ComponentLoaderArgs) => { return defer({ product, variants: variants?.product?.variants, + storeDomain: shop.primaryDomain.url, }); }; diff --git a/app/sections/single-product/quantity.tsx b/app/sections/single-product/quantity.tsx index d5a76df7..e1ce08cc 100644 --- a/app/sections/single-product/quantity.tsx +++ b/app/sections/single-product/quantity.tsx @@ -3,38 +3,47 @@ interface QuantityProps { onChange: (value: number) => void; } export function Quantity(props: QuantityProps) { - let {value, onChange} = props; - let handleKeyDown = (e: React.KeyboardEvent) => { - // Prevent the user from entering non-numeric characters - if (e.key !== 'Backspace' && e.key !== 'Delete' && e.key !== 'ArrowLeft' && e.key !== 'ArrowRight' && isNaN(Number(e.key))) { - e.preventDefault(); - } + let {value, onChange} = props; + let handleKeyDown = (e: React.KeyboardEvent) => { + // Prevent the user from entering non-numeric characters + if ( + e.key !== 'Backspace' && + e.key !== 'Delete' && + e.key !== 'ArrowLeft' && + e.key !== 'ArrowRight' && + isNaN(Number(e.key)) + ) { + e.preventDefault(); } + }; return ( -
- - onChange(Number(e.currentTarget.value))} - /> - +
+ Quantity +
+ + onChange(Number(e.currentTarget.value))} + /> + +
); } From 0c64a93aec49830f72c8afcd1ad99fef8c1b8eba Mon Sep 17 00:00:00 2001 From: ken Date: Fri, 17 Nov 2023 14:58:21 +0700 Subject: [PATCH 21/26] chore: update review --- .../($locale).api.review.$productHandle.tsx | 12 ++- app/sections/judgeme-review.tsx | 101 +++++++----------- app/sections/single-product/index.tsx | 10 +- 3 files changed, 51 insertions(+), 72 deletions(-) diff --git a/app/routes/($locale).api.review.$productHandle.tsx b/app/routes/($locale).api.review.$productHandle.tsx index 304031fc..aa2ef790 100644 --- a/app/routes/($locale).api.review.$productHandle.tsx +++ b/app/routes/($locale).api.review.$productHandle.tsx @@ -1,6 +1,5 @@ import {type RouteLoaderArgs} from '@weaverse/hydrogen'; import invariant from 'tiny-invariant'; -import {json} from '@shopify/remix-oxygen'; async function getInternalIdByHandle(api_token: string,shop_domain: string, handle: string) { let api = `https://judge.me/api/v1/products/-1?` + new URLSearchParams({ @@ -16,10 +15,12 @@ export async function loader(args: RouteLoaderArgs) { let { params, context} = args; let env = context.env let handle = params.productHandle - invariant(handle, 'Missing product handle'); let api_token = env.JUDGEME_PUBLIC_TOKEN let shop_domain = env.PUBLIC_STORE_DOMAIN - + invariant(handle, 'Missing product handle'); + if (!api_token) return { + error: 'Missing JUDGEME_PUBLIC_TOKEN' + } let internalId = await getInternalIdByHandle(api_token, shop_domain, handle) if (internalId) { let data = await fetch(`https://judge.me/api/v1/reviews?`+ new URLSearchParams({ @@ -28,9 +29,10 @@ export async function loader(args: RouteLoaderArgs) { product_id: internalId })).then(res => res.json()) let reviews = data.reviews - let rating = reviews.reduce((acc, review) => acc + review.rating, 0) / reviews.length + let rating = reviews.reduce((acc, review) => acc + review.rating, 0) / (reviews.length || 1) return { - rating + rating, + reviewNumber: reviews.length }; } return { diff --git a/app/sections/judgeme-review.tsx b/app/sections/judgeme-review.tsx index b880a2ff..9f9b3782 100644 --- a/app/sections/judgeme-review.tsx +++ b/app/sections/judgeme-review.tsx @@ -2,74 +2,49 @@ import type { HydrogenComponentProps, HydrogenComponentSchema, } from '@weaverse/hydrogen'; -import StarsRating from "react-star-rate"; +import StarsRating from 'react-star-rate'; import {useParentInstance} from '@weaverse/hydrogen'; import {useFetcher} from '@remix-run/react'; -import { useEffect } from 'react'; -import { usePrefixPathWithLocale } from '~/lib/utils'; +import {forwardRef, useEffect} from 'react'; +import {usePrefixPathWithLocale} from '~/lib/utils'; -let JudgemeReview = (props: HydrogenComponentProps) => { - const {load, data} = useFetcher(); - let context = useParentInstance() - - let rating = Math.round((data?.rating || 0) * 100) / 100 - let handle = context.data?.product?.handle - const api = handle && usePrefixPathWithLocale( - `/api/review/${handle}` - ); - useEffect( () => { - if (api) { - load(api) - } - }, [load, api]) - if (!data?.rating) return null - return
- ({rating}) -
; -}; +let JudgemeReview = forwardRef( + (props, ref) => { + const {load, data} = useFetcher<{ + rating: number; + reviewNumber: number; + error?: string; + }>(); + let context = useParentInstance(); + let handle = context?.data?.product?.handle!; + const api = handle && usePrefixPathWithLocale(`/api/review/${handle}`); + useEffect(() => { + if (api) { + load(api); + } + }, [load, api]); + if (!data) return null + if (data.error) return
{data.error}
; + let rating = Math.round((data.rating || 0) * 100) / 100; + let reviewNumber = data.reviewNumber || 0; + return ( +
+ {' '} + ({reviewNumber}) +
+ ); + }, +); export default JudgemeReview; - - -// export let loader = async (args) => { -// let {env} = args.weaverse -// let api_token = env.JUDGEME_PUBLIC_TOKEN -// let shop_domain = env.PUBLIC_STORE_DOMAIN -// console.log("🚀 ~ api_token:", env) -// async function getInternalIdByHandle(handle: string) { -// let api = `https://judge.me/api/v1/products/-1?` + new URLSearchParams({ -// api_token, -// shop_domain, -// handle: handle -// }) -// let data = await fetch(api).then(res => res.json()) -// return data?.product?.id -// } -// let handle = 'adidas-classic-backpack' -// let internalId = await getInternalIdByHandle(handle) -// if (internalId) { -// let data = await fetch(`https://judge.me/api/v1/reviews?`+ new URLSearchParams({ -// api_token, -// shop_domain, -// product_id: internalId -// })).then(res => res.json()) -// let reviews = data.reviews -// let rating = reviews.reduce((acc, review) => acc + review.rating, 0) / reviews.length -// // console.log("🚀 ~ data:", rating) -// return { -// rating -// }; -// } -// return { -// rating: 0 -// } -// } - - export let schema: HydrogenComponentSchema = { type: 'judgeme', title: 'Judgeme review', diff --git a/app/sections/single-product/index.tsx b/app/sections/single-product/index.tsx index 3ac56556..fa1dfbef 100644 --- a/app/sections/single-product/index.tsx +++ b/app/sections/single-product/index.tsx @@ -28,13 +28,16 @@ type SingleProductProps = HydrogenComponentProps< let SingleProduct = forwardRef( (props, ref) => { let {loaderData, children, ...rest} = props; - let {storeDomain, product, variants} = loaderData?.data!; + if (!loaderData) return
+ Please select product to show single product +
; + let {storeDomain, product, variants} = loaderData.data ; let productTitle = product?.title; let [selectedVariant, setSelectedVariant] = useState(variants?.nodes[0]); let [quantity, setQuantity] = useState(1); return (
-
+
Date: Fri, 17 Nov 2023 15:23:38 +0700 Subject: [PATCH 22/26] chore: update single product --- app/sections/single-product/index.tsx | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/app/sections/single-product/index.tsx b/app/sections/single-product/index.tsx index fa1dfbef..10fe5890 100644 --- a/app/sections/single-product/index.tsx +++ b/app/sections/single-product/index.tsx @@ -1,4 +1,4 @@ -import { Money, ShopPayButton } from '@shopify/hydrogen'; +import { Image, Money, ShopPayButton } from '@shopify/hydrogen'; import { defer } from '@shopify/remix-oxygen'; import { ComponentLoaderArgs, @@ -9,7 +9,7 @@ import { } from '@weaverse/hydrogen'; import { forwardRef, useState } from 'react'; import { ProductQuery } from 'storefrontapi.generated'; -import { AddToCartButton, ProductGallery } from '~/components'; +import { AddToCartButton } from '~/components'; import { PRODUCT_QUERY, VARIANTS_QUERY } from '~/data/queries'; import { Quantity } from './quantity'; import { ProductVariants } from './variants'; @@ -28,10 +28,13 @@ type SingleProductProps = HydrogenComponentProps< let SingleProduct = forwardRef( (props, ref) => { let {loaderData, children, ...rest} = props; - if (!loaderData) return
- Please select product to show single product -
; - let {storeDomain, product, variants} = loaderData.data ; + if (!loaderData) + return ( +
+ Please select product to show single product +
+ ); + let {storeDomain, product, variants} = loaderData.data; let productTitle = product?.title; let [selectedVariant, setSelectedVariant] = useState(variants?.nodes[0]); let [quantity, setQuantity] = useState(1); @@ -39,9 +42,10 @@ let SingleProduct = forwardRef(
-
From 15c827f393392a74e6ee2d0c5054470f87439335 Mon Sep 17 00:00:00 2001 From: hta218 Date: Sat, 18 Nov 2023 11:41:13 +0700 Subject: [PATCH 23/26] Refactor single product section and variants component --- app/sections/single-product/index.tsx | 53 ++++++++-------- app/sections/single-product/variants.tsx | 78 ++++++++++++------------ 2 files changed, 65 insertions(+), 66 deletions(-) diff --git a/app/sections/single-product/index.tsx b/app/sections/single-product/index.tsx index 10fe5890..5311b4f8 100644 --- a/app/sections/single-product/index.tsx +++ b/app/sections/single-product/index.tsx @@ -1,21 +1,18 @@ -import { Image, Money, ShopPayButton } from '@shopify/hydrogen'; -import { defer } from '@shopify/remix-oxygen'; +import {Image, Money, ShopPayButton} from '@shopify/hydrogen'; import { ComponentLoaderArgs, HydrogenComponentProps, HydrogenComponentSchema, WeaverseProduct, - getSelectedProductOptions, } from '@weaverse/hydrogen'; -import { forwardRef, useState } from 'react'; -import { ProductQuery } from 'storefrontapi.generated'; -import { AddToCartButton } from '~/components'; -import { PRODUCT_QUERY, VARIANTS_QUERY } from '~/data/queries'; -import { Quantity } from './quantity'; -import { ProductVariants } from './variants'; +import {forwardRef, useEffect, useState} from 'react'; +import {ProductQuery} from 'storefrontapi.generated'; +import {AddToCartButton} from '~/components'; +import {PRODUCT_QUERY, VARIANTS_QUERY} from '~/data/queries'; +import {Quantity} from './quantity'; +import {ProductVariants} from './variants'; type SingleProductData = { - heading: string; productsCount: number; product: WeaverseProduct; }; @@ -28,16 +25,23 @@ type SingleProductProps = HydrogenComponentProps< let SingleProduct = forwardRef( (props, ref) => { let {loaderData, children, ...rest} = props; - if (!loaderData) + let {storeDomain, product, variants} = loaderData || {}; + let [selectedVariant, setSelectedVariant] = useState(variants?.nodes?.[0]); + let [quantity, setQuantity] = useState(1); + + useEffect(() => { + setSelectedVariant(variants?.nodes?.[0]); + setQuantity(1); + }, [product]); + + if (!product) { + // TODO: should render placeholder instead of this message return ( -
+
Please select product to show single product
); - let {storeDomain, product, variants} = loaderData.data; - let productTitle = product?.title; - let [selectedVariant, setSelectedVariant] = useState(variants?.nodes[0]); - let [quantity, setQuantity] = useState(1); + } return (
@@ -50,7 +54,7 @@ let SingleProduct = forwardRef(

- {productTitle} + {product?.title}

( {product?.descriptionHtml}

( export let loader = async (args: ComponentLoaderArgs) => { let {weaverse, data} = args; let {storefront} = weaverse; - let selectedOptions = getSelectedProductOptions(weaverse.request); if (!data?.product) { return null; } @@ -113,18 +117,11 @@ export let loader = async (args: ComponentLoaderArgs) => { let {product, shop} = await storefront.query(PRODUCT_QUERY, { variables: { handle: productHandle, - // Should not get from request since this section could be used everywhere - // TODO: update the query to not require `selectedOptions` or create a new query for this component - selectedOptions, + selectedOptions: [], language: storefront.i18n.language, country: storefront.i18n.country, }, }); - // In order to show which variants are available in the UI, we need to query - // all of them. But there might be a *lot*, so instead separate the variants - // into it's own separate query that is deferred. So there's a brief moment - // where variant options might show as available when they're not, but after - // this deferred query resolves, the UI will update. let variants = await storefront.query(VARIANTS_QUERY, { variables: { handle: productHandle, @@ -132,11 +129,11 @@ export let loader = async (args: ComponentLoaderArgs) => { country: storefront.i18n.country, }, }); - return defer({ + return { product, variants: variants?.product?.variants, storeDomain: shop.primaryDomain.url, - }); + }; }; export let schema: HydrogenComponentSchema = { diff --git a/app/sections/single-product/variants.tsx b/app/sections/single-product/variants.tsx index e748bbc4..40961ab3 100644 --- a/app/sections/single-product/variants.tsx +++ b/app/sections/single-product/variants.tsx @@ -1,12 +1,12 @@ +import {Listbox} from '@headlessui/react'; import {VariantSelector} from '@shopify/hydrogen'; +import clsx from 'clsx'; +import {useRef} from 'react'; import { ProductQuery, ProductVariantFragmentFragment, } from 'storefrontapi.generated'; -import {Listbox} from '@headlessui/react'; import {Heading, IconCaret, IconCheck, Link} from '~/components'; -import clsx from 'clsx'; -import {useRef} from 'react'; interface ProductVariantsProps { selectedVariant: ProductVariantFragmentFragment; @@ -19,36 +19,36 @@ interface ProductVariantsProps { options: NonNullable['options']; } export function ProductVariants(props: ProductVariantsProps) { - let {selectedVariant, onSelectedVariantChange, options, variants, handle} = props; + let {selectedVariant, onSelectedVariantChange, options, variants, handle} = + props; const closeRef = useRef(null); - let selectedOptions = selectedVariant?.selectedOptions + let selectedOptions = selectedVariant?.selectedOptions; let nodes = variants?.nodes; - let handleSelectOption = (optionName: string, value: string) => { - let newSelectedOptions = selectedOptions?.map((opt) => { - if (opt.name === optionName) { - return { - ...opt, - value, - } - } - return opt - }) - let newSelectedVariant = nodes?.find((variant) => { - let variantOptions = variant.selectedOptions - let isMatch = true - for (let i = 0; i < variantOptions.length; i++) { - if (variantOptions[i].value !== newSelectedOptions?.[i].value) { - isMatch = false - break - } - } - return isMatch - }) - if (newSelectedVariant) { - onSelectedVariantChange(newSelectedVariant) - } - - } + let handleSelectOption = (optionName: string, value: string) => { + let newSelectedOptions = selectedOptions?.map((opt) => { + if (opt.name === optionName) { + return { + ...opt, + value, + }; + } + return opt; + }); + let newSelectedVariant = nodes?.find((variant) => { + let variantOptions = variant.selectedOptions; + let isMatch = true; + for (let i = 0; i < variantOptions.length; i++) { + if (variantOptions[i].value !== newSelectedOptions?.[i].value) { + isMatch = false; + break; + } + } + return isMatch; + }); + if (newSelectedVariant) { + onSelectedVariantChange(newSelectedVariant); + } + }; return (
{({option}) => { let optionName = option.name; - let selectedValue = selectedOptions?.find((opt) => opt.name === optionName)?.value - console.log("🚀 ~ selectedValue:", selectedValue) + let selectedValue = selectedOptions?.find( + (opt) => opt.name === optionName, + )?.value; return (
( { - handleSelectOption(optionName, value) - }} + onClick={() => { + handleSelectOption(optionName, value); + }} > {value} From 095538635d8fcd52bbc0e7a06603bdf25491b8ab Mon Sep 17 00:00:00 2001 From: hta218 Date: Sat, 18 Nov 2023 11:49:47 +0700 Subject: [PATCH 24/26] Refactor API review route and add Judgeme integration --- .../($locale).api.review.$productHandle.tsx | 89 ++++++++++++------- remix.env.d.ts | 1 + 2 files changed, 58 insertions(+), 32 deletions(-) diff --git a/app/routes/($locale).api.review.$productHandle.tsx b/app/routes/($locale).api.review.$productHandle.tsx index aa2ef790..793bf8bf 100644 --- a/app/routes/($locale).api.review.$productHandle.tsx +++ b/app/routes/($locale).api.review.$productHandle.tsx @@ -1,41 +1,66 @@ import {type RouteLoaderArgs} from '@weaverse/hydrogen'; import invariant from 'tiny-invariant'; -async function getInternalIdByHandle(api_token: string,shop_domain: string, handle: string) { - let api = `https://judge.me/api/v1/products/-1?` + new URLSearchParams({ +type JudgemeProductData = { + product: { + id: string; + handle: string; + }; +}; + +type JudgemeReviewsData = { + reviews: { + rating: number; + }[]; +}; + +async function getInternalIdByHandle( + api_token: string, + shop_domain: string, + handle: string, +) { + let api = + `https://judge.me/api/v1/products/-1?` + + new URLSearchParams({ api_token, shop_domain, - handle: handle - }) - let data = await fetch(api).then(res => res.json()) - return data?.product?.id - } + handle: handle, + }); + let data = (await fetch(api).then((res) => res.json())) as JudgemeProductData; + return data?.product?.id; +} export async function loader(args: RouteLoaderArgs) { - let { params, context} = args; - let env = context.env - let handle = params.productHandle - let api_token = env.JUDGEME_PUBLIC_TOKEN - let shop_domain = env.PUBLIC_STORE_DOMAIN - invariant(handle, 'Missing product handle'); - if (!api_token) return { - error: 'Missing JUDGEME_PUBLIC_TOKEN' - } - let internalId = await getInternalIdByHandle(api_token, shop_domain, handle) - if (internalId) { - let data = await fetch(`https://judge.me/api/v1/reviews?`+ new URLSearchParams({ - api_token, - shop_domain, - product_id: internalId - })).then(res => res.json()) - let reviews = data.reviews - let rating = reviews.reduce((acc, review) => acc + review.rating, 0) / (reviews.length || 1) - return { - rating, - reviewNumber: reviews.length - }; - } + let {params, context} = args; + let env = context.env; + let handle = params.productHandle; + let api_token = env.JUDGEME_PUBLIC_TOKEN; + let shop_domain = env.PUBLIC_STORE_DOMAIN; + invariant(handle, 'Missing product handle'); + if (!api_token) + return { + error: 'Missing JUDGEME_PUBLIC_TOKEN', + }; + let internalId = await getInternalIdByHandle(api_token, shop_domain, handle); + if (internalId) { + let data = (await fetch( + `https://judge.me/api/v1/reviews?` + + new URLSearchParams({ + api_token, + shop_domain, + product_id: internalId, + }), + ).then((res) => res.json())) as JudgemeReviewsData; + let reviews = data.reviews; + let rating = + reviews.reduce((acc, review) => acc + review.rating, 0) / + (reviews.length || 1); return { - rating: 0 - } + rating, + reviewNumber: reviews.length, + }; + } + return { + rating: 0, + }; } diff --git a/remix.env.d.ts b/remix.env.d.ts index d405f536..abc55bd0 100644 --- a/remix.env.d.ts +++ b/remix.env.d.ts @@ -24,6 +24,7 @@ declare global { PUBLIC_STOREFRONT_ID: string; WEAVERSE_PROJECT_ID: string; WEAVERSE_HOST: string; + JUDGEME_PUBLIC_TOKEN: string; } } From ca16f9ae5df0ce9df2ab39073abc50f95ce30e89 Mon Sep 17 00:00:00 2001 From: hta218 Date: Sat, 18 Nov 2023 11:56:38 +0700 Subject: [PATCH 25/26] Update @weaverse/hydrogen version to 2.6.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ecd133c5..5417e01b 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "@shopify/cli-hydrogen": "^6.0.2", "@shopify/hydrogen": "^2023.10.2", "@shopify/remix-oxygen": "^2.0.1", - "@weaverse/hydrogen": "2.6.1", + "@weaverse/hydrogen": "^2.6.2", "clsx": "2.0.0", "cross-env": "7.0.3", "graphql": "16.8.1", From 84e70ea4bc8ec4b296bded0e94f54e00534989c3 Mon Sep 17 00:00:00 2001 From: hta218 Date: Sun, 19 Nov 2023 14:51:52 +0700 Subject: [PATCH 26/26] Update version to 2.3.1 --- README.md | 2 +- package.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c61e4fb7..a74de86b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@

- Version + Version License: MIT diff --git a/package.json b/package.json index 5417e01b..4be9d0bf 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "private": false, "sideEffects": false, "author": "Weaverse", - "version": "2.3.0", + "version": "2.3.1", "scripts": { "dev": "shopify hydrogen dev --codegen --port 3456", "build": "shopify hydrogen build", @@ -66,4 +66,4 @@ "engines": { "node": ">=16.13" } -} +} \ No newline at end of file