@luxfi/core 5.0.2 → 5.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,34 @@
1
+ [
2
+ {
3
+ "id": "LXM-AG-B",
4
+ "title": "Minted Bar",
5
+ "parentTitle": "Lux Silver",
6
+ "desc": "Get unprecedented access to silver with 1:1 asset-backed Lux Silver NFTs, sovereign ownership of physical silver without management fees, and mine-direct discount pricing.",
7
+ "img": {
8
+ "src": "/assets/commerce/silver/product/silver-bar-pt-800x800.png",
9
+ "dim": {
10
+ "w": 800,
11
+ "h": 800
12
+ }
13
+ },
14
+ "products": [
15
+ {
16
+ "id": "fce17569-a86c-4f69-bc61-ab435ed0c46f",
17
+ "sku": "LXM-AG-B-1_OZ",
18
+ "fullTitle": "Lux Silver, 1oz Minted Bar",
19
+ "optionLabel": "1oz",
20
+ "familyTitle": "Minted Bar",
21
+ "familyId": "LXM-AG-B",
22
+ "desc": "Get unprecedented access to silver with 1:1 asset-backed Lux Silver NFTs, sovereign ownership of physical silver without management fees, and mine-direct discount pricing.",
23
+ "price": 0,
24
+ "img": {
25
+ "src": "/assets/commerce/silver/product/silver-bar-pt-800x800.png",
26
+ "dim": {
27
+ "w": 800,
28
+ "h": 800
29
+ }
30
+ }
31
+ }
32
+ ]
33
+ }
34
+ ]
@@ -0,0 +1,12 @@
1
+ // updated manually apr 29, 2024
2
+
3
+ export default {
4
+ au: {
5
+ market1oz: 2336.50,
6
+ discount: 0.01
7
+ },
8
+ ag: {
9
+ market1oz: 27.143,
10
+ discount: 0.10
11
+ },
12
+ }
@@ -0,0 +1,50 @@
1
+ import type { Family } from '@hanzo/commerce/types'
2
+
3
+ import prices from './EDIT-ME-bullion-market-prices'
4
+
5
+ const sep = {
6
+ tok: '-',
7
+ subTok: '_',
8
+ decimal: '.'
9
+ }
10
+
11
+ const GRAMS_PER_OZ = 28.3495
12
+
13
+ const tree: any = {}
14
+ for (let key in prices) {
15
+ const values = prices[key as keyof typeof prices]
16
+ tree[key] = {
17
+ oz: values.market1oz * (1 - values.discount),
18
+ g: values.market1oz * (1 - values.discount) / GRAMS_PER_OZ
19
+ }
20
+ }
21
+
22
+ // LXB-AU-B-1_OZ, LXB-AU-B-2.5_g
23
+ const priceFromSKU = (
24
+ sku: string
25
+ ) => {
26
+ const tokens = sku.split(sep.tok)
27
+ const type_ = tokens[1].toLowerCase()
28
+
29
+ const quanAndUnit = tokens[tokens.length - 1]
30
+ const quanAndUnitToks = quanAndUnit.split(sep.subTok)
31
+ let quantity = quanAndUnitToks[0].includes(sep.decimal) ? parseFloat(quanAndUnitToks[0].split(sep.decimal).join('.')) : parseInt(quanAndUnitToks[0])
32
+ let unit = quanAndUnitToks[1].toLowerCase()
33
+ if (unit === 'kg') {
34
+ quantity *= 1000
35
+ unit = 'g'
36
+ }
37
+ else if (unit === 'lb') {
38
+ quantity *= 16
39
+ unit = 'oz'
40
+ }
41
+
42
+ return tree[type_][unit] * quantity
43
+ }
44
+
45
+ export default (c: Family): Family => {
46
+ for (let prod of c.products) {
47
+ prod.price = priceFromSKU(prod.sku)
48
+ }
49
+ return c
50
+ }
@@ -0,0 +1,14 @@
1
+ import type { VideoDef } from '@hanzo/ui/types'
2
+ import type { Family } from '@hanzo/commerce/types'
3
+
4
+ export default (fam: Family, map: Map<string, VideoDef>): Family => {
5
+ if (fam.parentTitle) {
6
+ for (let prod of fam.products) {
7
+ const video = map.get(fam.parentTitle)
8
+ if (video) {
9
+ prod.video = video
10
+ }
11
+ }
12
+ }
13
+ return fam
14
+ }
@@ -0,0 +1,5 @@
1
+ import PRICES from './EDIT-ME-bullion-market-prices'
2
+
3
+ export default (type_: keyof typeof PRICES): number => (
4
+ PRICES[type_].market1oz * (1 - PRICES[type_].discount)
5
+ )
@@ -0,0 +1,18 @@
1
+ import type { VideoDef } from '@hanzo/ui/types'
2
+
3
+ import bullionByFamily from './AUTO-GEN-bullion-by-family.json'
4
+
5
+ import assignPrices from './assign-prices'
6
+ import assignVideosByFamilyGroup from './assign-videos-by-family-group'
7
+
8
+ export const getBullionFamilies = (videoMap: Map<string, VideoDef>) => (
9
+ bullionByFamily.map(
10
+ (fam) => (assignPrices(fam))).map(
11
+ (fam) => (assignVideosByFamilyGroup(fam, videoMap)
12
+ )
13
+ )
14
+ )
15
+
16
+ export { default as serviceOptions } from './lux-service-options'
17
+ export { default as bullionPrice1oz } from './bullion-price-1oz'
18
+
@@ -0,0 +1,6 @@
1
+ import type { ServiceOptions } from '@hanzo/commerce'
2
+
3
+ export default {
4
+ dbName: 'lux-commerce',
5
+ ordersTable: 'orders'
6
+ } satisfies ServiceOptions
@@ -2,9 +2,10 @@
2
2
  import React from 'react'
