Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added menu bar for smaller screens and updated some spacing #4

Merged
merged 3 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ This starter kit is built with:

## Step-by-step setup

> **Important:** If you're totally new to Next.js and Paddle, we have a more complete tutorial on our dev docs: [Build and deploy Next.js app with Vercel and Supabase](https://developer.paddle.com/build/vercel-starter-kit?utm_source=dx&utm_medium=paddle-nextjs-starter-kit)
> **Important:** If you're totally new to Next.js and Paddle, we have a more complete tutorial on our dev docs: [Build and deploy Next.js app with Vercel and Supabase](https://developer.paddle.com/build/nextjs-supabase-vercel-starter-kit?utm_source=dx&utm_medium=paddle-nextjs-starter-kit)

### 1. Deploy on Vercel

#### Start deploy

Click this button to clone this repo and create a new project in your Vercel account:

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FPaddleHQ%2Fpaddle-nextjs-starter-kit&env=PADDLE_API_KEY,PADDLE_NOTIFICATION_WEBHOOK_SECRET,NEXT_PUBLIC_PADDLE_ENV,NEXT_PUBLIC_PADDLE_CLIENT_TOKEN&integration-ids=oac_VqOgBHqhEoFTPzGkPd7L0iH6)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FPaddleHQ%2Fpaddle-nextjs-starter-kit&env=PADDLE_API_KEY,PADDLE_NOTIFICATION_WEBHOOK_SECRET,NEXT_PUBLIC_PADDLE_ENV,NEXT_PUBLIC_PADDLE_CLIENT_TOKEN&integration-ids=oac_VqOgBHqhEoFTPzGkPd7L0iH6&external-id=https%3A%2F%2Fgithub.com%2FPaddleHQ%2Fpaddle-nextjs-starter-kit%2Ftree%2Fmain)

