diff --git a/biome.json b/biome.json index 2b9b070..47a3d37 100644 --- a/biome.json +++ b/biome.json @@ -22,6 +22,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" ] }, @@ -78,4 +80,4 @@ "semicolons": "always" } } -} \ No newline at end of file +} diff --git a/content-collections.ts b/content-collections.ts index f283c77..68edc69 100644 --- a/content-collections.ts +++ b/content-collections.ts @@ -1,4 +1,4 @@ -import path from 'path'; +import path from 'node:path'; import { DEFAULT_LOCALE, LOCALES } from '@/i18n/routing'; import { defineCollection, defineConfig } from '@content-collections/core'; import { @@ -313,14 +313,14 @@ function extractLocaleAndBase(fileName: string): { if (parts.length === 1) { // Simple filename without locale: xxx return { locale: DEFAULT_LOCALE, base: parts[0] }; - } else if (parts.length === 2 && LOCALES.includes(parts[1])) { + } + if (parts.length === 2 && LOCALES.includes(parts[1])) { // Filename with locale: xxx.zh return { locale: parts[1], base: parts[0] }; - } else { - // Unexpected format, use first part as base and default locale - console.warn(`Unexpected filename format: ${fileName}`); - return { locale: DEFAULT_LOCALE, base: parts[0] }; } + // Unexpected format, use first part as base and default locale + console.warn(`Unexpected filename format: ${fileName}`); + return { locale: DEFAULT_LOCALE, base: parts[0] }; } export default defineConfig({ diff --git a/package.json b/package.json index 3a079ce..b02dcd1 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "build": "content-collections build && next build", "start": "next start", "lint": "biome check --write .", + "lint:fix": "biome check --fix --unsafe .", "format": "biome format --write .", "db:generate": "drizzle-kit generate", "db:migrate": "drizzle-kit migrate", diff --git a/src/app/api/storage/presigned-url/route.ts b/src/app/api/storage/presigned-url/route.ts index 41aa9a5..8730e60 100644 --- a/src/app/api/storage/presigned-url/route.ts +++ b/src/app/api/storage/presigned-url/route.ts @@ -1,4 +1,4 @@ -import { randomUUID } from 'crypto'; +import { randomUUID } from 'node:crypto'; import { getPresignedUploadUrl } from '@/storage'; import { StorageError } from '@/storage/types'; import { type NextRequest, NextResponse } from 'next/server'; diff --git a/src/components/blocks/features/features2.tsx b/src/components/blocks/features/features2.tsx index 1238675..716118c 100644 --- a/src/components/blocks/features/features2.tsx +++ b/src/components/blocks/features/features2.tsx @@ -48,7 +48,7 @@ export default function Features2Section() { return (
-
+

