Skip to content

Commit 3ba36de

Browse files
feat: subscribe for updates module (#910)
* feat: subscribe for updates module * fix: resolve comments * fix: lint error temporarily
1 parent f8da540 commit 3ba36de

File tree

7 files changed

+163
-22
lines changed

7 files changed

+163
-22
lines changed

sdks/js/packages/core/react/components/onboarding/onboarding.module.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,10 @@
3737
.textFieldCode {
3838
text-align: center;
3939
}
40+
41+
.subscribeContainer {
42+
width: 100%;
43+
max-width: 496px;
44+
padding: var(--pd-32);
45+
box-sizing: border-box;
46+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
'use client';
2+
3+
import { yupResolver } from '@hookform/resolvers/yup';
4+
import { Button, Flex, Text, Switch } from '@raystack/apsara/v1';
5+
import { Controller, useForm } from 'react-hook-form';
6+
import * as yup from 'yup';
7+
import { Container } from '../Container';
8+
import styles from './onboarding.module.css';
9+
import { PREFERENCE_OPTIONS } from '~/react/utils/constants';
10+
import { usePreferences } from '~/react/hooks/usePreferences';
11+
import { ReactNode } from '@tanstack/react-router';
12+
import { Header } from '../Header';
13+
import Skeleton from 'react-loading-skeleton';
14+
15+
const schema = yup.object({
16+
[PREFERENCE_OPTIONS.NEWSLETTER]: yup.boolean().optional()
17+
});
18+
19+
type FormData = yup.InferType<typeof schema>;
20+
21+
type SubscribeProps = {
22+
logo?: ReactNode;
23+
title?: string;
24+
preferenceTitle?: string;
25+
preferenceDescription?: string;
26+
onSubmit?: (data: FormData) => void;
27+
};
28+
29+
export const Subscribe = ({
30+
logo,
31+
title = 'Subscribe for updates',
32+
preferenceTitle = 'Updates, News & Events',
33+
preferenceDescription = 'Stay informed on new features, improvements, and key updates',
34+
onSubmit
35+
}: SubscribeProps) => {
36+
const { preferences, isFetching, updatePreferences } = usePreferences();
37+
38+
const newsletterValue =
39+
preferences?.[PREFERENCE_OPTIONS.NEWSLETTER]?.value === 'true';
40+
41+
const {
42+
control,
43+
handleSubmit,
44+
formState: { isSubmitting }
45+
} = useForm<FormData>({
46+
values: {
47+
[PREFERENCE_OPTIONS.NEWSLETTER]: newsletterValue
48+
},
49+
resolver: yupResolver(schema)
50+
});
51+
52+
async function onFormSubmit(data: FormData) {
53+
return updatePreferences([
54+
{
55+
name: PREFERENCE_OPTIONS.NEWSLETTER,
56+
value: String(data[PREFERENCE_OPTIONS.NEWSLETTER])
57+
}
58+
])
59+
.then(() => onSubmit?.(data))
60+
.catch(err => {
61+
console.error('frontier:sdk:: error during submit', err);
62+
});
63+
}
64+
return (
65+
<Flex direction="column" gap="large">
66+
<Header logo={logo} title={title} />
67+
<form onSubmit={handleSubmit(onFormSubmit)}>
68+
<Container
69+
className={styles.subscribeContainer}
70+
shadow="sm"
71+
radius="xs"
72+
>
73+
<Flex direction="column" gap="medium">
74+
<Flex justify="between">
75+
<Text size={6} weight={500}>
76+
{preferenceTitle}
77+
</Text>
78+
{isFetching ? (
79+
<Skeleton width={34} height={20} />
80+
) : (
81+
<Controller
82+
render={({ field: { value, onChange, ...field } }) => (
83+
<Switch
84+
checked={value}
85+
onCheckedChange={onChange}
86+
{...field}
87+
/>
88+
)}
89+
control={control}
90+
name={PREFERENCE_OPTIONS.NEWSLETTER}
91+
/>
92+
)}
93+
</Flex>
94+
<Text size={4} style={{ color: 'var(--foreground-muted)' }}>
95+
{preferenceDescription}
96+
</Text>
97+
</Flex>
98+
<Button
99+
style={{ width: '100%' }}
100+
type="submit"
101+
data-test-id="frontier-sdk-subscribe-btn"
102+
disabled={isFetching || isSubmitting}
103+
loading={isSubmitting}
104+
loaderText="Loading..."
105+
>
106+
Continue
107+
</Button>
108+
</Container>
109+
</form>
110+
</Flex>
111+
);
112+
};

sdks/js/packages/core/react/components/organization/preferences/index.tsx

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
'use client';
22

3-
import { useMemo } from 'react';
43
import { GearIcon, MoonIcon, SunIcon } from '@radix-ui/react-icons';
54
import { Image, Select, Separator, Box } from '@raystack/apsara';
65
import { Flex, useTheme, Text } from '@raystack/apsara/v1';
@@ -65,13 +64,8 @@ export default function UserPreferences() {
6564
const { preferences, isLoading, isFetching, updatePreferences } =
6665
usePreferences();
6766

68-
const newsletterValue = useMemo(
69-
() =>
70-
preferences.find(
71-
preference => preference.name === PREFERENCE_OPTIONS.NEWSLETTER
72-
)?.value ?? 'false',
73-
[preferences]
74-
);
67+
const newsletterValue =
68+
preferences?.[PREFERENCE_OPTIONS.NEWSLETTER]?.value ?? 'false';
7569

7670
return (
7771
<Flex direction="column" style={{ width: '100%' }}>

sdks/js/packages/core/react/hooks/usePreferences.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import { useState, useCallback, useEffect } from 'react';
22
import { V1Beta1Preference } from '~/api-client';
33
import { useFrontier } from '../contexts/FrontierContext';
44

5+
type Preferences = Record<string, V1Beta1Preference>;
6+
57
export interface UsePreferences {
6-
preferences: V1Beta1Preference[];
8+
preferences: Preferences;
79
isLoading: boolean;
810
isFetching: boolean;
911
status: 'idle' | 'fetching' | 'loading';
@@ -13,18 +15,25 @@ export interface UsePreferences {
1315
) => Promise<V1Beta1Preference[] | undefined>;
1416
}
1517

18+
function getFormattedData(preferences: V1Beta1Preference[] = []): Preferences {
19+
return preferences.reduce((acc: Preferences, preference) => {
20+
if (preference?.name) acc[preference.name] = preference;
21+
return acc;
22+
}, {});
23+
}
24+
1625
export function usePreferences(): UsePreferences {
1726
const { client } = useFrontier();
18-
const [preferences, setPreferences] = useState<V1Beta1Preference[]>([]);
19-
const [status, setStatus] = useState<UsePreferences['status']>('idle');
27+
const [preferences, setPreferences] = useState<Preferences>({});
28+
const [status, setStatus] = useState<UsePreferences['status']>('fetching');
2029

2130
const fetchPreferences = useCallback(async () => {
2231
try {
2332
setStatus('fetching');
2433
const response =
2534
await client?.frontierServiceListCurrentUserPreferences();
2635
const data = response?.data.preferences || [];
27-
setPreferences(data);
36+
setPreferences(getFormattedData(data));
2837
return data;
2938
} catch (err) {
3039
console.error(
@@ -46,7 +55,7 @@ export function usePreferences(): UsePreferences {
4655
bodies: preferences
4756
});
4857
const data = response?.data?.preferences ?? [];
49-
setPreferences(data);
58+
setPreferences(getFormattedData(data));
5059
return data;
5160
} catch (err) {
5261
console.error(

sdks/js/packages/core/react/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export { MagicLink } from './components/onboarding/magiclink';
99
export { MagicLinkVerify } from './components/onboarding/magiclink-verify';
1010
export { SignIn } from './components/onboarding/signin';
1111
export { SignUp } from './components/onboarding/signup';
12+
export { Subscribe } from './components/onboarding/subscribe';
1213
export { CreateOrganization } from './components/organization/create';
1314
export { OrganizationProfile } from './components/organization/profile';
1415
export { Window } from './components/window';
@@ -25,3 +26,5 @@ export type {
2526
FrontierClientOptions,
2627
FrontierClientBillingOptions
2728
} from '../shared/types';
29+
30+
export { PREFERENCE_OPTIONS } from './utils/constants';
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
'use client';
2+
import { Flex } from '@raystack/apsara';
3+
// import { Subscribe } from '@raystack/frontier/react';
4+
import React from 'react';
5+
6+
export default function SubscribeRoute() {
7+
return (
8+
<Flex
9+
justify="center"
10+
align="center"
11+
style={{ height: '100vh', width: '100vw' }}
12+
>
13+
{/* <Subscribe onSubmit={data => alert(JSON.stringify(data))} /> */}
14+
</Flex>
15+
);
16+
}

sdks/js/pnpm-lock.yaml

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)