From c837d4f592972eda1622526c585f60383def28da Mon Sep 17 00:00:00 2001 From: Giancarlo Buomprisco Date: Tue, 31 Mar 2026 21:24:37 +0800 Subject: [PATCH] chore: bump version to 3.1.1 in package.json; refactor mobile navigation components and improve layout structure (#472) - Updated version to 3.1.1 in package.json. - Refactored mobile navigation components to enhance structure and usability. - Adjusted layout components to improve responsiveness and visual consistency. - Introduced shared mobile navigation components for better code reuse. --- .../_components/home-account-selector.tsx | 4 +- .../_components/home-menu-navigation.tsx | 8 +- .../_components/home-mobile-navigation.tsx | 82 ++------- .../(user)/_components/home-page-header.tsx | 18 +- .../_lib/server/user-billing.service.ts | 2 +- apps/web/app/[locale]/home/(user)/layout.tsx | 8 +- .../team-account-accounts-selector.tsx | 6 +- .../team-account-layout-mobile-navigation.tsx | 155 ++++-------------- .../team-account-layout-page-header.tsx | 19 ++- .../team-account-navigation-menu.tsx | 26 +-- .../_lib/server/team-billing.service.ts | 2 +- .../app/[locale]/home/[account]/layout.tsx | 24 ++- .../components/mobile-navigation-shared.tsx | 78 +++++++++ package.json | 2 +- .../src/schema/stripe-client-env.schema.ts | 4 +- .../src/services/create-stripe-checkout.ts | 26 +-- packages/ui/src/makerkit/page.tsx | 66 +++++--- 17 files changed, 270 insertions(+), 260 deletions(-) create mode 100644 apps/web/components/mobile-navigation-shared.tsx diff --git a/apps/web/app/[locale]/home/(user)/_components/home-account-selector.tsx b/apps/web/app/[locale]/home/(user)/_components/home-account-selector.tsx index 710f37d5c..181f23138 100644 --- a/apps/web/app/[locale]/home/(user)/_components/home-account-selector.tsx +++ b/apps/web/app/[locale]/home/(user)/_components/home-account-selector.tsx @@ -22,13 +22,15 @@ export function HomeAccountSelector(props: { }>; userId: string; + collapsed?: boolean; }) { const router = useRouter(); const context = useContext(SidebarContext); + const collapsed = props.collapsed ?? !context?.open; return (
- +
+ +
{routes.map((route) => ( @@ -54,7 +56,9 @@ export function HomeMenuNavigation(props: { workspace: UserWorkspace }) { - +
+ +
diff --git a/apps/web/app/[locale]/home/(user)/_components/home-mobile-navigation.tsx b/apps/web/app/[locale]/home/(user)/_components/home-mobile-navigation.tsx index 84908a6e2..e3687b36f 100644 --- a/apps/web/app/[locale]/home/(user)/_components/home-mobile-navigation.tsx +++ b/apps/web/app/[locale]/home/(user)/_components/home-mobile-navigation.tsx @@ -1,15 +1,12 @@ 'use client'; -import Link from 'next/link'; - -import { LogOut, Menu } from 'lucide-react'; +import { Menu } from 'lucide-react'; import { useSignOut } from '@kit/supabase/hooks/use-sign-out'; import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, - DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, @@ -17,6 +14,10 @@ import { import { If } from '@kit/ui/if'; import { Trans } from '@kit/ui/trans'; +import { + MobileNavRouteLinks, + MobileNavSignOutItem, +} from '~/components/mobile-navigation-shared'; import featuresFlagConfig from '~/config/feature-flags.config'; import { personalAccountNavigationConfig } from '~/config/personal-account-navigation.config'; @@ -27,25 +28,6 @@ import type { UserWorkspace } from '../_lib/server/load-user-workspace'; export function HomeMobileNavigation(props: { workspace: UserWorkspace }) { const signOut = useSignOut(); - const Links = personalAccountNavigationConfig.routes.map((item, index) => { - if ('children' in item) { - return item.children.map((child) => { - return ( - - ); - }); - } - - if ('divider' in item) { - return ; - } - }); - return ( @@ -60,6 +42,7 @@ export function HomeMobileNavigation(props: { workspace: UserWorkspace }) { @@ -68,57 +51,16 @@ export function HomeMobileNavigation(props: { workspace: UserWorkspace }) { - {Links} + + + - signOut.mutateAsync()} /> + signOut.mutateAsync()} /> ); } - -function DropdownLink( - props: React.PropsWithChildren<{ - path: string; - label: string; - Icon: React.ReactNode; - }>, -) { - return ( - - {props.Icon} - - - - - - } - key={props.path} - /> - ); -} - -function SignOutDropdownItem( - props: React.PropsWithChildren<{ - onSignOut: () => unknown; - }>, -) { - return ( - - - - - - - - ); -} diff --git a/apps/web/app/[locale]/home/(user)/_components/home-page-header.tsx b/apps/web/app/[locale]/home/(user)/_components/home-page-header.tsx index 209a4f421..8a7255abd 100644 --- a/apps/web/app/[locale]/home/(user)/_components/home-page-header.tsx +++ b/apps/web/app/[locale]/home/(user)/_components/home-page-header.tsx @@ -1,12 +1,26 @@ +import { cookies } from 'next/headers'; + import { PageHeader } from '@kit/ui/page'; -export function HomeLayoutPageHeader( +import { personalAccountNavigationConfig } from '~/config/personal-account-navigation.config'; + +export async function HomeLayoutPageHeader( props: React.PropsWithChildren<{ title: string | React.ReactNode; description: string | React.ReactNode; }>, ) { + const cookieStore = await cookies(); + const layoutStyleCookie = cookieStore.get('layout-style')?.value; + const displaySidebarTrigger = + (layoutStyleCookie ?? personalAccountNavigationConfig.style) === 'sidebar'; + return ( - {props.children} + + {props.children} + ); } diff --git a/apps/web/app/[locale]/home/(user)/billing/_lib/server/user-billing.service.ts b/apps/web/app/[locale]/home/(user)/billing/_lib/server/user-billing.service.ts index 321087cfe..bd7dff861 100644 --- a/apps/web/app/[locale]/home/(user)/billing/_lib/server/user-billing.service.ts +++ b/apps/web/app/[locale]/home/(user)/billing/_lib/server/user-billing.service.ts @@ -109,7 +109,7 @@ class UserBillingService { planId, customerId, accountId, - error: message + error: message, }, `Checkout session not created due to an error`, ); diff --git a/apps/web/app/[locale]/home/(user)/layout.tsx b/apps/web/app/[locale]/home/(user)/layout.tsx index 805052b26..2a7cef967 100644 --- a/apps/web/app/[locale]/home/(user)/layout.tsx +++ b/apps/web/app/[locale]/home/(user)/layout.tsx @@ -52,7 +52,7 @@ async function SidebarLayout({ children }: React.PropsWithChildren) { - + @@ -75,7 +75,7 @@ async function HeaderLayout({ children }: React.PropsWithChildren) { - + @@ -92,7 +92,9 @@ function MobileNavigation({ }) { return ( <> - +
+ +
diff --git a/apps/web/app/[locale]/home/[account]/_components/team-account-accounts-selector.tsx b/apps/web/app/[locale]/home/[account]/_components/team-account-accounts-selector.tsx index c67e437ad..b934931e0 100644 --- a/apps/web/app/[locale]/home/[account]/_components/team-account-accounts-selector.tsx +++ b/apps/web/app/[locale]/home/[account]/_components/team-account-accounts-selector.tsx @@ -1,9 +1,11 @@ 'use client'; +import { useContext } from 'react'; + import { useRouter } from 'next/navigation'; import { AccountSelector } from '@kit/accounts/account-selector'; -import { useSidebar } from '@kit/ui/sidebar'; +import { SidebarContext } from '@kit/ui/sidebar'; import featureFlagsConfig from '~/config/feature-flags.config'; import pathsConfig from '~/config/paths.config'; @@ -23,7 +25,7 @@ export function TeamAccountAccountsSelector(params: { }>; }) { const router = useRouter(); - const ctx = useSidebar(); + const ctx = useContext(SidebarContext); return ( ; const features = { - enableTeamAccounts: featureFlagsConfig.enableTeamAccounts, enableTeamCreation: featureFlagsConfig.enableTeamCreation, }; @@ -45,128 +41,21 @@ export const TeamAccountLayoutMobileNavigation = ( accounts: Accounts; }>, ) => { + const router = useRouter(); const signOut = useSignOut(); - const Links = getTeamAccountSidebarConfig(props.account).routes.map( - (item, index) => { - if ('children' in item) { - return item.children.map((child) => { - return ( - - ); - }); - } - - if ('divider' in item) { - return ; - } - }, - ); - return ( - - - - {Links} - - - - signOut.mutateAsync()} /> - - - ); -}; - -function DropdownLink( - props: React.PropsWithChildren<{ - path: string; - label: string; - Icon: React.ReactNode; - }>, -) { - return ( - - {props.Icon} - - - - - - } - /> - ); -} - -function SignOutDropdownItem( - props: React.PropsWithChildren<{ - onSignOut: () => unknown; - }>, -) { - return ( - - - - - - - - ); -} - -function TeamAccountsModal(props: { - accounts: Accounts; - userId: string; - account: string; -}) { - const router = useRouter(); - - return ( - - e.preventDefault()} - > - - - - - - - } - /> - - - - + + + - - + -
-
-
- + + + + + + + + + + + signOut.mutateAsync()} /> + + ); -} +}; diff --git a/apps/web/app/[locale]/home/[account]/_components/team-account-layout-page-header.tsx b/apps/web/app/[locale]/home/[account]/_components/team-account-layout-page-header.tsx index cecd62b30..3d5769e0b 100644 --- a/apps/web/app/[locale]/home/[account]/_components/team-account-layout-page-header.tsx +++ b/apps/web/app/[locale]/home/[account]/_components/team-account-layout-page-header.tsx @@ -1,13 +1,28 @@ +import { cookies } from 'next/headers'; + import { PageHeader } from '@kit/ui/page'; -export function TeamAccountLayoutPageHeader( +import { getTeamAccountSidebarConfig } from '~/config/team-account-navigation.config'; + +export async function TeamAccountLayoutPageHeader( props: React.PropsWithChildren<{ title: string | React.ReactNode; description: string | React.ReactNode; account: string; }>, ) { + const cookieStore = await cookies(); + const layoutStyleCookie = cookieStore.get('layout-style')?.value; + const defaultStyle = getTeamAccountSidebarConfig(props.account).style; + const displaySidebarTrigger = + (layoutStyleCookie ?? defaultStyle) === 'sidebar'; + return ( - {props.children} + + {props.children} + ); } diff --git a/apps/web/app/[locale]/home/[account]/_components/team-account-navigation-menu.tsx b/apps/web/app/[locale]/home/[account]/_components/team-account-navigation-menu.tsx index 500701d72..e48f93e4a 100644 --- a/apps/web/app/[locale]/home/[account]/_components/team-account-navigation-menu.tsx +++ b/apps/web/app/[locale]/home/[account]/_components/team-account-navigation-menu.tsx @@ -41,7 +41,9 @@ export function TeamAccountNavigationMenu(props: { return (
- +
+ +
{routes.map((route) => ( @@ -50,20 +52,22 @@ export function TeamAccountNavigationMenu(props: {
-
+
- ({ - label: account.name, - value: account.slug, - image: account.picture_url, - }))} - /> +
+ ({ + label: account.name, + value: account.slug, + image: account.picture_url, + }))} + /> +
- + -
+
) { const data = use(loadTeamWorkspace(account)); + const accounts = data.accounts.map(({ name, slug, picture_url }) => ({ + label: name, + value: slug, + image: picture_url, + })); + return ( @@ -101,6 +107,20 @@ function HeaderLayout({ + +
+ +
+ +
+ +
+
+ {children}
diff --git a/apps/web/components/mobile-navigation-shared.tsx b/apps/web/components/mobile-navigation-shared.tsx new file mode 100644 index 000000000..04abd33c9 --- /dev/null +++ b/apps/web/components/mobile-navigation-shared.tsx @@ -0,0 +1,78 @@ +'use client'; + +import Link from 'next/link'; + +import { LogOut } from 'lucide-react'; + +import { DropdownMenuItem, DropdownMenuSeparator } from '@kit/ui/dropdown-menu'; +import { Trans } from '@kit/ui/trans'; + +export function MobileNavDropdownLink( + props: React.PropsWithChildren<{ + path: string; + label: string; + Icon?: React.ReactNode; + }>, +) { + return ( + + {props.Icon} + + + + + + } + /> + ); +} + +export function MobileNavSignOutItem(props: { onSignOut: () => unknown }) { + return ( + + + + + + + + ); +} + +export function MobileNavRouteLinks(props: { + routes: Array< + | { + children: Array<{ + path: string; + label: string; + Icon?: React.ReactNode; + }>; + } + | { divider: true } + >; +}) { + return props.routes.map((item, index) => { + if ('children' in item) { + return item.children.map((child) => ( + + )); + } + + if ('divider' in item) { + return ; + } + }); +} diff --git a/package.json b/package.json index 9421358ca..c0f7c5460 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "next-supabase-saas-kit-turbo", - "version": "3.1.0", + "version": "3.1.1", "private": true, "author": { "name": "MakerKit", diff --git a/packages/billing/stripe/src/schema/stripe-client-env.schema.ts b/packages/billing/stripe/src/schema/stripe-client-env.schema.ts index db70f1884..fe36111ad 100644 --- a/packages/billing/stripe/src/schema/stripe-client-env.schema.ts +++ b/packages/billing/stripe/src/schema/stripe-client-env.schema.ts @@ -4,9 +4,7 @@ const isHostedMode = process.env.STRIPE_UI_MODE === 'hosted_page'; export const StripeClientEnvSchema = z .object({ - publishableKey: isHostedMode - ? z.string().optional() - : z.string().min(1), + publishableKey: isHostedMode ? z.string().optional() : z.string().min(1), }) .refine( (schema) => { diff --git a/packages/billing/stripe/src/services/create-stripe-checkout.ts b/packages/billing/stripe/src/services/create-stripe-checkout.ts index db80da937..67f231af5 100644 --- a/packages/billing/stripe/src/services/create-stripe-checkout.ts +++ b/packages/billing/stripe/src/services/create-stripe-checkout.ts @@ -1,19 +1,19 @@ -import type { Stripe } from "stripe"; -import * as z from "zod"; +import type { Stripe } from 'stripe'; +import * as z from 'zod'; -import type { CreateBillingCheckoutSchema } from "@kit/billing/schema"; +import type { CreateBillingCheckoutSchema } from '@kit/billing/schema'; /** * @description If set to true, users can start a trial without entering their credit card details */ const enableTrialWithoutCreditCard = - process.env.STRIPE_ENABLE_TRIAL_WITHOUT_CC === "true"; + process.env.STRIPE_ENABLE_TRIAL_WITHOUT_CC === 'true'; -const UI_MODE_VALUES = ["embedded_page", "hosted_page"] as const; +const UI_MODE_VALUES = ['embedded_page', 'hosted_page'] as const; const uiMode = z .enum(UI_MODE_VALUES) - .default("embedded_page") + .default('embedded_page') .parse(process.env.STRIPE_UI_MODE); /** @@ -37,9 +37,9 @@ export async function createStripeCheckout( // docs: https://stripe.com/docs/billing/subscriptions/build-subscription const mode: Stripe.Checkout.SessionCreateParams.Mode = - params.plan.paymentType === "recurring" ? "subscription" : "payment"; + params.plan.paymentType === 'recurring' ? 'subscription' : 'payment'; - const isSubscription = mode === "subscription"; + const isSubscription = mode === 'subscription'; let trialDays: number | null | undefined = params.plan.trialDays; @@ -53,7 +53,7 @@ export async function createStripeCheckout( ? { trial_settings: { end_behavior: { - missing_payment_method: "cancel" as const, + missing_payment_method: 'cancel' as const, }, }, } @@ -89,10 +89,10 @@ export async function createStripeCheckout( const customerCreation = isSubscription || customer ? ({} as Record) - : { customer_creation: "always" }; + : { customer_creation: 'always' }; const lineItems = params.plan.lineItems.map((item) => { - if (item.type === "metered") { + if (item.type === 'metered') { return { price: item.id, }; @@ -114,7 +114,7 @@ export async function createStripeCheckout( const paymentCollectionMethod = enableTrialWithoutCreditCard && params.plan.trialDays ? { - payment_method_collection: "if_required" as const, + payment_method_collection: 'if_required' as const, } : {}; @@ -138,7 +138,7 @@ function getUrls(params: { }) { const url = `${params.returnUrl}?session_id={CHECKOUT_SESSION_ID}`; - if (params.uiMode === "hosted_page") { + if (params.uiMode === 'hosted_page') { return { success_url: url, cancel_url: params.returnUrl, diff --git a/packages/ui/src/makerkit/page.tsx b/packages/ui/src/makerkit/page.tsx index ed2e88eb5..dcb45f06f 100644 --- a/packages/ui/src/makerkit/page.tsx +++ b/packages/ui/src/makerkit/page.tsx @@ -60,7 +60,7 @@ export function PageMobileNavigation( return (
@@ -73,30 +73,39 @@ function PageWithHeader(props: PageProps) { const { Navigation, Children, MobileNavigation } = getSlotsFromPage(props); return ( -
+
-
- {Navigation} -
+
+
+ {Navigation} +
- {MobileNavigation} + {MobileNavigation} +
-
{Children}
+
+ {Children} +
); @@ -113,7 +122,15 @@ export function PageBody( } export function PageNavigation(props: React.PropsWithChildren) { - return
{props.children}
; + return ( +
+ {props.children} +
+ ); } export function PageDescription(props: React.PropsWithChildren) { @@ -147,16 +164,25 @@ export function PageHeader({ title, description, className, + displaySidebarTrigger = true, }: React.PropsWithChildren<{ className?: string; title?: string | React.ReactNode; description?: string | React.ReactNode; + displaySidebarTrigger?: boolean; }>) { return ( -
-
-
- +
+
+
+ + +
- {children} +
+ {children} +
); }