diff --git a/src/components/blocks/integration/integration2.tsx b/src/components/blocks/integration/integration2.tsx index 6eebaf0..8f42610 100644 --- a/src/components/blocks/integration/integration2.tsx +++ b/src/components/blocks/integration/integration2.tsx @@ -21,7 +21,7 @@ export default function Integration2Section() {
-
+
diff --git a/src/components/blocks/testimonials/testimonials.tsx b/src/components/blocks/testimonials/testimonials.tsx index f9f265b..05dfe01 100644 --- a/src/components/blocks/testimonials/testimonials.tsx +++ b/src/components/blocks/testimonials/testimonials.tsx @@ -127,7 +127,7 @@ export default function TestimonialsSection() { width="120" height="120" /> - +
diff --git a/src/components/contact/contact-form-card.tsx b/src/components/contact/contact-form-card.tsx index 5db7577..b084d62 100644 --- a/src/components/contact/contact-form-card.tsx +++ b/src/components/contact/contact-form-card.tsx @@ -69,7 +69,7 @@ export function ContactFormCard() { // Submit form data using the contact server action const result = await sendMessageAction(values); - if (result && result.data?.success) { + if (result?.data?.success) { toast.success(t('success')); form.reset(); } else { diff --git a/src/components/dashboard/data-table.tsx b/src/components/dashboard/data-table.tsx index 65c72ce..feb8b20 100644 --- a/src/components/dashboard/data-table.tsx +++ b/src/components/dashboard/data-table.tsx @@ -607,16 +607,16 @@ export function DataTable({ value="past-performance" className="flex flex-col px-4 lg:px-6" > -
+
-
+
-
+
); diff --git a/src/components/icons/discord.tsx b/src/components/icons/discord.tsx index 991c911..b02767a 100644 --- a/src/components/icons/discord.tsx +++ b/src/components/icons/discord.tsx @@ -16,7 +16,7 @@ export function DiscordIcon(props: SVGProps) { + /> ); } diff --git a/src/components/icons/facebook.tsx b/src/components/icons/facebook.tsx index d4da4ea..d10a599 100644 --- a/src/components/icons/facebook.tsx +++ b/src/components/icons/facebook.tsx @@ -16,7 +16,7 @@ export function FacebookIcon(props: SVGProps) { + /> ); } diff --git a/src/components/icons/instagram.tsx b/src/components/icons/instagram.tsx index b83f107..5c5029c 100644 --- a/src/components/icons/instagram.tsx +++ b/src/components/icons/instagram.tsx @@ -16,7 +16,7 @@ export function InstagramIcon(props: SVGProps) { + /> ); } diff --git a/src/components/icons/linkedin.tsx b/src/components/icons/linkedin.tsx index 524f1af..7b5291a 100644 --- a/src/components/icons/linkedin.tsx +++ b/src/components/icons/linkedin.tsx @@ -16,7 +16,7 @@ export function LinkedInIcon(props: SVGProps) { + /> ); } diff --git a/src/components/icons/tiktok.tsx b/src/components/icons/tiktok.tsx index 963681a..739df53 100644 --- a/src/components/icons/tiktok.tsx +++ b/src/components/icons/tiktok.tsx @@ -16,7 +16,7 @@ export function TikTokIcon(props: SVGProps) { + /> ); } diff --git a/src/components/icons/twitter.tsx b/src/components/icons/twitter.tsx index 91bc952..72ea57a 100644 --- a/src/components/icons/twitter.tsx +++ b/src/components/icons/twitter.tsx @@ -16,7 +16,7 @@ export function TwitterIcon(props: SVGProps) { + /> ); } diff --git a/src/components/layout/footer.tsx b/src/components/layout/footer.tsx index e2b3161..12dc1ab 100644 --- a/src/components/layout/footer.tsx +++ b/src/components/layout/footer.tsx @@ -39,21 +39,20 @@ export function Footer({ className }: React.HTMLAttributes) { {/* social links */}
- {socialLinks && - socialLinks.map((link) => ( - - {link.title} - {link.icon ? link.icon : null} - - ))} + > + {link.title} + {link.icon ? link.icon : null} + + ))}
@@ -63,33 +62,32 @@ export function Footer({ className }: React.HTMLAttributes) {
{/* footer links */} - {footerLinks && - footerLinks.map((section) => ( -
- - {section.title} - -
    - {section.items?.map( - (item) => - item.href && ( -
  • - - {item.title} - -
  • - ) - )} -
-
- ))} + {footerLinks?.map((section) => ( +
+ + {section.title} + +
    + {section.items?.map( + (item) => + item.href && ( +
  • + + {item.title} + +
  • + ) + )} +
+
+ ))}
diff --git a/src/components/layout/navbar-mobile.tsx b/src/components/layout/navbar-mobile.tsx index 287135f..c4b4080 100644 --- a/src/components/layout/navbar-mobile.tsx +++ b/src/components/layout/navbar-mobile.tsx @@ -177,104 +177,101 @@ function MainMobileMenu({ userLoggedIn, onLinkClicked }: MainMobileMenuProps) { {/* main menu */}
    - {menuLinks && - menuLinks.map((item) => { - const isActive = item.href - ? localePathname.startsWith(item.href) - : item.items?.some( - (subItem) => - subItem.href && localePathname.startsWith(subItem.href) - ); + {menuLinks?.map((item) => { + const isActive = item.href + ? localePathname.startsWith(item.href) + : item.items?.some( + (subItem) => + subItem.href && localePathname.startsWith(subItem.href) + ); - return ( -
  • - {item.items ? ( - - setExpanded((prev) => ({ - ...prev, - [item.title.toLowerCase()]: isOpen, - })) - } - > - - - - -
      - {item.items.map((subItem) => { - const isSubItemActive = - subItem.href && - localePathname.startsWith(subItem.href); + return ( +
    • + {item.items ? ( + + setExpanded((prev) => ({ + ...prev, + [item.title.toLowerCase()]: isOpen, + })) + } + > + + + + +
        + {item.items.map((subItem) => { + const isSubItemActive = + subItem.href && + localePathname.startsWith(subItem.href); - return ( -
      • - + +
        -
        +
        + - {subItem.icon ? subItem.icon : null} -
        -
        - - {subItem.title} - - {/* hide description for now */} - {/* {subItem.description && ( + {subItem.title} + + {/* hide description for now */} + {/* {subItem.description && (

        )} */} -

        - {subItem.external && ( - - )} - -
      • - ); - })} -
      -
      -
      - ) : ( - -
      - {item.title} -
      -
      - )} -
    • - ); - })} +
+ {subItem.external && ( + + )} + + + ); + })} + + + + ) : ( + +
+ {item.title} +
+
+ )} + + ); + })} {/* bottom buttons */} diff --git a/src/components/layout/navbar.tsx b/src/components/layout/navbar.tsx index a504737..4b21804 100644 --- a/src/components/layout/navbar.tsx +++ b/src/components/layout/navbar.tsx @@ -78,136 +78,132 @@ export function Navbar({ scroll }: NavBarProps) {
- {menuLinks && - menuLinks.map((item, index) => - item.items ? ( - - - subItem.href - ? localePathname.startsWith(subItem.href) - : false - ) - ? 'true' - : undefined - } - className={customNavigationMenuTriggerStyle} - > - {item.title} - - -
    - {item.items && - item.items.map((subItem, subIndex) => { - const isSubItemActive = - subItem.href && - localePathname.startsWith(subItem.href); - return ( -
  • - - + item.items ? ( + + + subItem.href + ? localePathname.startsWith(subItem.href) + : false + ) + ? 'true' + : undefined + } + className={customNavigationMenuTriggerStyle} + > + {item.title} + + +
      + {item.items?.map((subItem, subIndex) => { + const isSubItemActive = + subItem.href && + localePathname.startsWith(subItem.href); + return ( +
    • + + +
      + {subItem.icon ? subItem.icon : null} +
      +
      +
      + {subItem.title} +
      + {subItem.description && (
      - {subItem.icon ? subItem.icon : null} + {subItem.description}
      -
      -
      - {subItem.title} -
      - {subItem.description && ( -
      - {subItem.description} -
      - )} -
      - {subItem.external && ( - + )} +
      + {subItem.external && ( + -
      -
    • - ); - })} -
    -
    -
    - ) : ( - - + )} +
    +
    +
  • + ); + })} +
+
+
+ ) : ( + + + - - {item.title} - - - - ) - )} + {item.title} + + + + ) + )}
diff --git a/src/components/layout/user-button-mobile.tsx b/src/components/layout/user-button-mobile.tsx index 9924972..21e8afd 100644 --- a/src/components/layout/user-button-mobile.tsx +++ b/src/components/layout/user-button-mobile.tsx @@ -85,22 +85,21 @@ export function UserButtonMobile({ user }: UserButtonProps) {
    - {avatarLinks && - avatarLinks.map((item) => ( -
  • ( +
  • + - - {item.icon ? item.icon : null} -

    {item.title}

    -
    -
  • - ))} + {item.icon ? item.icon : null} +

    {item.title}

    + + + ))}
  • 0) { return result[0].id; - } else { - console.warn(`No user found with customerId ${customerId}`); } + console.warn(`No user found with customerId ${customerId}`); return undefined; } catch (error) { @@ -611,11 +610,10 @@ export class StripeProvider implements PaymentProvider { `<< Failed to create one-time payment record for user ${userId}` ); return; - } else { - console.log( - `<< Created one-time payment record for user ${userId}, price: ${priceId}` - ); } + console.log( + `<< Created one-time payment record for user ${userId}, price: ${priceId}` + ); } /** diff --git a/src/storage/index.ts b/src/storage/index.ts index 7f9bf3f..3a2c745 100644 --- a/src/storage/index.ts +++ b/src/storage/index.ts @@ -132,56 +132,55 @@ export const uploadFileFromBrowser = async ( return await response.json(); } // For larger files, use pre-signed URL - else { - // First, get a pre-signed URL - const presignedUrlResponse = await fetch(API_STORAGE_PRESIGNED_URL, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - filename: file.name, - contentType: file.type, - folder: folder || '', - }), - }); - if (!presignedUrlResponse.ok) { - const error = await presignedUrlResponse.json(); - throw new Error(error.message || 'Failed to get pre-signed URL'); - } + // First, get a pre-signed URL + const presignedUrlResponse = await fetch(API_STORAGE_PRESIGNED_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + filename: file.name, + contentType: file.type, + folder: folder || '', + }), + }); - const { url, key } = await presignedUrlResponse.json(); - - // Then upload directly to the storage provider - const uploadResponse = await fetch(url, { - method: 'PUT', - headers: { - 'Content-Type': file.type, - }, - body: file, - }); - - if (!uploadResponse.ok) { - throw new Error('Failed to upload file using pre-signed URL'); - } - - // Get the public URL - const fileUrlResponse = await fetch(API_STORAGE_FILE_URL, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ key }), - }); - - if (!fileUrlResponse.ok) { - const error = await fileUrlResponse.json(); - throw new Error(error.message || 'Failed to get file URL'); - } - - return await fileUrlResponse.json(); + if (!presignedUrlResponse.ok) { + const error = await presignedUrlResponse.json(); + throw new Error(error.message || 'Failed to get pre-signed URL'); } + + const { url, key } = await presignedUrlResponse.json(); + + // Then upload directly to the storage provider + const uploadResponse = await fetch(url, { + method: 'PUT', + headers: { + 'Content-Type': file.type, + }, + body: file, + }); + + if (!uploadResponse.ok) { + throw new Error('Failed to upload file using pre-signed URL'); + } + + // Get the public URL + const fileUrlResponse = await fetch(API_STORAGE_FILE_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ key }), + }); + + if (!fileUrlResponse.ok) { + const error = await fileUrlResponse.json(); + throw new Error(error.message || 'Failed to get file URL'); + } + + return await fileUrlResponse.json(); } catch (error) { const message = error instanceof Error diff --git a/src/storage/provider/s3.ts b/src/storage/provider/s3.ts index 6260de2..d26328e 100644 --- a/src/storage/provider/s3.ts +++ b/src/storage/provider/s3.ts @@ -1,4 +1,4 @@ -import { randomUUID } from 'crypto'; +import { randomUUID } from 'node:crypto'; import { GetObjectCommand, PutObjectCommand,