3
3
 
4
4
  import { Button, Card } from '@hanzo/ui/primitives'
5
+ import { cn } from '@hanzo/ui/util'
5
6
 
6
7
  import LuxLogo from './icons/lux-logo'
7
- import { cn } from '@hanzo/ui/util'
8
+ import type { ChatbotSuggestedQuestion } from '../types'
8
9
 
9
10
  const ChatWidget: React.FC<{
10
11
  title: string,
@@ -12,6 +13,7 @@ const ChatWidget: React.FC<{
12
13
  subtitle?: string,
13
14
  question?: string,
14
15
  /*
16
+ ChatBotSuggestQuestion.icon
15
17
  Currently supports these icons from remix icons (https://remixicon.com/):
16
18
  GlobalLineIcon,
17
19
  ShieldFlashLineIcon,
@@ -19,7 +21,7 @@ const ChatWidget: React.FC<{
19
21
  GroupLineIcon,
20
22
  QuestionnaireLineIcon
21
23
  */
22
- suggestedQuestions?: { heading: string, message: string, icon?: string }[]
24
+ suggestedQuestions?: ChatbotSuggestedQuestion[]
23
25
  }> = ({
24
26
  title,
25
27
  chatbotUrl,
@@ -1,109 +1,17 @@
1
1
  'use client'
2
- import React, { useEffect, useRef } from 'react'
3
- import { createPortal } from 'react-dom'
4
- import { useRouter, usePathname } from 'next/navigation'
5
- import { ObservableSet, observable, reaction, type IReactionDisposer, runInAction} from 'mobx'
2
+ import React from 'react'
3
+ import { useRouter } from 'next/navigation'
6
4
  import { observer } from 'mobx-react-lite'
7
5
 
8
- import { cn } from '@hanzo/ui/util'
9
- import { Image } from '@hanzo/ui/primitives'
10
-
11
- import { useCommerce, useCommerceUI, CarouselBuyCard, LineItemRef } from '@hanzo/commerce'
12
- import type { LineItem } from '@hanzo/commerce/types'
6
+ import { useCommerceUI, CarouselBuyCard } from '@hanzo/commerce'
13
7
 
14
8
  import CommerceDrawer from './drawer'
15
9
  import CheckoutButton from '../checkout-button'
16
10
 
17
- const DEF_IMG_CONSTRAINT={w: 40, h: 24}
18
- const CO_ANIM_DURATION = 400
19
- const CO_ANIM_TIMING_FN = 'cubic-bezier(0.4, 0, 0.2, 1)'
20
- const CO_WIDGET_W_CLX = {
21
- checkout: 'w-pr-40',
22
- itemInfo: 'w-pr-60'
23
- }
24
-
25
- const CO_WIDGET_SHADOW_STYLE = {
26
- border: '1px solid rgb(100 100 100)',
27
- boxShadow: '2px 4px 4px -3px rgb(125 125 125 / 0.7), 4px -4px 8px -4px rgb(125 125 125 / 0.7)'
28
- }
29
-
30
11
  const CommerceUIComponent: React.FC = observer(() => {
31
12
 
32
13
  const ui = useCommerceUI()
33
- const cmmc = useCommerce()
34
14
  const router = useRouter()
35
- const isCheckout = usePathname() === '/checkout'
36
-
37
- const animClxRef = useRef<ObservableSet<string>>(observable.set(new Set<string>(
38
- (cmmc.cartEmpty || ui.buyOptionsSkuPath || isCheckout) ? ['hidden'] : []
39
- )))
40
-
41
- // a ref that is synced to ui.activeItem, but persists for CO_ANIM_DURATION longer
42
- // so ui does not jump while animating "out"
43
- const laggingActiveItemRef = useRef<LineItemRef>(new LineItemRef())
44
- const disposersRef = useRef<IReactionDisposer[]>([])
45
-
46
- useEffect(() => {
47
- disposersRef.current.push(
48
- reaction(
49
- () => (ui.activeItem),
50
- (item: LineItem | undefined) => {
51
- if (item) {
52
- laggingActiveItemRef.current.set(item)
53
- }
54
- else {
55
- setTimeout(() => { laggingActiveItemRef.current.set(undefined) }, CO_ANIM_DURATION)
56
- }
57
- },
58
- {equals: (val, prev) => (val?.sku === prev?.sku)}
59
- )
60
- )
61
-
62
- disposersRef.current.push(
63
- reaction(
64
- () => ({
65
- microOpen: !(cmmc.cartEmpty || !!ui.buyOptionsSkuPath || isCheckout),
66
- buyOpen: !!ui.buyOptionsSkuPath
67
- }),
68
- (val, prev) => {
69
-
70
- runInAction(() => {
71
- if (!val.microOpen && prev.microOpen) {
72
- animClxRef.current.add('checkout-widget-disappears')
73
- }
74
- else if (val.microOpen && !prev.microOpen) {
75
- animClxRef.current.delete('hidden')
76
- animClxRef.current.add('checkout-widget-appears')
77
- }
78
- if (!val.buyOpen && prev.buyOpen) {
79
- animClxRef.current.add('checkout-widget-appears-after-buy-drawer-closes')
80
- }
81
- else {
82
- animClxRef.current.delete('checkout-widget-appears-after-buy-drawer-closes')
83
- }
84
- })
85
-
86
- setTimeout(() => {runInAction(() => {
87
- animClxRef.current.delete('checkout-widget-appears')
88
- animClxRef.current.delete('checkout-widget-appears-after-buy-drawer-closes')
89
- if (animClxRef.current.has('checkout-widget-disappears') ) {
90
- animClxRef.current.delete('checkout-widget-disappears')
91
- animClxRef.current.add('hidden')
92
- }
93
- })}, 800)
94
- },
95
- {equals: (val, prev) => (
96
- val.microOpen === prev.microOpen
97
- &&
98
- val.buyOpen === prev.buyOpen
99
- )}
100
- )
101
- )
102
-
103
- return () => {
104
- disposersRef.current.forEach((d) => {d()})
105
- }
106
- }, [])
107
15
 
108
16
  const handleCheckout = () => {
109
17
  router.push('/checkout')
@@ -116,11 +24,10 @@ const CommerceUIComponent: React.FC = observer(() => {
116
24
  }
117
25
  }
118
26
 
119
- return (<>
27
+ return (
120
28
  <CommerceDrawer
121
29
  open={!!ui.buyOptionsSkuPath}
122
30
  setOpen={reallyOnlyCloseDrawer}
123
- modal={true}
124
31
  drawerClx={'w-full md:max-w-[550px] md:mx-auto lg:max-w-[50vw]'}
125
32
  >
126
33
  <CarouselBuyCard
@@ -133,58 +40,7 @@ const CommerceUIComponent: React.FC = observer(() => {
133
40
  selectorClx='max-w-[475px]'
134
41
  />
135
42
  </CommerceDrawer>
136
- {globalThis?.document?.body && createPortal(
137
- <div
138
- className={cn(
139
- 'min-w-[160px] sm:max-w-[320px] w-[calc(100%-72px)] ml-2 !h-10',
140
- 'z-below-modal-2 fixed bottom-[20px] left-0 right-0',
141
- 'rounded-lg bg-background',
142
- 'flex',
143
- ui.activeItem ? 'gap-2' : '',
144
- Array.from(animClxRef.current).join(' ')
145
- )}
146
- style={laggingActiveItemRef.current.item ? {} : CO_WIDGET_SHADOW_STYLE}
147
- >
148
- <div
149
- className={cn(
150
- 'flex flex-row justify-between items-center',
151
- ui.activeItem ? CO_WIDGET_W_CLX.itemInfo : 'w-0',
152
- laggingActiveItemRef.current.item ? 'px-3 border rounded-lg border-muted-3' : ''
153
- )}
154
- style={{
155
- transitionProperty: 'width',
156
- transitionTimingFunction: CO_ANIM_TIMING_FN,
157
- transitionDuration: `${CO_ANIM_DURATION}ms`
158
- }}
159
- >
160
- {laggingActiveItemRef.current.item?.img ? (
161
- <Image def={laggingActiveItemRef.current.item.img} constrainTo={DEF_IMG_CONSTRAINT} preload className='grow-0 shrink-0'/>
162
- ) : ( // placeholder so things align
163
- <div style={{height: DEF_IMG_CONSTRAINT.h, width: DEF_IMG_CONSTRAINT.w}} className='bg-level-3 grow-0 shrink-0'/>
164
- )}
165
-
166
- <div className='text-muted grow ml-1'>
167
- {laggingActiveItemRef.current.item && (<>
168
- <p className='whitespace-nowrap text-sm'>{laggingActiveItemRef.current.item.title}</p>
169
- <p className='whitespace-nowrap text-xxs' >recently added...</p>
170
- </>)}
171
- </div>
172
- </div>
173
- <CheckoutButton
174
- handleCheckout={handleCheckout}
175
- centerText={!!!ui.activeItem}
176
- variant='primary' rounded='lg'
177
- className={cn(ui.activeItem ? CO_WIDGET_W_CLX.checkout : 'w-full')}
178
- style={{
179
- transitionProperty: 'width',
180
- transitionTimingFunction: CO_ANIM_TIMING_FN,
181
- transitionDuration: `${CO_ANIM_DURATION}ms`
182
- }}
183
- />
184
- </div>,
185
- globalThis?.document?.body
186
- )}
187
- </>)
43
+ )
188
44
  })
189
45
 
190
46
  export default CommerceUIComponent
@@ -0,0 +1,13 @@
1
+ export default {
2
+ itemImgConstraint: { w: 40, h: 24 },
3
+ animDurationMs: 400,
4
+ animTimingFn: 'cubic-bezier(0.4, 0, 0.2, 1)',
5
+ compWidthClx: {
6
+ checkout: 'w-pr-40',
7
+ itemInfo: 'w-pr-60'
8
+ },
9
+ shadowStyle: {
10
+ border: '1px solid rgb(100 100 100)',
11
+ boxShadow: '2px 4px 4px -3px rgb(125 125 125 / 0.7), 4px -4px 8px -4px rgb(125 125 125 / 0.7)'
12
+ }
13
+ }
@@ -0,0 +1,86 @@
1
+ 'use client'
2
+ import React from 'react'
3
+ import { createPortal } from 'react-dom'
4
+ import { usePathname, useRouter } from 'next/navigation'
5
+ import { observer } from 'mobx-react-lite'
6
+
7
+ import { cn } from '@hanzo/ui/util'
8
+ import { Image } from '@hanzo/ui/primitives'
9
+
10
+ import { useCommerceUI } from '@hanzo/commerce'
11
+
12
+ import CheckoutButton from '../checkout-button'
13
+ import useAnimationClxSet from './use-anim-clx-set'
14
+ import useLaggingItemRef from './use-lagging-item-ref'
15
+ import CONST from './const'
16
+
17
+ const CheckoutWidget: React.FC<{
18
+ clx?: string
19
+ }> = observer(({
20
+ clx=''
21
+ }) => {
22
+
23
+ const router = useRouter()
24
+
25
+ const isCheckout = usePathname() === '/checkout'
26
+ const clxSet = useAnimationClxSet(isCheckout)
27
+
28
+ const itemRef = useCommerceUI()
29
+ const laggingRef = useLaggingItemRef(itemRef, CONST.animDurationMs)
30
+
31
+ const handleCheckout = () => { router.push('/checkout')}
32
+
33
+ return globalThis?.document?.body && createPortal(
34
+ (<div
35
+ className={cn(
36
+ 'min-w-[160px] sm:max-w-[320px] w-[calc(100%-72px)] ml-2 !h-10',
37
+ 'z-below-modal-2 fixed bottom-[20px] left-0 right-0',
38
+ 'rounded-lg bg-background',
39
+ 'flex',
40
+ itemRef.item ? 'gap-2' : '',
41
+ clxSet.asArray.join(' ')
42
+ )}
43
+ style={laggingRef.item ? {} : CONST.shadowStyle}
44
+ >
45
+ <div
46
+ className={cn(
47
+ 'flex flex-row justify-between items-center',
48
+ itemRef.item ? CONST.compWidthClx.itemInfo : 'w-0',
49
+ laggingRef.item ? 'px-3 border rounded-lg border-muted-3' : ''
50
+ )}
51
+ style={{
52
+ transitionProperty: 'width',
53
+ transitionTimingFunction: CONST.animTimingFn,
54
+ transitionDuration: `${CONST.animDurationMs}ms`
55
+ }}
56
+ >
57
+ {laggingRef.item?.img ? (
58
+ <Image def={laggingRef.item.img} constrainTo={CONST.itemImgConstraint} preload className='grow-0 shrink-0'/>
59
+ ) : ( // placeholder so things align
60
+ <div style={{height: CONST.itemImgConstraint.h, width: CONST.itemImgConstraint.w}} className='bg-level-3 grow-0 shrink-0'/>
61
+ )}
62
+
63
+ <div className='text-muted grow ml-1'>
64
+ {laggingRef.item && (<>
65
+ <p className='whitespace-nowrap text-sm'>{laggingRef.item.title}</p>
66
+ <p className='whitespace-nowrap text-xxs' >recently added...</p>
67
+ </>)}
68
+ </div>
69
+ </div>
70
+ <CheckoutButton
71
+ handleCheckout={handleCheckout}
72
+ centerText={!!!itemRef.item}
73
+ variant='primary' rounded='lg'
74
+ className={cn(itemRef.item ? CONST.compWidthClx.checkout : 'w-full')}
75
+ style={{
76
+ transitionProperty: 'width',
77
+ transitionTimingFunction: CONST.animTimingFn,
78
+ transitionDuration: `${CONST.animDurationMs}ms`
79
+ }}
80
+ />
81
+ </div>),
82
+ globalThis?.document?.body
83
+ )
84
+ })
85
+
86
+ export default CheckoutWidget
@@ -0,0 +1,48 @@
1
+ import {
2
+ action,
3
+ computed,
4
+ makeObservable,
5
+ observable
6
+ } from 'mobx'
7
+
8
+ class ObsStringSet {
9
+
10
+ private _set = observable.set(new Set<string>())
11
+
12
+ constructor(initial: string[] = []) {
13
+ initial.forEach((el) => {this._set.add(el)})
14
+ makeObservable(this, {
15
+ add: action,
16
+ remove: action,
17
+ asArray: computed
18
+ })
19
+ }
20
+
21
+ add = (v: string | string[]): void => {
22
+ if (Array.isArray(v)) {
23
+ v.forEach((el) => {this._set.add(el)})
24
+ }
25
+ else {
26
+ this._set.add(v)
27
+ }
28
+ }
29
+
30
+ remove = (v: string | string[]): void => {
31
+ if (Array.isArray(v)) {
32
+ v.forEach((el) => {this._set.delete(el)})
33
+ }
34
+ else {
35
+ this._set.delete(v)
36
+ }
37
+ }
38
+
39
+ has = (v: string): boolean => (
40
+ this._set.has(v)
41
+ )
42
+
43
+ get asArray() {
44
+ return Array.from(this._set)
45
+ }
46
+ }
47
+
48
+ export default ObsStringSet
@@ -0,0 +1,57 @@
1
+ import { useEffect, useRef } from 'react'
2
+ import { reaction, runInAction} from 'mobx'
3
+
4
+ import ObsStringSet from './obs-string-set'
5
+ import { useCommerce, useCommerceUI } from '@hanzo/commerce'
6
+
7
+ export default (isCheckout: boolean): ObsStringSet => {
8
+
9
+ const ui = useCommerceUI()
10
+ const cmmc = useCommerce()
11
+
12
+ const clxSetRef = useRef<ObsStringSet>(new ObsStringSet(
13
+ (cmmc.cartEmpty || ui.buyOptionsSkuPath || isCheckout) ? ['hidden'] : []
14
+ ))
15
+
16
+ useEffect(() => (
17
+ reaction(
18
+ () => ({
19
+ microOpen: !(cmmc.cartEmpty || !!ui.buyOptionsSkuPath || isCheckout),
20
+ buyOpen: !!ui.buyOptionsSkuPath
21
+ }),
22
+ (val, prev) => {
23
+
24
+ runInAction(() => {
25
+ if (!val.microOpen && prev.microOpen) {
26
+ clxSetRef.current.add('checkout-widget-disappears')
27
+ }
28
+ else if (val.microOpen && !prev.microOpen) {
29
+ clxSetRef.current.remove('hidden')
30
+ clxSetRef.current.add('checkout-widget-appears')
31
+ }
32
+ if (!val.buyOpen && prev.buyOpen) {
33
+ clxSetRef.current.add('checkout-widget-appears-after-buy-drawer-closes')
34
+ }
35
+ else {
36
+ clxSetRef.current.remove('checkout-widget-appears-after-buy-drawer-closes')
37
+ }
38
+ })
39
+
40
+ setTimeout(() => {runInAction(() => {
41
+ clxSetRef.current.remove(['checkout-widget-appears', 'checkout-widget-appears-after-buy-drawer-closes'])
42
+ if (clxSetRef.current.has('checkout-widget-disappears') ) {
43
+ clxSetRef.current.remove('checkout-widget-disappears')
44
+ clxSetRef.current.add('hidden')
45
+ }
46
+ })}, 800)
47
+ },
48
+ {equals: (val, prev) => (
49
+ val.microOpen === prev.microOpen
50
+ &&
51
+ val.buyOpen === prev.buyOpen
52
+ )}
53
+ )
54
+ ), [isCheckout])
55
+
56
+ return clxSetRef.current
57
+ }
@@ -0,0 +1,30 @@
1
+ import { useEffect, useRef } from 'react'
2
+ import { reaction } from 'mobx'
3
+
4
+ import type { LineItem, ObsLineItemRef } from "@hanzo/commerce/types"
5
+ import { LineItemRef } from '@hanzo/commerce'
6
+
7
+ export default (orig: ObsLineItemRef, lagMs: number): ObsLineItemRef => {
8
+
9
+ // a ref that is synced to 'orig', but persists for lagMs longer
10
+ // so ui does not jump while animating out.
11
+ // (Fascilitates for start and end states in animation)
12
+ const laggingRef = useRef<LineItemRef>(new LineItemRef())
13
+
14
+ useEffect(() => (
15
+ reaction(
16
+ () => (orig.item),
17
+ (item: LineItem | undefined) => {
18
+ if (item) {
19
+ laggingRef.current.set(item)
20
+ }
21
+ else {
22
+ setTimeout(() => { laggingRef.current.set(undefined) }, lagMs)
23
+ }
24
+ },
25
+ {equals: (val, prev) => (val?.sku === prev?.sku)}
26
+ )
27
+ ), [])
28
+
29
+ return laggingRef.current
30
+ }
@@ -9,13 +9,12 @@ export { default as Logo } from './logo'
9
9
  export { default as MiniChart } from './mini-chart'
10
10
  export { default as NotFound } from './not-found'
11
11
 
12
-
13
-
12
+ export { default as AuthListener } from './auth/auth-listener'
14
13
  export { default as BuyDrawer } from './commerce/buy-drawer'
15
- export { default as CheckoutPanel } from './commerce/checkout-panel'
16
14
  export { default as CheckoutButton } from './commerce/checkout-button'
15
+ export { default as CheckoutPanel } from './commerce/checkout-panel'
16
+ export { default as CheckoutWidget } from './commerce/checkout-widget'
17
17
  export { default as LoginPanel } from './auth/login-panel'
18
- export { default as AuthListener } from './auth/auth-listener'
19
18
  export { default as Scripts } from './scripts'
20
19
 
21
20
 
package/next/index.ts CHANGED
@@ -1 +1 @@
1
- export { default as determineDeviceMW } from './determine-device-mw'
1
+ export { default as determineDeviceMW } from './middleware/determine-device-mw'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luxfi/core",
3
- "version": "5.0.2",
3
+ "version": "5.0.4",
4
4
  "description": "Library that contains shared UI primitives, support for a common design system, and other boilerplate support.",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/",
@@ -27,6 +27,7 @@
27
27
  },
28
28
  "exports": {
29
29
  ".": "./components/index.ts",
30
+ "./commerce": "./commerce/index.ts",
30
31
  "./root-layout": "./root-layout/index.tsx",
31
32
  "./server-actions": "./server-actions/index.ts",
32
33
  "./next": "./next/index.ts",
@@ -36,6 +37,9 @@
36
37
  "./conf": "./conf/index.ts"
37
38
  },
38
39
  "dependencies": {
40
+ "@hanzo/auth": "2.4.6",
41
+ "@hanzo/commerce": "7.0.1",
42
+ "@hanzo/ui": "3.7.0",
39
43
  "@next/third-parties": "^14.1.0",
40
44
  "cookies-next": "^4.1.1",
41
45
  "date-fns": "^3.6.0",
@@ -44,9 +48,6 @@
44
48
  "react-social-icons": "^6.4.0"
45
49
  },
46
50
  "peerDependencies": {
47
- "@hanzo/auth": "2.4.6",
48
- "@hanzo/commerce": "7.0.0",
49
- "@hanzo/ui": "3.7.0",
50
51
  "@hookform/resolvers": "^3.3.2",
51
52
  "lucide-react": "^0.344.0",
52
53
  "next": "14.1.3",
@@ -12,9 +12,10 @@ import { FacebookPixelHead } from '../next/analytics/pixel-analytics'
12
12
 
13
13
  import { AuthListener, ChatWidget, Header, Scripts } from '../components'
14
14
  import BuyDrawer from '../components/commerce/buy-drawer'
15
+ import CheckoutWidget from '../components/commerce/checkout-widget'
15
16
 
16
17
  import { selectionUISpecifiers } from '../conf'
17
- import type SiteDef from '../site-def/site-def'
18
+ import type SiteDef from '../types/site-def'
18
19
 
19
20
  import '../style/lux-global.css'
20
21
  import '../style/cart-animation.css'
@@ -58,7 +59,6 @@ const RootLayout: React.FC<PropsWithChildren & {
58
59
  }) => {
59
60
 
60
61
  const currentUser = await getUserServerSide()
61
- const usingCommerce = siteDef?.ext?.commerce && siteDef.ext.commerce.rootNode && siteDef.ext.commerce.families
62
62
 
63
63
  const Guts: React.FC = () => (<>
64
64
  {showHeader && <Header siteDef={siteDef}/>}
@@ -68,7 +68,7 @@ const RootLayout: React.FC<PropsWithChildren & {
68
68
  title='LUX'
69
69
  subtitle='AI'
70
70
  chatbotUrl='https://lux.chat/iframe'
71
- suggestedQuestions={siteDef.ext?.chatBot?.suggestedQuestions ?? []}
71
+ suggestedQuestions={siteDef.chatbot?.suggestedQuestions ?? []}
72
72
  />
73
73
  )}
74
74
  </>)
@@ -92,15 +92,16 @@ const RootLayout: React.FC<PropsWithChildren & {
92
92
  }}>
93
93
  <Scripts/>
94
94
  <AuthServiceProvider user={currentUser} conf={{} as AuthServiceConf}>
95
- {usingCommerce ? (
95
+ {siteDef?.commerce ? (
96
96
  <CommerceProvider
97
- rootNode={siteDef.ext.commerce.rootNode}
98
- families={siteDef.ext.commerce.families}
99
- options={siteDef.ext.commerce.options}
97
+ rootNode={siteDef.commerce!.rootNode}
98
+ families={siteDef.commerce!.families}
99
+ options={siteDef.commerce!.options}
100
100
  uiSpecs={selectionUISpecifiers}
101
101
  >
102
102
  <Guts />
103
103
  <BuyDrawer />
104
+ <CheckoutWidget />
104
105
  </CommerceProvider>
105
106
  ) : (
106
107
  <Guts />
package/site-def/index.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export * as footer from './footer' // so footer.standard, footer.community, etc
2
2
  export { default as mainNav } from './main-nav'
3
- export type { default as SiteDef } from './site-def'
3
+ export type { SiteDef } from '../types'
package/tsconfig.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "extends": "../tsconfig.hanzo-modules.base.json",
2
+ "extends": "../tsconfig.lux.base.json",
3
3
  "include": [
4
4
  "**/*.ts",
5
5
  "**/*.tsx",
@@ -0,0 +1,7 @@
1
+ import type ChatbotSuggestQuestion from './chatbot-suggested-question'
2
+
3
+ interface ChatbotConfig {
4
+ suggestedQuestions?: ChatbotSuggestQuestion[]
5
+ }
6
+
7
+ export { type ChatbotConfig as default }
@@ -0,0 +1,7 @@
1
+ interface ChatbotSuggestedQuestion {
2
+ heading: string
3
+ message: string
4
+ icon?: string
5
+ }
6
+
7
+ export { type ChatbotSuggestedQuestion as default }
@@ -0,0 +1,10 @@
1
+ import type { ServiceOptions } from '@hanzo/commerce'
2
+ import type { CategoryNode, Family } from '@hanzo/commerce/types'
3
+
4
+ interface CommerceConfig {
5
+ families: Family[]
6
+ rootNode: CategoryNode
7
+ options?: ServiceOptions
8
+ }
9
+
10
+ export { type CommerceConfig as default }
package/types/index.ts CHANGED
@@ -1 +1,5 @@
1
- export type { ContactInfo, ContactInfoFields } from './contact-info'
1
+ export type { ContactInfo, ContactInfoFields } from './contact-info'
2
+ export type { default as SiteDef } from './site-def'
3
+ export type { default as ChatbotSuggestedQuestion } from './chatbot-suggested-question'
4
+ export type { default as ChatbotConfig } from './chatbot-config'
5
+ export type { default as CommerceConfig } from './commerce-config'
@@ -1,9 +1,15 @@
1
1
  import React from 'react'
2
+
2
3
  import type { LinkDef } from '@hanzo/ui/types'
3
4
 
5
+ import type CommerceConfig from './commerce-config'
6
+ import type ChatbotConfig from './chatbot-config'
7
+
4
8
  interface SiteDef {
9
+
5
10
  /** url of this site. All nav links in the system will show it in 'current' state */
6
11
  currentAs?: string
12
+
7
13
  nav: {
8
14
  /** common elements (will auto-select currentAs if it's provide) */
9
15
  /** optional feature element. right-most after 'elements' (any min-w is ignored) */
@@ -25,13 +31,15 @@ interface SiteDef {
25
31
  */
26
32
  footer: LinkDef[][]
27
33
 
28
- /** optional override of default 'above copyright' horizantal links */
29
- /** default (undefined or absent): @ui/sideDef/footer/legal are rendered */
30
- /** [] renders nothing above the copyright */
34
+ /**
35
+ * optional override of default 'above copyright' horizantal links
36
+ default (undefined / absent): links in side-def/footer/legal are rendered
37
+ [] renders nothing above the copyright
38
+ */
31
39
  aboveCopyright?: LinkDef[]
32
40
 
33
- /** any site-specific stuff we'd like access to (link urls, etc) */
34
- ext?: any
41
+ commerce?: CommerceConfig
42
+ chatbot?: ChatbotConfig
35
43
  }
36
44
 
37
- export { type SiteDef as default }
45
+ export { type SiteDef as default }
@@ -1,4 +0,0 @@
1
- <svg viewBox="0 0 32 40" >
2
- <path d="M27.724,27.85a3.4,3.4,0,0,0,.825-2.672l-1.8-14.4A3.405,3.405,0,0,0,23.375,7.8h-.521a5.173,5.173,0,0,0-10.32,0H3.894a1,1,0,0,0,0,2H8.929a3.316,3.316,0,0,0-.29.978l-.382,3.055a.889.889,0,0,0-.163-.033h-3a1,1,0,0,0,0,2H8.011L6.839,25.178A3.4,3.4,0,0,0,10.212,29H25.175A3.4,3.4,0,0,0,27.724,27.85ZM17.694,5a3.2,3.2,0,0,1,3.16,2.8h-6.32A3.194,3.194,0,0,1,17.694,5ZM9.163,26.526a1.384,1.384,0,0,1-.34-1.1l1.8-14.4A1.4,1.4,0,0,1,12.013,9.8h.481V13a1,1,0,0,0,2,0V9.8h6.4V13a1,1,0,0,0,2,0V9.8h.481a1.4,1.4,0,0,1,1.39,1.226l1.8,14.4A1.4,1.4,0,0,1,25.175,27H10.212A1.38,1.38,0,0,1,9.163,26.526Z"/>
3
- <path d="M7.894,11.8a1,1,0,0,0-1-1h-3a1,1,0,0,0,0,2h3A1,1,0,0,0,7.894,11.8Z"/>
4
- </svg>