You can also [create a new application manually](https://vercel.com/new).

Expand All @@ -63,12 +63,12 @@ Click **Add** to walk through integrating with Supabase. You'll be asked to auth

Then, enter Paddle environment variables:

| Variable | Used for | How to get it |
| ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PADDLE_API_KEY` | An [API key](https://developer.paddle.com/api-reference/about/authentication?utm_source=dx&utm_medium=paddle-nextjs-starter-kit), used for interacting with Paddle data in the backend. For example, syncing customer and subscription data with Supabase. | Go to [**Paddle > Developer tools > Authentication**](https://sandbox-vendors.paddle.com/authentication-v2) and create a new API key. |
| `NEXT_PUBLIC_PADDLE_CLIENT_TOKEN` | A [client-side key](https://developer.paddle.com/api-reference/about/authentication?utm_source=dx&utm_medium=paddle-nextjs-starter-kit), used for interacting with Paddle in the frontend. For example, getting localized prices for pricing pages and opening a checkout. | Go to [**Paddle > Developer tools > Authentication**](https://sandbox-vendors.paddle.com/authentication-v2) and create a new client-side token. |
| `PADDLE_NOTIFICATION_WEBHOOK_SECRET` | A secret key used for verifying that [webhooks](https://developer.paddle.com/webhooks/notification-destinations?utm_source=dx&utm_medium=paddle-nextjs-starter-kit) came from Paddle and haven't been tampered with in transit. Important for security. | Go to [**Paddle > Developer tools > Notifications**](https://sandbox-vendors.paddle.com/notifications), create a new notification destination, then edit to copy the secret key. |
| `NEXT_PUBLIC_PADDLE_ENV` | Environment for our Paddle account. This should match the kind of Paddle account you signed up for. | Enter `sandbox` for sandbox accounts or `production` for live accounts. |
| Variable | Used for | How to get it |
| ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PADDLE_API_KEY` | An [API key](https://developer.paddle.com/api-reference/about/authentication?utm_source=dx&utm_medium=paddle-nextjs-starter-kit), used for interacting with Paddle data in the backend. For example, syncing customer and subscription data with Supabase. | Go to [**Paddle > Developer tools > Authentication**](https://sandbox-vendors.paddle.com/authentication-v2) and create a new API key. |
| `NEXT_PUBLIC_PADDLE_CLIENT_TOKEN` | A [client-side key](https://developer.paddle.com/api-reference/about/authentication?utm_source=dx&utm_medium=paddle-nextjs-starter-kit), used for interacting with Paddle in the frontend. For example, getting localized prices for pricing pages and opening a checkout. | Go to [**Paddle > Developer tools > Authentication**](https://sandbox-vendors.paddle.com/authentication-v2) and create a new client-side token. |
| `PADDLE_NOTIFICATION_WEBHOOK_SECRET` | A secret key used for verifying that [webhooks](https://developer.paddle.com/webhooks/notification-destinations?utm_source=dx&utm_medium=paddle-nextjs-starter-kit) came from Paddle and haven't been tampered with in transit. Important for security. | Go to [**Paddle > Developer tools > Notifications**](https://sandbox-vendors.paddle.com/notifications), create a new notification destination for `https://<PROJECTNAME>.vercel.app/api/webhook`, then edit to copy the secret key. |
| `NEXT_PUBLIC_PADDLE_ENV` | Environment for our Paddle account. This should match the kind of Paddle account you signed up for. | Enter `sandbox` for sandbox accounts or `production` for live accounts. |

You can use `https://<PROJECTNAME>.vercel.app/api/webhook` as the endpoint URL for your notification destination, where `<PROJECTNAME>` is the name of your project in Vercel. This may change if your project name isn't unique, but we can update this later.

Expand Down Expand Up @@ -163,6 +163,6 @@ For help, contact the Paddle DX team at `[email protected]`.

## Learn more

- [Build and deploy Next.js app with Vercel and Supabase](https://developer.paddle.com/build/vercel-starter-kit?utm_source=dx&utm_medium=paddle-nextjs-starter-kit)
- [Build and deploy Next.js app with Vercel and Supabase](https://developer.paddle.com/build/nextjs-supabase-vercel-starter-kit?utm_source=dx&utm_medium=paddle-nextjs-starter-kit)
- [Paddle API reference](https://developer.paddle.com/api-reference/overview?utm_source=dx&utm_medium=paddle-nextjs-starter-kit)
- [Sign up for Paddle Billing](https://sandbox-login.paddle.com/signup?utm_source=dx&utm_medium=paddle-nextjs-starter-kit)
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ export async function DashboardSubscriptionCardGroup() {
</Button>
</CardTitle>
</CardHeader>
<CardContent className={'p-0 pt-6'}>
<CardContent className={'p-0 pt-6 @container'}>
{subscriptions?.data ? (
<SubscriptionCards className={'grid-cols-2 gap-6'} subscriptions={subscriptions.data.slice(0, 2) ?? []} />
<SubscriptionCards
className={'grid-cols-1 gap-6 @[600px]:grid-cols-2'}
subscriptions={subscriptions.data.slice(0, 2) ?? []}
/>
) : (
<ErrorContent />
)}
Expand Down
4 changes: 2 additions & 2 deletions src/components/dashboard/landing/dashboard-landing-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import { DashboardTeamMembersCard } from '@/components/dashboard/landing/compone
export function DashboardLandingPage() {
return (
<div className={'grid flex-1 items-start gap-6 p-0 md:grid-cols-1 lg:grid-cols-2 xl:grid-cols-3'}>
<div className={'grid auto-rows-max items-start gap-8 lg:col-span-2'}>
<div className={'grid auto-rows-max items-start gap-6 lg:col-span-2'}>
<DashboardUsageCardGroup />
<DashboardSubscriptionCardGroup />
</div>
<div className={'grid auto-rows-max items-start gap-8'}>
<div className={'grid auto-rows-max items-start gap-6'}>
<DashboardTeamMembersCard />
<DashboardTutorialCard />
</div>
Expand Down
6 changes: 5 additions & 1 deletion src/components/dashboard/layout/dashboard-page-header.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Separator } from '@/components/ui/separator';
import { MobileSidebar } from '@/components/dashboard/layout/mobile-sidebar';

interface Props {
pageTitle: string;
Expand All @@ -7,7 +8,10 @@ interface Props {
export function DashboardPageHeader({ pageTitle }: Props) {
return (
<div>
<h1 className="text-lg font-semibold md:text-4xl">{pageTitle}</h1>
<div className={'flex items-center gap-6'}>
<MobileSidebar />
<h1 className="text-lg font-semibold md:text-4xl">{pageTitle}</h1>
</div>
<Separator className={'relative bg-border my-8 dashboard-header-highlight'} />
</div>
);
Expand Down
22 changes: 22 additions & 0 deletions src/components/dashboard/layout/mobile-sidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet';
import { Button } from '@/components/ui/button';
import { Menu } from 'lucide-react';
import { Sidebar } from '@/components/dashboard/layout/sidebar';
import { SidebarUserInfo } from '@/components/dashboard/layout/sidebar-user-info';

export function MobileSidebar() {
return (
<Sheet>
<SheetTrigger asChild>
<Button variant="ghost" size="icon" className="shrink-0 md:hidden">
<Menu className="h-5 w-5" />
<span className="sr-only">Toggle navigation menu</span>
</Button>
</SheetTrigger>
<SheetContent side="left" className="flex flex-col">
<Sidebar />
<SidebarUserInfo />
</SheetContent>
</Sheet>
);
}
14 changes: 8 additions & 6 deletions src/components/dashboard/payments/components/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,14 @@ export const columns: ColumnDef<Transaction>[] = [
size: columnSize,
cell: ({ row }) => {
return (
<div className={'whitespace-nowrap flex gap-1'}>
<span className={'font-semibold'}>{getPaymentReason(row.original.origin)}</span>
<span className={'font-medium'}>{row.original.details?.lineItems[0].product?.name}</span>
{row.original.details?.lineItems && row.original.details?.lineItems.length > 1 && (
<span className={'font-medium'}>+{row.original.details?.lineItems.length - 1} more</span>
)}
<div className={'max-w-[250px]'}>
<div className={'whitespace-nowrap flex gap-1 truncate'}>
<span className={'font-semibold'}>{getPaymentReason(row.original.origin)}</span>
<span className={'font-medium truncate'}>{row.original.details?.lineItems[0].product?.name}</span>
{row.original.details?.lineItems && row.original.details?.lineItems.length > 1 && (
<span className={'font-medium'}>+{row.original.details?.lineItems.length - 1} more</span>
)}
</div>
</div>
);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function PaymentMethodSection({ transactions, updatePaymentMethodUrl }: P
return null;
}
return (
<div className={'flex gap-6 pt-6 items-end justify-between'}>
<div className={'flex gap-6 pt-6 items-end justify-between @16xs:flex-wrap'}>
<div className={'flex flex-col gap-4'}>
<div className={'text-base text-secondary leading-4 whitespace-nowrap'}>Payment method</div>
<div className={'flex gap-1 items-end'}>
Expand All @@ -31,7 +31,7 @@ export function PaymentMethodSection({ transactions, updatePaymentMethodUrl }: P
<div>
<Button asChild={true} size={'sm'} className={'text-sm rounded-sm border-border'} variant={'outline'}>
<Link target={'_blank'} href={updatePaymentMethodUrl}>
Manage
Update
</Link>
</Button>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function SubscriptionCards({ subscriptions, className }: Props) {
{subscriptions.map((subscription) => {
const subscriptionItem = subscription.items[0];
const price = subscriptionItem.quantity * parseFloat(subscriptionItem.price.unitPrice.amount);
const formattedPrice = parseMoney(price.toString(), subscriptionItem.price.unitPrice.currencyCode);
const formattedPrice = parseMoney(price.toString(), subscription.currencyCode);
const frequency =
subscription.billingCycle.frequency === 1
? `/${subscription.billingCycle.interval}`
Expand All @@ -30,7 +30,12 @@ export function SubscriptionCards({ subscriptions, className }: Props) {
<Card key={subscription.id} className={'bg-background/50 backdrop-blur-[24px] border-border p-6'}>
<CardHeader className="p-0 space-y-0">
<CardTitle className="flex flex-col justify-between items-start mb-6">
<div className={'flex mb-4 justify-between w-full'}>
<div
className={cn('flex mb-4 w-full', {
'justify-between': subscriptionItem.product.imageUrl,
'justify-end': !subscriptionItem.product.imageUrl,
})}
>
{subscriptionItem.product.imageUrl && (
<Image
src={subscriptionItem.product.imageUrl}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ export function SubscriptionDetail({ subscriptionId }: Props) {
<SubscriptionHeader subscription={subscription.data} />
<Separator className={'relative bg-border mb-8 dashboard-header-highlight'} />
</div>
<div className={'grid flex-1 items-start gap-8 xl:grid-cols-4'}>
<div className={'grid auto-rows-max items-start gap-8'}>
<div className={'grid gap-6 grid-cols-1 xl:grid-cols-6'}>
<div className={'grid auto-rows-max gap-6 grid-cols-1 xl:col-span-2'}>
<SubscriptionNextPaymentCard transactions={transactions.data} subscription={subscription.data} />
<SubscriptionPastPaymentsCard transactions={transactions.data} subscriptionId={subscriptionId} />
</div>
<div className={'grid auto-rows-max items-start gap-8 xl:col-span-3'}>
<div className={'grid auto-rows-max gap-6 grid-cols-1 xl:col-span-4'}>
<SubscriptionLineItems subscription={subscription.data} />
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { Status } from '@/components/shared/status/status';
import { parseMoney } from '@/utils/paddle/parse-money';
import dayjs from 'dayjs';
import { SubscriptionHeaderActionButton } from '@/components/dashboard/subscriptions/components/subscription-header-action-button';
import { Alert } from '@/components/ui/alert';
import { SubscriptionAlerts } from '@/components/dashboard/subscriptions/components/subscription-alerts';
import { MobileSidebar } from '@/components/dashboard/layout/mobile-sidebar';

interface Props {
subscription: Subscription;
Expand All @@ -24,16 +24,17 @@ export function SubscriptionHeader({ subscription }: Props) {
const formattedStartedDate = dayjs(subscription.startedAt).format('MMM DD, YYYY');

return (
<div className={'flex justify-between items-center'}>
<div className={'flex justify-between items-start sm:items-center flex-col sm:flex-row mb-6 sm:mb-0'}>
<div className={'flex flex-col w-full'}>
<SubscriptionAlerts subscription={subscription} />
<div className={'flex items-center gap-5'}>
<MobileSidebar />
{subscriptionItem.product.imageUrl && (
<Image src={subscriptionItem.product.imageUrl} alt={subscriptionItem.product.name} width={48} height={48} />
)}
<span className={'text-4xl leading-9 font-medium'}>{subscriptionItem.product.name}</span>
</div>
<div className={'flex items-center gap-8 py-8 pb-6'}>
<div className={'flex items-center gap-6 py-8 pb-6 flex-wrap md:flex-wrap'}>
<div className={'flex gap-1 items-end'}>
<span className={'text-4xl leading-9 font-medium'}>{formattedPrice}</span>
<span className={'text-secondary text-sm leading-[14px] font-medium'}>{frequency}</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ export function SubscriptionNextPaymentCard({ subscription, transactions }: Prop
return null;
}
return (
<Card className={'bg-background/50 backdrop-blur-[24px] border-border p-6'}>
<Card className={'bg-background/50 backdrop-blur-[24px] border-border p-6 @container'}>
<div className={'flex gap-6 flex-col border-border border-b pb-6'}>
<div className={'text-xl font-medium'}>Next payment</div>
<div className={'flex gap-1 items-end'}>
<div className={'flex gap-1 items-end @16xs:flex-wrap'}>
<span className={'text-xl leading-5 font-medium text-primary'}>
{parseMoney(subscription?.nextTransaction?.details.totals.total, subscription?.currencyCode)}
</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export function MultipleSubscriptionsView({ subscriptions }: Props) {
return (
<>
<DashboardPageHeader pageTitle={'Subscriptions'} />
<SubscriptionCards className={'grid-cols-1 lg:grid-cols-3 gap-8'} subscriptions={subscriptions} />
<SubscriptionCards className={'grid-cols-1 lg:grid-cols-3 gap-6'} subscriptions={subscriptions} />
</>
);
}
2 changes: 1 addition & 1 deletion src/components/home/footer/powered-by-paddle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function PoweredByPaddle() {
<span className={'text-sm leading-[14px]'}>A Next.js template by</span>
<Image src={'/assets/icons/logo/paddle-white-logo.svg'} alt={'Paddle logo'} width={54} height={14} />
</div>
<div className={'flex justify-center items-center gap-2'}>
<div className={'flex justify-center items-center gap-2 flex-wrap md:flex-nowrap'}>
<Link className={'text-sm leading-[14px]'} href={'https://paddle.com'} target={'_blank'}>
<span className={'flex items-center gap-1'}>
Explore Paddle
Expand Down
Loading
Loading