Skip to content

Commit 8dd4a53

Browse files
bug: fixed stripe payment
1 parent b0bb67b commit 8dd4a53

File tree

15 files changed

+198
-17
lines changed

15 files changed

+198
-17
lines changed

actions/stripe/get-user-subscription.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { StripeSubscriptionPeriod } from '@prisma/client';
44
import { compareAsc, fromUnixTime } from 'date-fns';
5+
import { StatusCodes } from 'http-status-codes';
56

67
import { ONE_DAY_SEC } from '@/constants/common';
78
import { fetchCachedData } from '@/lib/cache';
@@ -62,8 +63,10 @@ export const getUserSubscription = async (userId = '', noCache = false) => {
6263
: await fetchCachedData(`user-subscription-[${userId}]`, callback, ONE_DAY_SEC);
6364

6465
return subscription;
65-
} catch (error) {
66-
console.error('[GET_USER_SUBSCRIPTION]', error);
66+
} catch (error: any) {
67+
if (error?.statusCode !== StatusCodes.NOT_FOUND) {
68+
console.error('[GET_USER_SUBSCRIPTION]', error);
69+
}
6770

6871
return null;
6972
}

app/(dashboard)/(routes)/settings/general/_components/advanced-options/advanced-options.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useTranslations } from 'next-intl';
66
import { useAppConfigStore } from '@/hooks/store/use-app-config-store';
77

88
import { ChristmasForm } from './christmas-form';
9+
import { NewTabCopilotForm } from './new-tab-copilot-form';
910
import { OtpForm } from './otp-form';
1011
import { PublicProfileForm } from './public-profile-form';
1112

@@ -23,6 +24,7 @@ export const AdvancedOptions = ({ initialData }: AdvancedOptionsProps) => {
2324
<p className="font-medium text-xl">{t('advancedOptions')}</p>
2425
<OtpForm initialData={initialData} />
2526
<PublicProfileForm initialData={initialData} />
27+
<NewTabCopilotForm initialData={initialData} />
2628
{config?.features?.christmas && <ChristmasForm initialData={initialData} />}
2729
</div>
2830
);

app/(dashboard)/(routes)/settings/general/_components/advanced-options/christmas-form.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
FormLabel,
1818
} from '@/components/ui/form';
1919
import { useToast } from '@/components/ui/use-toast';
20+
import { useUserSettingsStore } from '@/hooks/store/use-user-settings.store';
2021
import { fetcher } from '@/lib/fetcher';
2122

