@luxfi/core 4.4.14 → 4.4.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. package/components/access-code-input.tsx +71 -71
  2. package/components/auth/auth-listener.tsx +29 -29
  3. package/components/auth/auth-token/clear-auth-token.tsx +12 -12
  4. package/components/auth/auth-token/set-auth-token.tsx +16 -16
  5. package/components/auth/common-auth-domains.ts +16 -16
  6. package/components/auth/login-panel.tsx +103 -98
  7. package/components/chat-widget.tsx +77 -77
  8. package/components/commerce/bag-button.tsx +67 -67
  9. package/components/commerce/checkout-panel/close-button.tsx +26 -26
  10. package/components/commerce/checkout-panel/dt-bag-carousel.tsx +36 -36
  11. package/components/commerce/checkout-panel/dt-checkout-panel.tsx +66 -66
  12. package/components/commerce/checkout-panel/index.tsx +123 -123
  13. package/components/commerce/checkout-panel/links-row.tsx +21 -21
  14. package/components/commerce/checkout-panel/mb-checkout-panel.tsx +55 -55
  15. package/components/commerce/checkout-panel/steps-indicator.tsx +39 -39
  16. package/components/commerce/checkout-panel/thank-you.tsx +18 -18
  17. package/components/commerce/desktop-bag-popup.tsx +78 -78
  18. package/components/commerce/mobile-bag-drawer.tsx +51 -51
  19. package/components/commerce/mobile-menu-toggle-button.tsx +35 -35
  20. package/components/commerce/mobile-nav-menu.tsx +64 -64
  21. package/components/contact-dialog/contact-form.tsx +112 -112
  22. package/components/contact-dialog/disclaimer.tsx +13 -13
  23. package/components/contact-dialog/index.tsx +64 -64
  24. package/components/copyright.tsx +21 -21
  25. package/components/footer.tsx +77 -77
  26. package/components/header/desktop.tsx +54 -54
  27. package/components/header/index.tsx +26 -26
  28. package/components/header/mobile.tsx +161 -161
  29. package/components/header/theme-toggle.tsx +26 -26
  30. package/components/icons/bag-icon.tsx +10 -10
  31. package/components/icons/github.tsx +14 -14
  32. package/components/icons/index.tsx +35 -35
  33. package/components/icons/lux-logo.tsx +10 -10
  34. package/components/icons/secure-delivery.tsx +13 -13
  35. package/components/icons/social-icon.tsx +35 -35
  36. package/components/icons/social-svg.css +3 -3
  37. package/components/icons/youtube-logo.tsx +59 -59
  38. package/components/index.ts +27 -27
  39. package/components/logo.tsx +81 -81
  40. package/components/mini-chart/index.tsx +7 -7
  41. package/components/mini-chart/mini-chart-props.ts +43 -43
  42. package/components/mini-chart/mini-chart.tsx +85 -85
  43. package/components/mini-chart/wrapper.tsx +23 -23
  44. package/components/not-found/index.tsx +27 -27
  45. package/components/not-found/not-found-content.mdx +5 -5
  46. package/components/root-layout.tsx +71 -71
  47. package/components/scripts.tsx +23 -23
  48. package/conf/index.ts +50 -50
  49. package/environment.d.ts +5 -5
  50. package/next/analytics/fpixel.ts +15 -15
  51. package/next/analytics/google-analytics.ts +13 -13
  52. package/next/analytics/index.ts +3 -3
  53. package/next/analytics/pixel-analytics.tsx +54 -54
  54. package/next/determine-device-mw.ts +16 -16
  55. package/next/font/get-app-router-font-classes.ts +12 -12
  56. package/next/font/load-and-return-lux-next-fonts-on-import.ts +68 -68
  57. package/next/font/next-font-desc.ts +27 -27
  58. package/next/font/pages-router-font-vars.tsx +18 -18
  59. package/next/head-metadata/from-next/metadata-types.ts +158 -158
  60. package/next/head-metadata/from-next/opengraph-types.ts +267 -267
  61. package/next/head-metadata/from-next/twitter-types.ts +92 -92
  62. package/next/head-metadata/index.tsx +208 -208
  63. package/package.json +73 -73
  64. package/server-actions/firebase-app.ts +14 -14
  65. package/server-actions/index.ts +5 -5
  66. package/server-actions/store-contact.ts +51 -51
  67. package/site-def/footer/community.tsx +67 -67
  68. package/site-def/footer/company.ts +37 -37
  69. package/site-def/footer/ecosystem.ts +37 -37
  70. package/site-def/footer/index.tsx +26 -26
  71. package/site-def/footer/legal.ts +28 -28
  72. package/site-def/footer/network.ts +45 -45
  73. package/site-def/footer/svg/warpcast-logo.svg +11 -11
  74. package/site-def/index.ts +2 -2
  75. package/site-def/main-nav.ts +35 -35
  76. package/site-def/site-def.ts +36 -36
  77. package/style/lux-colors.css +85 -85
  78. package/style/lux-global.css +30 -30
  79. package/tailwind/fontFamily.tailwind.lux.ts +18 -18
  80. package/tailwind/index.ts +2 -2
  81. package/tailwind/lux-tw-fonts.ts +39 -39
  82. package/tailwind/tailwind.config.lux-preset.ts +10 -10
  83. package/tsconfig.json +10 -10
  84. package/types/contact-info.ts +10 -10
