From 23cd59bbac1a667e288d5657801b8785386e42e5 Mon Sep 17 00:00:00 2001 From: javayhu Date: Fri, 18 Apr 2025 21:47:14 +0800 Subject: [PATCH] refactor: biome lint part 1 --- biome.json | 4 +- content-collections.ts | 91 +++--- global.d.ts | 6 +- messages/en.json | 2 +- messages/zh.json | 4 +- next.config.ts | 22 +- src/actions/check-newsletter-status.ts | 2 +- src/actions/create-checkout-session.ts | 31 +- src/actions/create-customer-portal-session.ts | 40 ++- src/actions/get-active-subscription.ts | 33 +- src/actions/get-lifetime-status.ts | 42 ++- src/actions/send-message.ts | 9 +- src/actions/subscribe-newsletter.ts | 2 +- src/actions/unsubscribe-newsletter.ts | 4 +- src/analytics/analytics.tsx | 14 +- src/analytics/data-fast-analytics.tsx | 6 +- src/analytics/google-analytics.tsx | 6 +- src/analytics/open-panel-analytics.tsx | 4 +- src/analytics/plausible-analytics.tsx | 13 +- src/analytics/seline-analytics.tsx | 8 +- src/analytics/umami-analytics.tsx | 8 +- src/app/[locale]/(marketing)/(home)/page.tsx | 4 +- .../(marketing)/(legal)/cookie/page.tsx | 8 +- .../[locale]/(marketing)/(legal)/layout.tsx | 2 +- .../(marketing)/(legal)/privacy/page.tsx | 8 +- .../(marketing)/(legal)/terms/page.tsx | 8 +- .../(marketing)/(pages)/about/page.tsx | 14 +- .../(marketing)/(pages)/changelog/page.tsx | 12 +- .../(marketing)/(pages)/contact/page.tsx | 8 +- .../[locale]/(marketing)/(pages)/layout.tsx | 2 +- .../(marketing)/(pages)/waitlist/page.tsx | 12 +- .../[locale]/(marketing)/ai/audio/page.tsx | 10 +- .../[locale]/(marketing)/ai/image/page.tsx | 10 +- src/app/[locale]/(marketing)/ai/layout.tsx | 2 +- src/app/[locale]/(marketing)/ai/text/page.tsx | 10 +- .../[locale]/(marketing)/ai/video/page.tsx | 10 +- .../(marketing)/blocks/[category]/layout.tsx | 6 +- .../(marketing)/blocks/[category]/page.tsx | 6 +- .../blog/(blog)/category/[slug]/page.tsx | 8 +- .../(marketing)/blog/(blog)/layout.tsx | 6 +- .../[locale]/(marketing)/blog/(blog)/page.tsx | 14 +- .../(marketing)/blog/[...slug]/layout.tsx | 2 +- .../(marketing)/blog/[...slug]/page.tsx | 12 +- src/app/[locale]/(marketing)/layout.tsx | 2 +- .../[locale]/(marketing)/pricing/layout.tsx | 4 +- .../[locale]/(protected)/admin/users/page.tsx | 4 +- .../[locale]/(protected)/dashboard/page.tsx | 4 +- src/app/[locale]/(protected)/layout.tsx | 15 +- .../(protected)/settings/billing/layout.tsx | 4 +- .../(protected)/settings/billing/page.tsx | 4 +- .../(protected)/settings/profile/layout.tsx | 4 +- src/app/[locale]/auth/error/page.tsx | 12 +- .../[locale]/auth/forgot-password/page.tsx | 17 +- src/app/[locale]/auth/login/page.tsx | 16 +- src/app/[locale]/auth/register/page.tsx | 4 +- src/app/[locale]/auth/reset-password/page.tsx | 17 +- src/app/[locale]/docs/[[...slug]]/page.tsx | 50 +-- src/app/[locale]/docs/layout.tsx | 51 +-- src/app/[locale]/layout.tsx | 13 +- src/app/[locale]/providers.tsx | 14 +- src/app/api/search/route.ts | 13 +- src/app/api/storage/file-url/route.ts | 13 +- src/app/api/storage/presigned-url/route.ts | 11 +- src/app/api/storage/upload/route.ts | 14 +- src/app/api/webhooks/stripe/route.ts | 4 +- src/app/layout.tsx | 2 +- src/app/manifest.ts | 12 +- src/app/not-found.tsx | 2 +- src/app/robots.ts | 2 +- src/app/sitemap.ts | 78 ++--- src/assets/fonts/index.ts | 7 +- src/components/auth/auth-card.tsx | 4 +- src/components/auth/divider-with-text.tsx | 15 +- src/components/auth/forgot-password-form.tsx | 4 +- src/components/auth/login-form.tsx | 16 +- src/components/auth/login-wrapper.tsx | 7 - src/components/auth/register-form.tsx | 15 +- src/components/auth/reset-password-form.tsx | 6 +- src/components/auth/social-login-button.tsx | 9 +- .../blocks/calltoaction/calltoaction.tsx | 4 +- src/components/blocks/faqs/faqs.tsx | 2 +- src/components/blocks/features/features.tsx | 12 +- src/components/blocks/features/features2.tsx | 11 +- src/components/blocks/features/features3.tsx | 15 +- src/components/blocks/features/features4.tsx | 17 +- src/components/blocks/features/features5.tsx | 39 ++- src/components/blocks/hero/hero.tsx | 8 +- .../blocks/integration/integration.tsx | 6 +- .../blocks/integration/integration2.tsx | 4 +- .../blocks/logo-cloud/logo-cloud.tsx | 167 +++++----- src/components/blocks/pricing/pricing.tsx | 10 +- src/components/blocks/stats/stats.tsx | 10 +- .../blocks/testimonials/testimonials.tsx | 6 +- src/components/blog/blog-card.tsx | 4 +- src/components/blog/blog-category-filter.tsx | 2 +- .../blog/blog-category-list-desktop.tsx | 11 +- .../blog/blog-category-list-mobile.tsx | 2 +- src/components/blog/blog-grid.tsx | 2 +- src/components/contact/contact-form-card.tsx | 52 +--- .../dashboard/chart-area-interactive.tsx | 269 ++++++++-------- src/components/dashboard/dashboard-header.tsx | 20 +- .../dashboard/dashboard-sidebar.tsx | 10 +- src/components/dashboard/data-table.tsx | 215 +++++++------ src/components/dashboard/section-cards.tsx | 8 +- src/components/dashboard/sidebar-main.tsx | 17 +- src/components/dashboard/sidebar-user.tsx | 143 ++++----- src/components/dashboard/upgrade-card.tsx | 27 +- src/components/docs/index.tsx | 2 +- src/components/docs/lazy.ts | 12 +- src/components/docs/wrapper.tsx | 2 +- src/components/icons/discord.tsx | 21 +- .../layout/active-theme-provider.tsx | 29 +- src/components/layout/error.tsx | 4 +- src/components/layout/footer.tsx | 4 +- src/components/layout/locale-selector.tsx | 16 +- src/components/layout/locale-switcher.tsx | 32 +- src/components/layout/logo.tsx | 2 +- src/components/layout/navbar-mobile.tsx | 31 +- src/components/layout/navbar.tsx | 44 +-- src/components/layout/payment-provider.tsx | 6 +- src/components/layout/theme-selector.tsx | 28 +- src/components/layout/user-avatar.tsx | 8 +- src/components/layout/user-button-mobile.tsx | 6 +- src/components/layout/user-button.tsx | 8 +- src/components/motion/animated-group.tsx | 214 +++++++------ src/components/motion/infinite-slider.tsx | 8 +- src/components/motion/progressive-blur.tsx | 4 +- src/components/motion/text-effect.tsx | 208 ++++++++----- src/components/nsui/blocks.ts | 3 +- src/components/page/custom-page.tsx | 5 +- .../pricing/create-checkout-button.tsx | 15 +- .../pricing/customer-portal-button.tsx | 15 +- src/components/pricing/pricing-card.tsx | 70 +++-- src/components/pricing/pricing-table.tsx | 133 +++++--- .../settings/billing/billing-card.tsx | 109 +++---- .../notification/newsletter-form-card.tsx | 35 ++- .../settings/profile/update-avatar-card.tsx | 15 +- .../settings/profile/update-name-card.tsx | 42 +-- .../settings/security/delete-account-card.tsx | 26 +- .../security/password-card-wrapper.tsx | 26 +- .../settings/security/reset-password-card.tsx | 33 +- .../security/update-password-card.tsx | 67 ++-- src/components/shared/built-with-button.tsx | 2 +- src/components/shared/custom-mdx-content.tsx | 23 +- src/components/shared/empty-grid.tsx | 4 +- src/components/shared/filter-item-mobile.tsx | 2 +- .../waitlist/waitlist-form-card.tsx | 34 +- src/config/avatar-config.tsx | 10 +- src/config/footer-config.tsx | 4 +- src/config/navbar-config.tsx | 6 +- src/config/payment-config.tsx | 21 +- src/config/sidebar-config.tsx | 8 +- src/config/social-config.tsx | 6 +- src/config/website.tsx | 28 +- src/db/index.ts | 2 +- src/hooks/use-clipboard.ts | 2 +- src/hooks/use-mobile.ts | 24 +- src/hooks/use-payment.ts | 21 +- src/i18n/messages.ts | 2 +- src/i18n/request.ts | 2 +- src/i18n/routing.ts | 2 +- src/lib/auth-client.ts | 2 +- src/lib/auth.ts | 13 +- src/lib/constants.ts | 2 +- src/lib/docs/i18n.ts | 4 +- src/lib/docs/source.ts | 8 +- src/lib/metadata.ts | 2 +- src/lib/page/get-page.ts | 2 +- src/lib/price-plan.ts | 17 +- src/lib/release/get-releases.ts | 2 +- src/lib/server.ts | 18 +- src/lib/serviceWorker.ts | 16 +- src/lib/urls/urls.ts | 31 +- src/mail/components/email-layout.tsx | 10 +- src/mail/index.ts | 18 +- src/mail/provider/resend.ts | 28 +- src/mail/templates/contact-message.tsx | 14 +- src/mail/templates/forgot-password.tsx | 19 +- src/mail/templates/subscribe-newsletter.tsx | 18 +- src/mail/templates/verify-email.tsx | 8 +- src/mail/types.ts | 6 +- src/middleware.ts | 34 +- src/newsletter/index.ts | 6 +- src/newsletter/provider/resend.ts | 25 +- src/newsletter/types.ts | 12 +- src/payment/index.ts | 18 +- src/payment/provider/stripe.ts | 290 +++++++++++++----- src/payment/types.ts | 2 +- src/routes.ts | 7 +- src/storage/config/storage-config.ts | 6 +- src/storage/index.ts | 28 +- src/storage/provider/s3.ts | 52 +++- src/storage/types.ts | 6 +- src/stores/locale-store.ts | 2 +- src/stores/payment-store.ts | 60 ++-- src/styles/globals.css | 7 +- src/styles/mdx.css | 6 +- src/types/index.d.ts | 4 +- vercel.json | 2 +- 199 files changed, 2417 insertions(+), 1961 deletions(-) diff --git a/biome.json b/biome.json index be9b94f..2b9b070 100644 --- a/biome.json +++ b/biome.json @@ -66,6 +66,8 @@ "src/components/magicui/*.tsx", "src/app/[[]locale]/preview/**", "src/db/schema.ts", + "src/payment/types.ts", + "src/types/index.d.ts", "public/sw.js" ] }, @@ -76,4 +78,4 @@ "semicolons": "always" } } -} +} \ No newline at end of file diff --git a/content-collections.ts b/content-collections.ts index a549add..f283c77 100644 --- a/content-collections.ts +++ b/content-collections.ts @@ -1,18 +1,18 @@ -import { DEFAULT_LOCALE, LOCALES } from "@/i18n/routing"; -import { defineCollection, defineConfig } from "@content-collections/core"; +import path from 'path'; +import { DEFAULT_LOCALE, LOCALES } from '@/i18n/routing'; +import { defineCollection, defineConfig } from '@content-collections/core'; import { createDocSchema, createMetaSchema, transformMDX, } from '@fumadocs/content-collections/configuration'; -import path from "path"; /** * 1. Content Collections documentation * https://www.content-collections.dev/docs/quickstart/next * https://www.content-collections.dev/docs/configuration * https://www.content-collections.dev/docs/transform#join-collections - * + * * 2. Use Content Collections for Fumadocs * https://fumadocs.vercel.app/docs/headless/content-collections */ @@ -38,11 +38,11 @@ const metas = defineCollection({ /** * Blog Author collection - * + * * Authors are identified by their slug across all languages * New format: content/author/authorname.{locale}.mdx * Example: content/author/mksaas.mdx (default locale) and content/author/mksaas.zh.mdx (Chinese) - * + * * For author, slug is slugAsParams */ export const authors = defineCollection({ @@ -53,7 +53,7 @@ export const authors = defineCollection({ slug: z.string(), name: z.string(), avatar: z.string(), - locale: z.string().optional().default(DEFAULT_LOCALE) + locale: z.string().optional().default(DEFAULT_LOCALE), }), transform: async (data, context) => { // Get the filename from the path @@ -68,16 +68,16 @@ export const authors = defineCollection({ ...data, locale, }; - } + }, }); /** * Blog Category collection - * + * * Categories are identified by their slug across all languages * New format: content/category/categoryname.{locale}.mdx * Example: content/category/tutorial.mdx (default locale) and content/category/tutorial.zh.mdx (Chinese) - * + * * For category, slug is slugAsParams */ export const categories = defineCollection({ @@ -88,7 +88,7 @@ export const categories = defineCollection({ slug: z.string(), name: z.string(), description: z.string(), - locale: z.string().optional().default(DEFAULT_LOCALE) + locale: z.string().optional().default(DEFAULT_LOCALE), }), transform: async (data, context) => { // Get the filename from the path @@ -101,24 +101,24 @@ export const categories = defineCollection({ return { ...data, - locale + locale, }; - } + }, }); /** * Blog Post collection - * + * * New format: content/blog/post-slug.{locale}.mdx - * + * * slug: /blog/first-post, used in URL or sitemap * slugAsParams: first-post, used in route params - * + * * 1. For a blog post at content/blog/first-post.mdx (default locale): * locale: en * slug: /blog/first-post * slugAsParams: first-post - * + * * 2. For a blog post at content/blog/first-post.zh.mdx (Chinese locale): * locale: zh * slug: /blog/first-post @@ -136,7 +136,7 @@ export const posts = defineCollection({ published: z.boolean().default(true), categories: z.array(z.string()), author: z.string(), - estimatedTime: z.number().optional() // Reading time in minutes + estimatedTime: z.number().optional(), // Reading time in minutes }), transform: async (data, context) => { // Use Fumadocs transformMDX for consistent MDX processing @@ -156,18 +156,20 @@ export const posts = defineCollection({ .find((a) => a.slug === data.author && a.locale === locale); // Find categories by matching slug and locale - const blogCategories = data.categories.map(categorySlug => { - const category = context - .documents(categories) - .find(c => c.slug === categorySlug && c.locale === locale); + const blogCategories = data.categories + .map((categorySlug) => { + const category = context + .documents(categories) + .find((c) => c.slug === categorySlug && c.locale === locale); - return category; - }).filter(Boolean); // Remove null values + return category; + }) + .filter(Boolean); // Remove null values // Create the slug and slugAsParams const slug = `/blog/${base}`; const slugAsParams = base; - + // Calculate estimated reading time const wordCount = data.content.split(/\s+/).length; const wordsPerMinute = 200; // average reading speed: 200 words per minute @@ -182,21 +184,21 @@ export const posts = defineCollection({ slugAsParams, estimatedTime, body: transformedData.body, - toc: transformedData.toc + toc: transformedData.toc, }; - } + }, }); /** * Pages collection for policy pages like privacy-policy, terms-of-service, etc. - * + * * New format: content/pages/page-slug.{locale}.mdx - * + * * 1. For a page at content/pages/privacy-policy.mdx (default locale): * locale: en * slug: /pages/privacy-policy * slugAsParams: privacy-policy - * + * * 2. For a page at content/pages/privacy-policy.zh.mdx (Chinese locale): * locale: zh * slug: /pages/privacy-policy @@ -210,7 +212,7 @@ export const pages = defineCollection({ title: z.string(), description: z.string(), date: z.string().datetime(), - published: z.boolean().default(true) + published: z.boolean().default(true), }), transform: async (data, context) => { // Use Fumadocs transformMDX for consistent MDX processing @@ -234,21 +236,21 @@ export const pages = defineCollection({ slug, slugAsParams, body: transformedData.body, - toc: transformedData.toc + toc: transformedData.toc, }; - } + }, }); /** * Releases collection for changelog - * + * * New format: content/release/version-slug.{locale}.mdx - * + * * 1. For a release at content/release/v1-0-0.mdx (default locale): * locale: en * slug: /release/v1-0-0 * slugAsParams: v1-0-0 - * + * * 2. For a release at content/release/v1-0-0.zh.mdx (Chinese locale): * locale: zh * slug: /release/v1-0-0 @@ -263,7 +265,7 @@ export const releases = defineCollection({ description: z.string(), date: z.string().datetime(), version: z.string(), - published: z.boolean().default(true) + published: z.boolean().default(true), }), transform: async (data, context) => { // Use Fumadocs transformMDX for consistent MDX processing @@ -287,9 +289,9 @@ export const releases = defineCollection({ slug, slugAsParams, body: transformedData.body, - toc: transformedData.toc + toc: transformedData.toc, }; - } + }, }); /** @@ -297,11 +299,14 @@ export const releases = defineCollection({ * Handles filename formats: * - name -> locale: DEFAULT_LOCALE, base: name * - name.zh -> locale: zh, base: name - * + * * @param fileName Filename without extension (already has .mdx removed) * @returns Object with locale and base name */ -function extractLocaleAndBase(fileName: string): { locale: string; base: string } { +function extractLocaleAndBase(fileName: string): { + locale: string; + base: string; +} { // Split filename into parts const parts = fileName.split('.'); @@ -319,5 +324,5 @@ function extractLocaleAndBase(fileName: string): { locale: string; base: string } export default defineConfig({ - collections: [docs, metas, authors, categories, posts, pages, releases] -}); \ No newline at end of file + collections: [docs, metas, authors, categories, posts, pages, releases], +}); diff --git a/global.d.ts b/global.d.ts index 7479cfd..da0d666 100644 --- a/global.d.ts +++ b/global.d.ts @@ -1,9 +1,9 @@ -import { routing } from '@/i18n/routing'; -import messages from './messages/en.json'; +import type { routing } from '@/i18n/routing'; +import type messages from './messages/en.json'; /** * next-intl 4.0.0 - * + * * https://github.com/amannn/next-intl/blob/main/examples/example-app-router/global.d.ts */ declare module 'next-intl' { diff --git a/messages/en.json b/messages/en.json index 4072a2d..993b129 100644 --- a/messages/en.json +++ b/messages/en.json @@ -848,4 +848,4 @@ "description": "MkSaaS lets you make AI SaaS in days, simply and effortlessly", "content": "Working in progress" } -} \ No newline at end of file +} diff --git a/messages/zh.json b/messages/zh.json index 42b0724..e3071cd 100644 --- a/messages/zh.json +++ b/messages/zh.json @@ -671,7 +671,7 @@ }, "item-2": { "title": "产品特色功能二", - "description": "请在这里详细描述您的产品特色功能二,尽可能详细,使其更吸引用户" + "description": "请在这里详细描述您的产品特色功能二,尽可能详细,使其更吸引用户" }, "item-3": { "title": "产品特色功能三", @@ -849,4 +849,4 @@ "description": "MkSaaS 让您在几天内轻松构建您的 AI SaaS,简单且毫不费力", "content": "正在开发中" } -} \ No newline at end of file +} diff --git a/next.config.ts b/next.config.ts index f66e975..56799cc 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,6 +1,6 @@ -import type { NextConfig } from "next"; +import { withContentCollections } from '@content-collections/next'; +import type { NextConfig } from 'next'; import createNextIntlPlugin from 'next-intl/plugin'; -import { withContentCollections } from "@content-collections/next"; /** * https://nextjs.org/docs/app/api-reference/config/next-config-js @@ -12,22 +12,22 @@ const nextConfig: NextConfig = { // https://nextjs.org/docs/architecture/nextjs-compiler#remove-console // Remove all console.* calls in production only compiler: { - removeConsole: process.env.NODE_ENV === "production", + removeConsole: process.env.NODE_ENV === 'production', }, images: { remotePatterns: [ { - protocol: "https", - hostname: "avatars.githubusercontent.com", + protocol: 'https', + hostname: 'avatars.githubusercontent.com', }, { - protocol: "https", - hostname: "lh3.googleusercontent.com", + protocol: 'https', + hostname: 'lh3.googleusercontent.com', }, { - protocol: "https", - hostname: "randomuser.me", + protocol: 'https', + hostname: 'randomuser.me', }, { protocol: 'https', @@ -39,14 +39,14 @@ const nextConfig: NextConfig = { /** * You can specify the path to the request config file or use the default one (@/i18n/request.ts) - * + * * https://next-intl.dev/docs/getting-started/app-router/with-i18n-routing#next-config */ const withNextIntl = createNextIntlPlugin(); /** * withContentCollections must be the outermost plugin - * + * * https://www.content-collections.dev/docs/quickstart/next */ export default withContentCollections(withNextIntl(nextConfig)); diff --git a/src/actions/check-newsletter-status.ts b/src/actions/check-newsletter-status.ts index bdcba14..2daca00 100644 --- a/src/actions/check-newsletter-status.ts +++ b/src/actions/check-newsletter-status.ts @@ -31,4 +31,4 @@ export const checkNewsletterStatusAction = actionClient error: error instanceof Error ? error.message : 'Something went wrong', }; } - }); \ No newline at end of file + }); diff --git a/src/actions/create-checkout-session.ts b/src/actions/create-checkout-session.ts index 327f021..7a52ae6 100644 --- a/src/actions/create-checkout-session.ts +++ b/src/actions/create-checkout-session.ts @@ -1,14 +1,14 @@ 'use server'; -import { getSession } from "@/lib/server"; -import { findPlanByPlanId } from "@/lib/price-plan"; -import { getUrlWithLocale } from "@/lib/urls/urls"; -import { createCheckout } from "@/payment"; -import { CreateCheckoutParams } from "@/payment/types"; -import { getLocale } from "next-intl/server"; +import { findPlanByPlanId } from '@/lib/price-plan'; +import { getSession } from '@/lib/server'; +import { getUrlWithLocale } from '@/lib/urls/urls'; +import { createCheckout } from '@/payment'; +import type { CreateCheckoutParams } from '@/payment/types'; +import { Routes } from '@/routes'; +import { getLocale } from 'next-intl/server'; import { createSafeActionClient } from 'next-safe-action'; import { z } from 'zod'; -import { Routes } from "@/routes"; // Create a safe action client const actionClient = createSafeActionClient(); @@ -33,7 +33,9 @@ export const createCheckoutAction = actionClient // Get the current user session for authorization const session = await getSession(); if (!session) { - console.warn(`unauthorized request to create checkout session for user ${userId}`); + console.warn( + `unauthorized request to create checkout session for user ${userId}` + ); return { success: false, error: 'Unauthorized', @@ -42,7 +44,9 @@ export const createCheckoutAction = actionClient // Only allow users to create their own checkout session if (session.user.id !== userId) { - console.warn(`current user ${session.user.id} is not authorized to create checkout session for user ${userId}`); + console.warn( + `current user ${session.user.id} is not authorized to create checkout session for user ${userId}` + ); return { success: false, error: 'Not authorized to do this action', @@ -70,7 +74,10 @@ export const createCheckoutAction = actionClient }; // Create the checkout session with localized URLs - const successUrl = getUrlWithLocale('/settings/billing?session_id={CHECKOUT_SESSION_ID}', locale); + const successUrl = getUrlWithLocale( + '/settings/billing?session_id={CHECKOUT_SESSION_ID}', + locale + ); const cancelUrl = getUrlWithLocale(Routes.Pricing, locale); const params: CreateCheckoutParams = { planId, @@ -89,10 +96,10 @@ export const createCheckoutAction = actionClient data: result, }; } catch (error) { - console.error("create checkout session error:", error); + console.error('create checkout session error:', error); return { success: false, error: error instanceof Error ? error.message : 'Something went wrong', }; } - }); \ No newline at end of file + }); diff --git a/src/actions/create-customer-portal-session.ts b/src/actions/create-customer-portal-session.ts index f7cdec0..e4e46fa 100644 --- a/src/actions/create-customer-portal-session.ts +++ b/src/actions/create-customer-portal-session.ts @@ -1,13 +1,13 @@ 'use server'; -import db from "@/db"; -import { user } from "@/db/schema"; -import { getSession } from "@/lib/server"; -import { getUrlWithLocale } from "@/lib/urls/urls"; -import { createCustomerPortal } from "@/payment"; -import { CreatePortalParams } from "@/payment/types"; -import { eq } from "drizzle-orm"; -import { getLocale } from "next-intl/server"; +import db from '@/db'; +import { user } from '@/db/schema'; +import { getSession } from '@/lib/server'; +import { getUrlWithLocale } from '@/lib/urls/urls'; +import { createCustomerPortal } from '@/payment'; +import type { CreatePortalParams } from '@/payment/types'; +import { eq } from 'drizzle-orm'; +import { getLocale } from 'next-intl/server'; import { createSafeActionClient } from 'next-safe-action'; import { z } from 'zod'; @@ -17,7 +17,10 @@ const actionClient = createSafeActionClient(); // Portal schema for validation const portalSchema = z.object({ userId: z.string().min(1, { message: 'User ID is required' }), - returnUrl: z.string().url({ message: 'Return URL must be a valid URL' }).optional(), + returnUrl: z + .string() + .url({ message: 'Return URL must be a valid URL' }) + .optional(), }); /** @@ -27,11 +30,13 @@ export const createPortalAction = actionClient .schema(portalSchema) .action(async ({ parsedInput }) => { const { userId, returnUrl } = parsedInput; - + // Get the current user session for authorization const session = await getSession(); if (!session) { - console.warn(`unauthorized request to create portal session for user ${userId}`); + console.warn( + `unauthorized request to create portal session for user ${userId}` + ); return { success: false, error: 'Unauthorized', @@ -40,7 +45,9 @@ export const createPortalAction = actionClient // Only allow users to create their own portal session if (session.user.id !== userId) { - console.warn(`current user ${session.user.id} is not authorized to create portal session for user ${userId}`); + console.warn( + `current user ${session.user.id} is not authorized to create portal session for user ${userId}` + ); return { success: false, error: 'Not authorized to do this action', @@ -67,11 +74,12 @@ export const createPortalAction = actionClient const locale = await getLocale(); // Create the portal session with localized URL if no custom return URL is provided - const returnUrlWithLocale = returnUrl || getUrlWithLocale('/settings/billing', locale); + const returnUrlWithLocale = + returnUrl || getUrlWithLocale('/settings/billing', locale); const params: CreatePortalParams = { customerId: customerResult[0].customerId, returnUrl: returnUrlWithLocale, - locale + locale, }; const result = await createCustomerPortal(params); @@ -81,10 +89,10 @@ export const createPortalAction = actionClient data: result, }; } catch (error) { - console.error("create customer portal error:", error); + console.error('create customer portal error:', error); return { success: false, error: error instanceof Error ? error.message : 'Something went wrong', }; } - }); \ No newline at end of file + }); diff --git a/src/actions/get-active-subscription.ts b/src/actions/get-active-subscription.ts index f98c912..ecab486 100644 --- a/src/actions/get-active-subscription.ts +++ b/src/actions/get-active-subscription.ts @@ -1,9 +1,9 @@ 'use server'; -import { getSession } from "@/lib/server"; -import { getSubscriptions } from "@/payment"; +import { getSession } from '@/lib/server'; +import { getSubscriptions } from '@/payment'; import { createSafeActionClient } from 'next-safe-action'; -import { z } from "zod"; +import { z } from 'zod'; // Create a safe action client const actionClient = createSafeActionClient(); @@ -15,8 +15,8 @@ const schema = z.object({ /** * Get active subscription data - * - * If the user has multiple subscriptions, + * + * If the user has multiple subscriptions, * it returns the most recent active or trialing one */ export const getActiveSubscriptionAction = actionClient @@ -27,7 +27,9 @@ export const getActiveSubscriptionAction = actionClient // Get the current user session for authorization const session = await getSession(); if (!session) { - console.warn(`unauthorized request to get active subscription for user ${userId}`); + console.warn( + `unauthorized request to get active subscription for user ${userId}` + ); return { success: false, error: 'Unauthorized', @@ -36,7 +38,9 @@ export const getActiveSubscriptionAction = actionClient // Only allow users to check their own status unless they're admins if (session.user.id !== userId && session.user.role !== 'admin') { - console.warn(`current user ${session.user.id} is not authorized to get active subscription for user ${userId}`); + console.warn( + `current user ${session.user.id} is not authorized to get active subscription for user ${userId}` + ); return { success: false, error: 'Not authorized to do this action', @@ -46,7 +50,7 @@ export const getActiveSubscriptionAction = actionClient try { // Find the user's most recent active subscription const subscriptions = await getSubscriptions({ - userId: session.user.id + userId: session.user.id, }); // console.log('get user subscriptions:', subscriptions); @@ -54,8 +58,8 @@ export const getActiveSubscriptionAction = actionClient // Find the most recent active subscription (if any) if (subscriptions && subscriptions.length > 0) { // First try to find an active subscription - const activeSubscription = subscriptions.find(sub => - sub.status === 'active' || sub.status === 'trialing' + const activeSubscription = subscriptions.find( + (sub) => sub.status === 'active' || sub.status === 'trialing' ); // If found, use it @@ -63,7 +67,10 @@ export const getActiveSubscriptionAction = actionClient console.log('find active subscription for userId:', session.user.id); subscriptionData = activeSubscription; } else { - console.log('no active subscription found for userId:', session.user.id); + console.log( + 'no active subscription found for userId:', + session.user.id + ); } } else { console.log('no subscriptions found for userId:', session.user.id); @@ -74,10 +81,10 @@ export const getActiveSubscriptionAction = actionClient data: subscriptionData, }; } catch (error) { - console.error("get user subscription data error:", error); + console.error('get user subscription data error:', error); return { success: false, error: error instanceof Error ? error.message : 'Something went wrong', }; } - }); \ No newline at end of file + }); diff --git a/src/actions/get-lifetime-status.ts b/src/actions/get-lifetime-status.ts index b4c58dc..ac98a56 100644 --- a/src/actions/get-lifetime-status.ts +++ b/src/actions/get-lifetime-status.ts @@ -1,13 +1,13 @@ 'use server'; -import db from "@/db"; -import { payment } from "@/db/schema"; -import { getSession } from "@/lib/server"; -import { getAllPricePlans, findPlanByPriceId } from "@/lib/price-plan"; -import { PaymentTypes } from "@/payment/types"; -import { and, eq } from "drizzle-orm"; +import db from '@/db'; +import { payment } from '@/db/schema'; +import { findPlanByPriceId, getAllPricePlans } from '@/lib/price-plan'; +import { getSession } from '@/lib/server'; +import { PaymentTypes } from '@/payment/types'; +import { and, eq } from 'drizzle-orm'; import { createSafeActionClient } from 'next-safe-action'; -import { z } from "zod"; +import { z } from 'zod'; // Create a safe action client const actionClient = createSafeActionClient(); @@ -19,8 +19,8 @@ const schema = z.object({ /** * Get user lifetime membership status directly from the database - * - * NOTICE: If you first add lifetime plan and then delete it, + * + * NOTICE: If you first add lifetime plan and then delete it, * the user with lifetime plan should be considered as a lifetime member as well, * in order to do this, you have to update the logic to check the lifetime status, * for example, just check the planId is `lifetime` or not. @@ -33,7 +33,9 @@ export const getLifetimeStatusAction = actionClient // Get the current user session for authorization const session = await getSession(); if (!session) { - console.warn(`unauthorized request to get lifetime status for user ${userId}`); + console.warn( + `unauthorized request to get lifetime status for user ${userId}` + ); return { success: false, error: 'Unauthorized', @@ -42,7 +44,9 @@ export const getLifetimeStatusAction = actionClient // Only allow users to check their own status unless they're admins if (session.user.id !== userId && session.user.role !== 'admin') { - console.warn(`current user ${session.user.id} is not authorized to get lifetime status for user ${userId}`); + console.warn( + `current user ${session.user.id} is not authorized to get lifetime status for user ${userId}` + ); return { success: false, error: 'Not authorized to do this action', @@ -53,8 +57,8 @@ export const getLifetimeStatusAction = actionClient // Get lifetime plans const plans = getAllPricePlans(); const lifetimePlanIds = plans - .filter(plan => plan.isLifetime) - .map(plan => plan.id); + .filter((plan) => plan.isLifetime) + .map((plan) => plan.id); // Check if there are any lifetime plans defined in the system if (lifetimePlanIds.length === 0) { @@ -66,7 +70,11 @@ export const getLifetimeStatusAction = actionClient // Query the database for one-time payments with lifetime plans const result = await db - .select({ id: payment.id, priceId: payment.priceId, type: payment.type }) + .select({ + id: payment.id, + priceId: payment.priceId, + type: payment.type, + }) .from(payment) .where( and( @@ -77,7 +85,7 @@ export const getLifetimeStatusAction = actionClient ); // Check if any payment has a lifetime plan - const hasLifetimePayment = result.some(paymentRecord => { + const hasLifetimePayment = result.some((paymentRecord) => { const plan = findPlanByPriceId(paymentRecord.priceId); return plan && lifetimePlanIds.includes(plan.id); }); @@ -87,10 +95,10 @@ export const getLifetimeStatusAction = actionClient isLifetimeMember: hasLifetimePayment, }; } catch (error) { - console.error("get user lifetime status error:", error); + console.error('get user lifetime status error:', error); return { success: false, error: error instanceof Error ? error.message : 'Something went wrong', }; } - }); \ No newline at end of file + }); diff --git a/src/actions/send-message.ts b/src/actions/send-message.ts index 1fb4a9b..47a2458 100644 --- a/src/actions/send-message.ts +++ b/src/actions/send-message.ts @@ -15,12 +15,13 @@ const actionClient = createSafeActionClient(); */ // Contact form schema for validation const contactFormSchema = z.object({ - name: z.string() + name: z + .string() .min(3, { message: 'Name must be at least 3 characters' }) .max(30, { message: 'Name must not exceed 30 characters' }), - email: z.string() - .email({ message: 'Please enter a valid email address' }), - message: z.string() + email: z.string().email({ message: 'Please enter a valid email address' }), + message: z + .string() .min(10, { message: 'Message must be at least 10 characters' }) .max(500, { message: 'Message must not exceed 500 characters' }), }); diff --git a/src/actions/subscribe-newsletter.ts b/src/actions/subscribe-newsletter.ts index c286e1d..3d7018a 100644 --- a/src/actions/subscribe-newsletter.ts +++ b/src/actions/subscribe-newsletter.ts @@ -51,4 +51,4 @@ export const subscribeNewsletterAction = actionClient error: error instanceof Error ? error.message : 'Something went wrong', }; } - }); \ No newline at end of file + }); diff --git a/src/actions/unsubscribe-newsletter.ts b/src/actions/unsubscribe-newsletter.ts index e5006de..332623a 100644 --- a/src/actions/unsubscribe-newsletter.ts +++ b/src/actions/unsubscribe-newsletter.ts @@ -24,7 +24,7 @@ export const unsubscribeNewsletterAction = actionClient error: 'Unauthorized', }; } - + try { const unsubscribed = await unsubscribe(email); @@ -46,4 +46,4 @@ export const unsubscribeNewsletterAction = actionClient error: error instanceof Error ? error.message : 'Something went wrong', }; } - }); \ No newline at end of file + }); diff --git a/src/analytics/analytics.tsx b/src/analytics/analytics.tsx index 5043ff8..c4a315e 100644 --- a/src/analytics/analytics.tsx +++ b/src/analytics/analytics.tsx @@ -1,9 +1,9 @@ -import GoogleAnalytics from "./google-analytics"; -import { UmamiAnalytics } from "./umami-analytics"; -import { PlausibleAnalytics } from "./plausible-analytics"; -import DataFastAnalytics from "./data-fast-analytics"; -import OpenPanelAnalytics from "./open-panel-analytics"; -import { SelineAnalytics } from "./seline-analytics"; +import DataFastAnalytics from './data-fast-analytics'; +import GoogleAnalytics from './google-analytics'; +import OpenPanelAnalytics from './open-panel-analytics'; +import { PlausibleAnalytics } from './plausible-analytics'; +import { SelineAnalytics } from './seline-analytics'; +import { UmamiAnalytics } from './umami-analytics'; /** * Analytics Components all in one @@ -12,7 +12,7 @@ import { SelineAnalytics } from "./seline-analytics"; * 2. only work if the environment variable for the analytics is set */ export function Analytics() { - if (process.env.NODE_ENV !== "production") { + if (process.env.NODE_ENV !== 'production') { return null; } diff --git a/src/analytics/data-fast-analytics.tsx b/src/analytics/data-fast-analytics.tsx index 2faff5e..b39bbc1 100644 --- a/src/analytics/data-fast-analytics.tsx +++ b/src/analytics/data-fast-analytics.tsx @@ -1,6 +1,6 @@ -"use client"; +'use client'; -import Script from "next/script"; +import Script from 'next/script'; /** * DataFast Analytics @@ -8,7 +8,7 @@ import Script from "next/script"; * https://datafa.st */ export default function DataFastAnalytics() { - if (process.env.NODE_ENV !== "production") { + if (process.env.NODE_ENV !== 'production') { return null; } diff --git a/src/analytics/google-analytics.tsx b/src/analytics/google-analytics.tsx index 941258f..038b529 100644 --- a/src/analytics/google-analytics.tsx +++ b/src/analytics/google-analytics.tsx @@ -1,6 +1,6 @@ -"use client"; +'use client'; -import { GoogleAnalytics as NextGoogleAnalytics } from "@next/third-parties/google"; +import { GoogleAnalytics as NextGoogleAnalytics } from '@next/third-parties/google'; /** * Google Analytics @@ -9,7 +9,7 @@ import { GoogleAnalytics as NextGoogleAnalytics } from "@next/third-parties/goog * https://nextjs.org/docs/app/building-your-application/optimizing/third-party-libraries#google-analytics */ export default function GoogleAnalytics() { - if (process.env.NODE_ENV !== "production") { + if (process.env.NODE_ENV !== 'production') { return null; } diff --git a/src/analytics/open-panel-analytics.tsx b/src/analytics/open-panel-analytics.tsx index d83f9fe..b2badf4 100644 --- a/src/analytics/open-panel-analytics.tsx +++ b/src/analytics/open-panel-analytics.tsx @@ -1,4 +1,4 @@ -import { OpenPanelComponent } from "@openpanel/nextjs"; +import { OpenPanelComponent } from '@openpanel/nextjs'; /** * OpenPanel Analytics (https://openpanel.dev) @@ -6,7 +6,7 @@ import { OpenPanelComponent } from "@openpanel/nextjs"; * https://docs.openpanel.dev/docs/sdks/nextjs#options */ export default function OpenPanelAnalytics() { - if (process.env.NODE_ENV !== "production") { + if (process.env.NODE_ENV !== 'production') { return null; } diff --git a/src/analytics/plausible-analytics.tsx b/src/analytics/plausible-analytics.tsx index ecfa7ff..82422b1 100644 --- a/src/analytics/plausible-analytics.tsx +++ b/src/analytics/plausible-analytics.tsx @@ -1,6 +1,6 @@ -"use client"; +'use client'; -import Script from "next/script"; +import Script from 'next/script'; /** * Plausible Analytics @@ -8,7 +8,7 @@ import Script from "next/script"; * https://plausible.io */ export function PlausibleAnalytics() { - if (process.env.NODE_ENV !== "production") { + if (process.env.NODE_ENV !== 'production') { return null; } @@ -23,11 +23,6 @@ export function PlausibleAnalytics() { } return ( -