2223
type ChristmasFormFormProps = {
@@ -33,6 +34,10 @@ export const ChristmasForm = ({ initialData }: ChristmasFormFormProps) => {
3334
const { toast } = useToast();
3435
const router = useRouter();
3536

37+
const { setIsChristmasMode } = useUserSettingsStore((state) => ({
38+
setIsChristmasMode: state.setIsChristmasMode,
39+
}));
40+
3641
const form = useForm<z.infer<typeof formSchema>>({
3742
resolver: zodResolver(formSchema),
3843
defaultValues: {
@@ -46,6 +51,8 @@ export const ChristmasForm = ({ initialData }: ChristmasFormFormProps) => {
4651
try {
4752
await fetcher.patch(`/api/users/${initialData.id}`, { body: { settings: values } });
4853

54+
setIsChristmasMode(values.isChristmasMode);
55+
4956
router.refresh();
5057
} catch (error) {
5158
console.error('[CHRISTMAS_FORM]', error);
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
'use client';
2+
3+
import { zodResolver } from '@hookform/resolvers/zod';
4+
import { User, UserSettings } from '@prisma/client';
5+
import { useRouter } from 'next/navigation';
6+
import { useTranslations } from 'next-intl';
7+
import { useForm } from 'react-hook-form';
8+
import * as z from 'zod';
9+
10+
import { Switch } from '@/components/ui';
11+
import {
12+
Form,
13+
FormControl,
14+
FormDescription,
15+
FormField,
16+
FormItem,
17+
FormLabel,
18+
} from '@/components/ui/form';
19+
import { useToast } from '@/components/ui/use-toast';
20+
import { useUserSettingsStore } from '@/hooks/store/use-user-settings.store';
21+
import { fetcher } from '@/lib/fetcher';
22+
23+
type NewTabCopilotFormProps = {
24+
initialData: User & { settings: UserSettings | null };
25+
};
26+
27+
const formSchema = z.object({
28+
isCopilotInNewTab: z.boolean().default(false),
29+
});
30+
31+
export const NewTabCopilotForm = ({ initialData }: NewTabCopilotFormProps) => {
32+
const t = useTranslations('settings.new-tab-copilot');
33+
34+
const { toast } = useToast();
35+
const router = useRouter();
36+
37+
const { setIsCopilotInNewTab } = useUserSettingsStore((state) => ({
38+
setIsCopilotInNewTab: state.setIsCopilotInNewTab,
39+
}));
40+
41+
const form = useForm<z.infer<typeof formSchema>>({
42+
resolver: zodResolver(formSchema),
43+
defaultValues: {
44+
isCopilotInNewTab: Boolean(initialData?.settings?.isCopilotInNewTab),
45+
},
46+
});
47+
48+
const { isSubmitting, isValid } = form.formState;
49+
50+
const handleSubmit = async (values: z.infer<typeof formSchema>) => {
51+
try {
52+
await fetcher.patch(`/api/users/${initialData.id}`, { body: { settings: values } });
53+
54+
setIsCopilotInNewTab(values.isCopilotInNewTab);
55+
56+
router.refresh();
57+
} catch (error) {
58+
console.error('[NEW_TAB_COPILOT_FORM]', error);
59+
60+
toast({ isError: true });
61+
}
62+
};
63+
64+
return (
65+
<Form {...form}>
66+
<form onSubmit={form.handleSubmit(handleSubmit)}>
67+
<FormField
68+
control={form.control}
69+
name="isCopilotInNewTab"
70+
render={({ field }) => (
71+
<FormItem className="flex flex-row items-center justify-between space-x-3 space-y-0 rounded-md border p-4">
72+
<div className="space-y-0.5">
73+
<FormLabel>{t('title')}</FormLabel>
74+
<FormDescription className="text-xs">{t('body')}</FormDescription>
75+
</div>
76+
<FormControl>
77+
<Switch
78+
aria-readonly
79+
checked={field.value}
80+
disabled={!isValid || isSubmitting}
81+
onCheckedChange={field.onChange}
82+
type="submit"
83+
/>
84+
</FormControl>
85+
</FormItem>
86+
)}
87+
/>
88+
</form>
89+
</Form>
90+
);
91+
};

app/(dashboard)/(routes)/settings/general/_components/advanced-options/public-profile-form.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
FormLabel,
1818
} from '@/components/ui/form';
1919
import { useToast } from '@/components/ui/use-toast';
20+
import { useUserSettingsStore } from '@/hooks/store/use-user-settings.store';
2021
import { fetcher } from '@/lib/fetcher';
2122

2223
type PublicProfileFormProps = {
@@ -33,6 +34,10 @@ export const PublicProfileForm = ({ initialData }: PublicProfileFormProps) => {
3334
const { toast } = useToast();
3435
const router = useRouter();
3536

37+
const { setIsPublicProfile } = useUserSettingsStore((state) => ({
38+
setIsPublicProfile: state.setIsPublicProfile,
39+
}));
40+
3641
const form = useForm<z.infer<typeof formSchema>>({
3742
resolver: zodResolver(formSchema),
3843
defaultValues: {
@@ -45,7 +50,9 @@ export const PublicProfileForm = ({ initialData }: PublicProfileFormProps) => {
4550
const handleSubmit = async (values: z.infer<typeof formSchema>) => {
4651
try {
4752
await fetcher.patch(`/api/users/${initialData.id}`, { body: { settings: values } });
48-
toast({ title: t('visibilityUpdated') });
53+
54+
setIsPublicProfile(values.isPublicProfile);
55+
4956
router.refresh();
5057
} catch (error) {
5158
console.error('[PUBLIC_PROFILE_FORM]', error);

app/api/webhook/stripe/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export const POST = async (req: NextRequest) => {
7979
return null;
8080
})();
8181

82-
const transaction = await db.purchaseDetails.create({
82+
const transaction = await prisma.purchaseDetails.create({
8383
data: {
8484
city: session?.metadata?.city,
8585
country: session?.metadata?.country,

app/layout.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,11 @@ const notoSans = Noto_Sans({ subsets: ['latin'] });
3434

3535
const RootLayout = async ({ children }: RootLayoutProps) => {
3636
const appConfig = await getAppConfig();
37-
const locale = await getLocale();
37+
const userSettings = await getUserSettings();
3838
const messages = await getMessages();
39+
40+
const locale = await getLocale();
3941
const timeZone = await getTimeZone();
40-
const userSettings = appConfig?.features?.christmas ? await getUserSettings() : null;
4142

4243
return (
4344
<html lang={locale} suppressHydrationWarning>

app/providers.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ import Snowfall from 'react-snowfall';
1111
import { GetAppConfig } from '@/actions/configs/get-app-config';
1212
import { Toaster as ToastProvider } from '@/components/ui/toaster';
1313
import { useConfettiStore } from '@/hooks/store/use-confetti-store';
14+
import { useUserSettingsStore } from '@/hooks/store/use-user-settings.store';
1415
import { useAppConfig } from '@/hooks/use-app-config';
1516
import { useUserLocation } from '@/hooks/use-user-location';
17+
import { useUserSettings } from '@/hooks/use-user-settings';
1618
import { switchLanguage } from '@/lib/locale';
1719

1820
const AuthProvider = ({ children }: Readonly<{ children: React.ReactNode }>) => {
@@ -39,10 +41,12 @@ const ConfettiProvider = () => {
3941
);
4042
};
4143

42-
const ChristmasProvider = ({ userSettings }: { userSettings: UserSettings | null }) => {
43-
const isEnabled = userSettings?.isChristmasMode;
44+
const ChristmasProvider = () => {
45+
const { isChristmasMode } = useUserSettingsStore((state) => ({
46+
isChristmasMode: state.isChristmasMode,
47+
}));
4448

45-
if (!isEnabled) {
49+
if (!isChristmasMode) {
4650
return null;
4751
}
4852

@@ -82,6 +86,7 @@ export const Providers = ({
8286
const { config } = useAppConfig(appConfig);
8387

8488
useUserLocation();
89+
useUserSettings(userSettings);
8590

8691
useEffect(() => {
8792
switchLanguage(locale);
@@ -92,7 +97,7 @@ export const Providers = ({
9297
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
9398
<NextIntlClientProvider messages={messages} locale={locale} timeZone={timeZone}>
9499
<AuthProvider>
95-
{config?.features?.christmas && <ChristmasProvider userSettings={userSettings} />}
100+
{config?.features?.christmas && <ChristmasProvider />}
96101
<ConfettiProvider />
97102
<ToastProvider />
98103
{children}

components/chat/chat.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Image from 'next/image';
55
import { useState } from 'react';
66

77
import { useChatStore } from '@/hooks/store/use-chat-store';
8+
import { useUserSettingsStore } from '@/hooks/store/use-user-settings.store';
89
import { absoluteUrl, cn } from '@/lib/utils';
910

1011
import { PrettyLoader } from '../loaders/pretty-loader';
@@ -19,8 +20,16 @@ export const Chat = () => {
1920
currentModelLabel: state.currentModelLabel,
2021
}));
2122

23+
const { isCopilotInNewTab } = useUserSettingsStore((state) => ({
24+
isCopilotInNewTab: state.isCopilotInNewTab,
25+
}));
26+
27+
const handleOpenChange = isCopilotInNewTab
28+
? () => window.open(absoluteUrl('/chat'), '_blank')
29+
: setOpen;
30+
2231
return (
23-
<Sheet open={open} onOpenChange={setOpen}>
32+
<Sheet open={open} onOpenChange={handleOpenChange}>
2433
<SheetTrigger className="hover:opacity-75 transition duration-300">
2534
<button
2635
className={cn(

components/common/course-enroll-button.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,11 @@ export const CourseEnrollButton = ({
4949
});
5050

5151
toast({ title: t('redirect') });
52+
5253
window.location.assign(response.url);
5354
} catch (error) {
55+
console.error('[COURSE_ENROLL_BUTTON]', error);
56+
5457
toast({ isError: true });
5558
} finally {
5659
setIsFetching(false);
@@ -79,13 +82,17 @@ export const CourseEnrollButton = ({
7982
variant={variant}
8083
>
8184
{isLoading && <MoreHorizontal className="w-6 h-6 animate-pulse" />}
82-
{!isLoading && (amount ?? 0) > 0 && <ShoppingCart className="w-4 h-4 mr-2" />}
83-
{!isLoading && <span>{t('enrollFor')}</span>}
84-
{!isLoading && (amount ?? 0) > 0 && formattedPrice}
85-
{!isLoading && amount === 0 && (
85+
{!isLoading && (
8686
<>
87-
{t('free')}
88-
<ArrowRight className="w-4 h-4 ml-2" />
87+
{(amount ?? 0) > 0 && <ShoppingCart className="hidden md:block w-4 h-4 mr-2" />}
88+
<span>{t('enrollFor')}</span>
89+
{(amount ?? 0) > 0 && formattedPrice}
90+
{amount === 0 && (
91+
<>
92+
{t('free')}
93+
<ArrowRight className="w-4 h-4 ml-2" />
94+
</>
95+
)}
8996
</>
9097
)}
9198
</Button>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { create } from 'zustand';
2+
3+
type UserSettingsStore = {
4+
isChristmasMode: boolean;
5+
isCopilotInNewTab: boolean;
6+
isPublicProfile: boolean;
7+
setIsChristmasMode: (value: boolean) => void;
8+
setIsCopilotInNewTab: (value: boolean) => void;
9+
setIsPublicProfile: (value: boolean) => void;
10+
};
11+
12+
export const useUserSettingsStore = create<UserSettingsStore>((set) => ({
13+
isChristmasMode: false,
14+
isCopilotInNewTab: false,
15+
isPublicProfile: false,
16+
setIsChristmasMode: (value) => set({ isChristmasMode: value }),
17+
setIsCopilotInNewTab: (value) => set({ isCopilotInNewTab: value }),
18+
setIsPublicProfile: (value) => set({ isPublicProfile: value }),
19+
}));

hooks/use-user-settings.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
'use client';
2+
3+
import { UserSettings } from '@prisma/client';
4+
import { useEffect } from 'react';
5+
6+
import { useUserSettingsStore } from './store/use-user-settings.store';
7+
8+
export const useUserSettings = (userSettings: UserSettings | null) => {
9+
const { setIsChristmasMode, setIsCopilotInNewTab, setIsPublicProfile } = useUserSettingsStore();
10+
11+
useEffect(() => {
12+
setIsChristmasMode(Boolean(userSettings?.isChristmasMode));
13+
setIsCopilotInNewTab(Boolean(userSettings?.isCopilotInNewTab));
14+
setIsPublicProfile(Boolean(userSettings?.isPublicProfile));
15+
16+
// eslint-disable-next-line react-hooks/exhaustive-deps
17+
}, []);
18+
};

messages/be.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,10 @@
472472
"christmas": {
473473
"title": "Уключыць калядны настрой 🎄🎁",
474474
"body": "Калядная тэма дадае трохі магіі"
475+
},
476+
"new-tab-copilot": {
477+
"title": "Nova Copilot ў новай ўкладцы",
478+
"body": "Адкрываць чат заўсёды ў новай ўкладцы."
475479
}
476480
},
477481
"file-upload": {

messages/en.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,10 @@
472472
"christmas": {
473473
"title": "Turn on the Christmas mood 🎄🎁",
474474
"body": "The Christmas theme adds a little magic."
475+
},
476+
"new-tab-copilot": {
477+
"title": "Nova Copilot in a new tab",
478+
"body": "Open the chat always in a new tab."
475479
}
476480
},
477481
"file-upload": {

messages/ru.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,10 @@
471471
"christmas": {
472472
"title": "Включить рождественское настроение 🎄🎁",
473473
"body": "Рождественская тема добавляет немного магии"
474+
},
475+
"new-tab-copilot": {
476+
"title": "Nova Copilot в новой вкладке",
477+
"body": "Открывать чат всегда в новой вкладке."
474478
}
475479
},
476480
"file-upload": {

0 commit comments

Comments
 (0)