@@ -1,71 +1,71 @@
1
- 'use client'
2
-
3
- import { useState } from 'react'
4
- import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from '@hanzo/ui/primitives'
5
- import { cn } from '@hanzo/ui/util'
6
-
7
- const AccessCodeInput: React.FC<{
8
- onSuccess?: () => void
9
- onFail?: () => void
10
- validCodes?: string[]
11
- className?: string
12
- }> = ({
13
- onSuccess,
14
- onFail,
15
- validCodes,
16
- className
17
- }) => {
18
- const [status, setStatus] = useState<'valid' | 'invalid' | 'checking' | undefined>()
19
-
20
- const checkAccessCode = (code: string) => {
21
- setStatus(undefined)
22
- if (code.length === 6) {
23
- setStatus('checking')
24
- setTimeout(() => {
25
- if (validCodes?.includes(code) && onSuccess) {
26
- setStatus('valid')
27
- onSuccess()
28
- }
29
- else {
30
- setStatus('invalid')
31
- onFail && onFail()
32
- }
33
- }, 1000)
34
- }
35
- }
36
-
37
- return (
38
- <div className={cn('flex flex-col gap-2 mx-auto w-full text-center items-center', className)}>
39
- <InputOTP
40
- className='mx-auto'
41
- maxLength={6}
42
- onInput={(event) => checkAccessCode((event.target as HTMLInputElement).value)}
43
- render={({ slots }) => (
44
- <>
45
- <InputOTPGroup>
46
- {slots.slice(0, 3).map((slot, index) => (
47
- <InputOTPSlot key={index} {...slot} />
48
- ))}{" "}
49
- </InputOTPGroup>
50
- <InputOTPSeparator />
51
- <InputOTPGroup>
52
- {slots.slice(3).map((slot, index) => (
53
- <InputOTPSlot key={index + 3} {...slot} />
54
- ))}
55
- </InputOTPGroup>
56
- </>
57
- )}
58
- />
59
- <p className='h-[3rem]'>
60
- {
61
- status === 'checking' ? 'Checking access code...' :
62
- status === 'invalid' ? <span className='text-destructive'>Invalid access code!</span> :
63
- status === 'valid' ? <span>Access code is valid! Redirecting...</span> :
64
- null
65
- }
66
- </p>
67
- </div>
68
- )
69
- }
70
-
71
- export default AccessCodeInput
1
+ 'use client'
2
+
3
+ import { useState } from 'react'
4
+ import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from '@hanzo/ui/primitives'
5
+ import { cn } from '@hanzo/ui/util'
6
+
7
+ const AccessCodeInput: React.FC<{
8
+ onSuccess?: () => void
9
+ onFail?: () => void
10
+ validCodes?: string[]
11
+ className?: string
12
+ }> = ({
13
+ onSuccess,
14
+ onFail,
15
+ validCodes,
16
+ className
17
+ }) => {
18
+ const [status, setStatus] = useState<'valid' | 'invalid' | 'checking' | undefined>()
19
+
20
+ const checkAccessCode = (code: string) => {
21
+ setStatus(undefined)
22
+ if (code.length === 6) {
23
+ setStatus('checking')
24
+ setTimeout(() => {
25
+ if (validCodes?.includes(code) && onSuccess) {
26
+ setStatus('valid')
27
+ onSuccess()
28
+ }
29
+ else {
30
+ setStatus('invalid')
31
+ onFail && onFail()
32
+ }
33
+ }, 1000)
34
+ }
35
+ }
36
+
37
+ return (
38
+ <div className={cn('flex flex-col gap-2 mx-auto w-full text-center items-center', className)}>
39
+ <InputOTP
40
+ className='mx-auto'
41
+ maxLength={6}
42
+ onInput={(event) => checkAccessCode((event.target as HTMLInputElement).value)}
43
+ render={({ slots }) => (
44
+ <>
45
+ <InputOTPGroup>
46
+ {slots.slice(0, 3).map((slot, index) => (
47
+ <InputOTPSlot key={index} {...slot} />
48
+ ))}{" "}
49
+ </InputOTPGroup>
50
+ <InputOTPSeparator />
51
+ <InputOTPGroup>
52
+ {slots.slice(3).map((slot, index) => (
53
+ <InputOTPSlot key={index + 3} {...slot} />
54
+ ))}
55
+ </InputOTPGroup>
56
+ </>
57
+ )}
58
+ />
59
+ <p className='h-[3rem]'>
60
+ {
61
+ status === 'checking' ? 'Checking access code...' :
62
+ status === 'invalid' ? <span className='text-destructive'>Invalid access code!</span> :
63
+ status === 'valid' ? <span>Access code is valid! Redirecting...</span> :
64
+ null
65
+ }
66
+ </p>
67
+ </div>
68
+ )
69
+ }
70
+
71
+ export default AccessCodeInput
@@ -1,29 +1,29 @@
1
- 'use client'
2
-
3
- import { useEffect } from 'react'
4
- import { useAuth } from '@hanzo/auth/service'
5
- import { getCookie } from 'cookies-next'
6
-
7
- const AuthListener = () => {
8
- const auth = useAuth()
9
-
10
- useEffect(() => {
11
- fetch(`${process.env.NEXT_PUBLIC_LOGIN_SITE_URL}/api/auth/get-auth-token`, {
12
- method: 'GET',
13
- credentials: 'include',
14
- })
15
- .then(response => response.json())
16
- .then((data: any) => {
17
- const token = getCookie('auth-token')
18
- console.log(data)
19
- console.log(token)
20
- if (!!token) {
21
- auth.loginWithCustomToken(token)
22
- }
23
- })
24
- }, [auth])
25
-
26
- return ( <></> )
27
- }
28
-
29
- export default AuthListener
1
+ 'use client'
2
+
3
+ import { useEffect } from 'react'
4
+ import { useAuth } from '@hanzo/auth/service'
5
+ import { getCookie } from 'cookies-next'
6
+
7
+ const AuthListener = () => {
8
+ const auth = useAuth()
9
+
10
+ useEffect(() => {
11
+ fetch(`${process.env.NEXT_PUBLIC_LOGIN_SITE_URL}/api/auth/get-auth-token`, {
12
+ method: 'GET',
13
+ credentials: 'include',
14
+ })
15
+ .then(response => response.json())
16
+ .then((data: any) => {
17
+ const token = getCookie('auth-token')
18
+ console.log(data)
19
+ console.log(token)
20
+ if (!!token) {
21
+ auth.loginWithCustomToken(token)
22
+ }
23
+ })
24
+ }, [auth])
25
+
26
+ return ( <></> )
27
+ }
28
+
29
+ export default AuthListener
@@ -1,12 +1,12 @@
1
- import domains from '../common-auth-domains'
2
-
3
- const ClearAuthToken = () => {
4
- return (<>
5
- {domains.map(({url}) => (
6
- /* Clear auth-token cookie across all Lux domains */
7
- <img src={`${url}/api/auth/clear-auth-token`} className='absolute hidden'/>
8
- ))}
9
- </>)
10
- }
11
-
12
- export default ClearAuthToken
1
+ import domains from '../common-auth-domains'
2
+
3
+ const ClearAuthToken = () => {
4
+ return (<>
5
+ {domains.map(({url}) => (
6
+ /* Clear auth-token cookie across all Lux domains */
7
+ <img src={`${url}/api/auth/clear-auth-token`} className='absolute hidden'/>
8
+ ))}
9
+ </>)
10
+ }
11
+
12
+ export default ClearAuthToken
@@ -1,16 +1,16 @@
1
- import domains from '../common-auth-domains'
2
-
3
- const SetAuthToken: React.FC<{
4
- authToken: string
5
- }> = ({
6
- authToken,
7
- }) => {
8
- return (<>
9
- {!!authToken && domains.map(({url}) => (
10
- /* Set auth-token cookie across all Lux domains */
11
- <img src={`${url}/api/auth/set-auth-token?token=${authToken}`} className='absolute hidden'/>
12
- ))}
13
- </>)
14
- }
15
-
16
- export default SetAuthToken
1
+ import domains from '../common-auth-domains'
2
+
3
+ const SetAuthToken: React.FC<{
4
+ authToken: string
5
+ }> = ({
6
+ authToken,
7
+ }) => {
8
+ return (<>
9
+ {!!authToken && domains.map(({url}) => (
10
+ /* Set auth-token cookie across all Lux domains */
11
+ <img src={`${url}/api/auth/set-auth-token?token=${authToken}`} className='absolute hidden'/>
12
+ ))}
13
+ </>)
14
+ }
15
+
16
+ export default SetAuthToken
@@ -1,16 +1,16 @@
1
- const domains = [
2
- { id: 'lux.market', url: 'https://lux.market' },
3
- { id: 'lux.shop', url: 'https://lux.shop' },
4
- { id: 'lux.credit', url: 'https://lux.credit' },
5
- { id: 'lux.network', url: 'https://lux.network' },
6
- { id: 'wallet.lux.network', url: 'https://wallet.lux.network' },
7
- { id: 'safe.lux.network', url: 'https://safe.lux.network' },
8
- { id: 'lux.finance', url: 'https://lux.finance' },
9
- { id: 'lux.exchange', url: 'https://lux.exchange' },
10
- { id: 'lux.quest', url: 'https://lux.quest' },
11
- { id: 'lux.services', url: 'https://lux.services' },
12
- ]
13
-
14
- export {
15
- domains as default
16
- }
1
+ const domains = [
2
+ { id: 'lux.market', url: 'https://lux.market' },
3
+ { id: 'lux.shop', url: 'https://lux.shop' },
4
+ { id: 'lux.credit', url: 'https://lux.credit' },
5
+ { id: 'lux.network', url: 'https://lux.network' },
6
+ { id: 'wallet.lux.network', url: 'https://wallet.lux.network' },
7
+ { id: 'safe.lux.network', url: 'https://safe.lux.network' },
8
+ { id: 'lux.finance', url: 'https://lux.finance' },
9
+ { id: 'lux.exchange', url: 'https://lux.exchange' },
10
+ { id: 'lux.quest', url: 'https://lux.quest' },
11
+ { id: 'lux.services', url: 'https://lux.services' },
12
+ ]
13
+
14
+ export {
15
+ domains as default
16
+ }
@@ -1,98 +1,103 @@
1
- 'use client'
2
-
3
- import Link from 'next/link'
4
- import Autoplay from 'embla-carousel-autoplay'
5
- import { setCookie } from 'cookies-next'
6
-
7
- import { cn } from '@hanzo/ui/util'
8
- import { Button, Carousel, CarouselContent, CarouselItem } from '@hanzo/ui/primitives'
9
- import { LoginPanel as Login } from '@hanzo/auth/components'
10
-
11
- import { Logo } from '..'
12
- import LuxLogo from '../icons/lux-logo'
13
- import { legal } from '../../site-def/footer'
14
- import { useRouter } from 'next/navigation'
15
-
16
- const LoginPanel: React.FC<{
17
- close: () => void
18
- getStartedUrl?: string
19
- redirectUrl?: string
20
- className?: string
21
- reviews: { text: string, author: string, href: string }[]
22
- }> = ({
23
- close,
24
- getStartedUrl='/',
25
- redirectUrl,
26
- className='',
27
- reviews
28
- }) => {
29
- const router = useRouter()
30
-
31
- const termsOfServiceUrl = legal.find(({title}) => title === 'Terms and Conditions')?.href || ''
32
- const privacyPolicyUrl = legal.find(({title}) => title === 'Privacy Policy')?.href || ''
33
-
34
- const onLogin = (token: string) => {
35
- setCookie('auth-token', token, {
36
- expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30), // 30 days
37
- })
38
-
39
- redirectUrl && router.push(redirectUrl)
40
- }
41
-
42
- return (<>
43
- <div className={cn('grid grid-cols-1 md:grid-cols-2', className)}>
44
- <div className='hidden md:flex w-full h-full bg-level-1 flex-row items-end justify-end overflow-y-auto min-h-screen'>
45
- <div className='h-full w-full max-w-[750px] px-8 pt-0'>
46
- <div className='h-full w-full max-w-[550px] mx-auto flex flex-col justify-between min-h-screen py-10'>
47
- <Button
48
- variant='ghost'
49
- onClick={close}
50
- className='w-fit !min-w-0 p-2'
51
- >
52
- <Logo size='md' spanClassName='!cursor-pointer' layout='text-only'/>
53
- </Button>
54
- <Carousel
55
- options={{ align: 'center', loop: true }}
56
- className='w-full'
57
- plugins={[Autoplay({ delay: 5000, stopOnInteraction: true })]}
58
- >
59
- <CarouselContent>
60
- {reviews.map(({text, author, href}, index) => (
61
- <CarouselItem key={index}>
62
- <Link href={href} className='flex flex-col gap-3 cursor-pointer'>
63
- <p>“{text}“</p>
64
- <p className='text-sm'>{author}</p>
65
- </Link>
66
- </CarouselItem>
67
- ))}
68
- </CarouselContent>
69
- </Carousel>
70
- </div>
71
- </div>
72
- </div>
73
- <div className='w-full h-full bg-background flex flex-row items-center'>
74
- <div className='w-full max-w-[750px] relative flex flex-col items-center px-8 pt-0 text-center'>
75
- <div className='relative h-full w-full max-w-[400px] mx-auto flex flex-col gap-4 items-center py-10'>
76
- <Button
77
- variant='ghost'
78
- onClick={close}
79
- className='block md:hidden absolute rounded-full p-2 left-0 h-auto hover:bg-background'
80
- >
81
- <LuxLogo className='w-5 h-5'/>
82
- </Button>
83
- <Login
84
- getStartedUrl={getStartedUrl}
85
- redirectUrl={redirectUrl}
86
- className='w-full max-w-sm'
87
- termsOfServiceUrl={termsOfServiceUrl}
88
- privacyPolicyUrl={privacyPolicyUrl}
89
- onLoginChanged={onLogin}
90
- />
91
- </div>
92
- </div>
93
- </div>
94
- </div>
95
- </>)
96
- }
97
-
98
- export default LoginPanel
1
+ 'use client'
2
+
3
+ import Link from 'next/link'
4
+ import Autoplay from 'embla-carousel-autoplay'
5
+ import { setCookie } from 'cookies-next'
6
+
7
+ import { cn } from '@hanzo/ui/util'
8
+ import { Button, Carousel, CarouselContent, CarouselItem } from '@hanzo/ui/primitives'
9
+ import { LoginPanel as Login } from '@hanzo/auth/components'
10
+
11
+ import { Logo } from '..'
12
+ import LuxLogo from '../icons/lux-logo'
13
+ import { legal } from '../../site-def/footer'
14
+ import { useRouter } from 'next/navigation'
15
+
16
+ const LoginPanel: React.FC<{
17
+ close: () => void
18
+ getStartedUrl?: string
19
+ redirectUrl?: string
20
+ className?: string
21
+ reviews: { text: string, author: string, href: string }[]
22
+ }> = ({
23
+ close,
24
+ getStartedUrl='/',
25
+ redirectUrl,
26
+ className='',
27
+ reviews
28
+ }) => {
29
+ const router = useRouter()
30
+
31
+ const termsOfServiceUrl = legal.find(({title}) => title === 'Terms and Conditions')?.href || ''
32
+ const privacyPolicyUrl = legal.find(({title}) => title === 'Privacy Policy')?.href || ''
33
+
34
+ const onLogin = (token: string) => {
35
+ setCookie('auth-token', token, {
36
+ domain: '.lux.services',
37
+ path: '/',
38
+ sameSite: 'None',
39
+ secure: true,
40
+ httpOnly: true,
41
+ expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30), // 30 days
42
+ })
43
+
44
+ redirectUrl && router.push(redirectUrl)
45
+ }
46
+
47
+ return (<>
48
+ <div className={cn('grid grid-cols-1 md:grid-cols-2', className)}>
49
+ <div className='hidden md:flex w-full h-full bg-level-1 flex-row items-end justify-end overflow-y-auto min-h-screen'>
50
+ <div className='h-full w-full max-w-[750px] px-8 pt-0'>
51
+ <div className='h-full w-full max-w-[550px] mx-auto flex flex-col justify-between min-h-screen py-10'>
52
+ <Button
53
+ variant='ghost'
54
+ onClick={close}
55
+ className='w-fit !min-w-0 p-2'
56
+ >
57
+ <Logo size='md' spanClassName='!cursor-pointer' layout='text-only'/>
58
+ </Button>
59
+ <Carousel
60
+ options={{ align: 'center', loop: true }}
61
+ className='w-full'
62
+ plugins={[Autoplay({ delay: 5000, stopOnInteraction: true })]}
63
+ >
64
+ <CarouselContent>
65
+ {reviews.map(({text, author, href}, index) => (
66
+ <CarouselItem key={index}>
67
+ <Link href={href} className='flex flex-col gap-3 cursor-pointer'>
68
+ <p>“{text}“</p>
69
+ <p className='text-sm'>{author}</p>
70
+ </Link>
71
+ </CarouselItem>
72
+ ))}
73
+ </CarouselContent>
74
+ </Carousel>
75
+ </div>
76
+ </div>
77
+ </div>
78
+ <div className='w-full h-full bg-background flex flex-row items-center'>
79
+ <div className='w-full max-w-[750px] relative flex flex-col items-center px-8 pt-0 text-center'>
80
+ <div className='relative h-full w-full max-w-[400px] mx-auto flex flex-col gap-4 items-center py-10'>
81
+ <Button
82
+ variant='ghost'
83
+ onClick={close}
84
+ className='block md:hidden absolute rounded-full p-2 left-0 h-auto hover:bg-background'
85
+ >
86
+ <LuxLogo className='w-5 h-5'/>
87
+ </Button>
88
+ <Login
89
+ getStartedUrl={getStartedUrl}
90
+ redirectUrl={redirectUrl}
91
+ className='w-full max-w-sm'
92
+ termsOfServiceUrl={termsOfServiceUrl}
93
+ privacyPolicyUrl={privacyPolicyUrl}
94
+ onLoginChanged={onLogin}
95
+ />
96
+ </div>
97
+ </div>
98
+ </div>
99
+ </div>
100
+ </>)
101
+ }
102
+
103
+ export default LoginPanel
@@ -1,77 +1,77 @@
1
- 'use client'
2
- import React from 'react'
3
-
4
- import { Button, Card } from '@hanzo/ui/primitives'
5
-
6
- import LuxLogo from './icons/lux-logo'
7
- import { cn } from '@hanzo/ui/util'
8
-
9
- const ChatWidget: React.FC<{
10
- title: string,
11
- chatbotUrl: string,
12
- subtitle?: string,
13
- question?: string,
14
- /*
15
- Currently supports these icons from remix icons (https://remixicon.com/):
16
- GlobalLineIcon,
17
- ShieldFlashLineIcon,
18
- BankCardLineIcon,
19
- GroupLineIcon,
20
- QuestionnaireLineIcon
21
- */
22
- suggestedQuestions?: { heading: string, message: string, icon?: string }[]
23
- }> = ({
24
- title,
25
- chatbotUrl,
26
- subtitle,
27
- question,
28
- suggestedQuestions
29
- }) => {
30
-
31
- const [showChatbot, setShowChatbot] = React.useState<boolean>(false)
32
-
33
- const onClick = () => { setShowChatbot(!showChatbot) }
34
-
35
- const searchParams = new URLSearchParams()
36
- if (question) {
37
- searchParams.append('question', question)
38
- }
39
- if (suggestedQuestions) {
40
- searchParams.append('sQuestions', suggestedQuestions.map(({ message }) => message).join(','))
41
- searchParams.append('sHeadings', suggestedQuestions.map(({ heading }) => heading).join(','))
42
- searchParams.append('sIcons', suggestedQuestions.map(({ icon }) => icon).join(','))
43
- }
44
-
45
- const iframeSrc = `${chatbotUrl}?${searchParams.toString()}`
46
-
47
- return (<>
48
- <div className={
49
- 'fixed bottom-0 sm:bottom-16 right-0 w-full h-full ' +
50
- 'sm:max-w-[400px] sm:max-h-[550px] sm:px-4 z-above-floating ' +
51
- (showChatbot ? 'flex' : 'hidden')
52
- }>
53
- <Card className='flex flex-col h-full w-full'>
54
- <div className='flex px-4 py-2 h-12 bg-level-0 items-center justify-between'>
55
- <h3 className='font-semibold font-heading'>{title} <span className='opacity-60'>{subtitle}</span></h3>
56
- <Button onClick={onClick} variant='link' size='icon' className='w-fit sm:hidden'>
57
- <LuxLogo width={24} height={24}/>
58
- </Button>
59
- </div>
60
- <iframe src={iframeSrc} className='h-full' />
61
- </Card>
62
- </div>
63
-
64
- <LuxLogo
65
- width={28}
66
- height={28}
67
- onClick={onClick}
68
- className={cn(
69
- 'fixed bottom-5 right-5 z-floating transition-all cursor-pointer hover:drop-shadow-[0_2px_6px_rgba(255,255,255,1)]',
70
- showChatbot ? 'rotate-180' : ''
71
- )}
72
- strokeWidth={1}
73
- />
74
- </>)
75
- }
76
-
77
- export default ChatWidget
1
+ 'use client'
2
+ import React from 'react'
3
+
4
+ import { Button, Card } from '@hanzo/ui/primitives'
5
+
6
+ import LuxLogo from './icons/lux-logo'
7
+ import { cn } from '@hanzo/ui/util'
8
+
9
+ const ChatWidget: React.FC<{
10
+ title: string,
11
+ chatbotUrl: string,
12
+ subtitle?: string,
13
+ question?: string,
14
+ /*
15
+ Currently supports these icons from remix icons (https://remixicon.com/):
16
+ GlobalLineIcon,
17
+ ShieldFlashLineIcon,
18
+ BankCardLineIcon,
19
+ GroupLineIcon,
20
+ QuestionnaireLineIcon
21
+ */
22
+ suggestedQuestions?: { heading: string, message: string, icon?: string }[]
23
+ }> = ({
24
+ title,
25
+ chatbotUrl,
26
+ subtitle,
27
+ question,
28
+ suggestedQuestions
29
+ }) => {
30
+
31
+ const [showChatbot, setShowChatbot] = React.useState<boolean>(false)
32
+
33
+ const onClick = () => { setShowChatbot(!showChatbot) }
34
+
35
+ const searchParams = new URLSearchParams()
36
+ if (question) {
37
+ searchParams.append('question', question)
38
+ }
39
+ if (suggestedQuestions) {
40
+ searchParams.append('sQuestions', suggestedQuestions.map(({ message }) => message).join(','))
41
+ searchParams.append('sHeadings', suggestedQuestions.map(({ heading }) => heading).join(','))
42
+ searchParams.append('sIcons', suggestedQuestions.map(({ icon }) => icon).join(','))
43
+ }
44
+
45
+ const iframeSrc = `${chatbotUrl}?${searchParams.toString()}`
46
+
47
+ return (<>
48
+ <div className={
49
+ 'fixed bottom-0 sm:bottom-16 right-0 w-full h-full ' +
50
+ 'sm:max-w-[400px] sm:max-h-[550px] sm:px-4 z-above-floating ' +
51
+ (showChatbot ? 'flex' : 'hidden')
52
+ }>
53
+ <Card className='flex flex-col h-full w-full'>
54
+ <div className='flex px-4 py-2 h-12 bg-level-0 items-center justify-between'>
55
+ <h3 className='font-semibold font-heading'>{title} <span className='opacity-60'>{subtitle}</span></h3>
56
+ <Button onClick={onClick} variant='link' size='icon' className='w-fit sm:hidden'>
57
+ <LuxLogo width={24} height={24}/>
58
+ </Button>
59
+ </div>
60
+ <iframe src={iframeSrc} className='h-full' />
61
+ </Card>
62
+ </div>
63
+
64
+ <LuxLogo
65
+ width={28}
66
+ height={28}
67
+ onClick={onClick}
68
+ className={cn(
69
+ 'fixed bottom-5 right-5 z-floating transition-all cursor-pointer hover:drop-shadow-[0_2px_6px_rgba(255,255,255,1)]',
70
+ showChatbot ? 'rotate-180' : ''
71
+ )}
72
+ strokeWidth={1}
73
+ />
74
+ </>)
75
+ }
76
+
77
+ export default ChatWidget