diff options
| author | Sebastien Castiel <sebastien@castiel.me> | 2024-01-19 11:28:25 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-01-19 11:28:25 -0500 |
| commit | 23524cb94314fd50476ccffe654841d980c713d7 (patch) | |
| tree | 6546d4d8675c1e4bc9d30f6bf526e08eef9b4eaf /src | |
| parent | f9040f8beda10b3969572cf8764e49f9b24b222a (diff) | |
Clean project from marketing content (#50)
* Clean project from marketing content
* Remove some dependencies
Diffstat (limited to 'src')
| -rw-r--r-- | src/app/groups/layout.tsx | 8 | ||||
| -rw-r--r-- | src/app/layout.tsx | 19 | ||||
| -rw-r--r-- | src/app/page.tsx | 174 | ||||
| -rw-r--r-- | src/components/feedback-button/feedback-button-actions.tsx | 23 | ||||
| -rw-r--r-- | src/components/feedback-button/feedback-button-common.tsx | 9 | ||||
| -rw-r--r-- | src/components/feedback-button/feedback-button-email.tsx | 24 | ||||
| -rw-r--r-- | src/components/feedback-button/feedback-button.tsx | 277 | ||||
| -rw-r--r-- | src/lib/env.ts | 5 | ||||
| -rw-r--r-- | src/lib/resend.ts | 5 |
9 files changed, 9 insertions, 535 deletions
diff --git a/src/app/groups/layout.tsx b/src/app/groups/layout.tsx index 75128c8..3b8b10e 100644 --- a/src/app/groups/layout.tsx +++ b/src/app/groups/layout.tsx @@ -1,8 +1,3 @@ -import { - FeedbackButton, - FeedbackModal, -} from '@/components/feedback-button/feedback-button' -import { env } from '@/lib/env' import { PropsWithChildren } from 'react' export default function GroupsLayout({ children }: PropsWithChildren<{}>) { @@ -11,9 +6,6 @@ export default function GroupsLayout({ children }: PropsWithChildren<{}>) { <main className="flex-1 max-w-screen-md w-full mx-auto px-4 py-6 flex flex-col gap-6"> {children} </main> - <FeedbackModal donationUrl={env.STRIPE_DONATION_LINK}> - <FeedbackButton /> - </FeedbackModal> </> ) } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index bff432c..7d95d6e 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,13 +1,10 @@ -import { FeedbackModal } from '@/components/feedback-button/feedback-button' import { ProgressBar } from '@/components/progress-bar' import { ThemeProvider } from '@/components/theme-provider' import { ThemeToggle } from '@/components/theme-toggle' import { Button } from '@/components/ui/button' import { Toaster } from '@/components/ui/toaster' import { env } from '@/lib/env' -import { HeartFilledIcon } from '@radix-ui/react-icons' import type { Metadata, Viewport } from 'next' -import PlausibleProvider from 'next-plausible' import Image from 'next/image' import Link from 'next/link' import './globals.css' @@ -67,9 +64,6 @@ export default function RootLayout({ }) { return ( <html lang="en" suppressHydrationWarning> - {env.PLAUSIBLE_DOMAIN && ( - <PlausibleProvider domain={env.PLAUSIBLE_DOMAIN} trackOutboundLinks /> - )} <body className="pt-16 min-h-[100dvh] flex flex-col items-stretch bg-slate-50 bg-opacity-30 dark:bg-background"> <ThemeProvider attribute="class" @@ -111,7 +105,7 @@ export default function RootLayout({ </div> </header> - {children} + <div className="flex-1 flex flex-col">{children}</div> <footer className="sm:p-8 md:p-16 sm:mt-16 sm:text-sm md:text-base md:mt-32 bg-slate-50 dark:bg-card border-t p-6 mt-8 flex flex-col sm:flex-row sm:justify-between gap-4 text-xs [&_a]:underline"> <div className="flex flex-col space-y-2"> @@ -142,17 +136,6 @@ export default function RootLayout({ contributors </a> </span> - <span> - <FeedbackModal - donationUrl={env.STRIPE_DONATION_LINK} - defaultTab="support" - > - <Button variant="link" className="text-pink-600 -mx-4"> - <HeartFilledIcon className="w-4 h-4 mr-2" /> - Support us - </Button> - </FeedbackModal> - </span> </div> </div> </footer> diff --git a/src/app/page.tsx b/src/app/page.tsx index a7e44d3..4f43c4a 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,17 +1,5 @@ import { Button } from '@/components/ui/button' -import { - BarChartHorizontalBig, - CircleDollarSign, - Divide, - FolderTree, - Github, - List, - LucideIcon, - Share, - ShieldX, - Users, -} from 'lucide-react' -import Image from 'next/image' +import { Github, LucideIcon } from 'lucide-react' import Link from 'next/link' import { ReactNode } from 'react' @@ -28,164 +16,18 @@ export default function HomePage() { & <strong>Family</strong> </h1> <p className="max-w-[42rem] leading-normal text-muted-foreground sm:text-xl sm:leading-8"> - No ads. No account. <br className="sm:hidden" /> Open Source. - Forever Free. + Welcome to your new <strong>Spliit</strong> instance! <br /> + Customize this page by editing <em>src/app/page.tsx</em>. </p> <div className="flex gap-2"> - <Button asChild size="lg"> - <Link - className="inline-flex items-center justify-center text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background bg-primary text-primary-foreground hover:bg-primary/90 h-11 px-8 rounded-md" - href="/groups/create" - > - Create a group - </Link> + <Button asChild> + <Link href="/groups">Go to groups</Link> </Button> - </div> - </div> - </section> - - <section className="bg-slate-50 dark:bg-card py-16 md:py-24 lg:py-32"> - <div className="p-4 flex mx-auto max-w-screen-md flex-col items-center text-center"> - <h2 className="font-bold text-3xl leading-[1.1] sm:text-3xl md:text-6xl"> - Features - </h2> - <p - className="mt-2 md:mt-3 leading-normal text-muted-foreground sm:text-lg sm:leading-7" - style={{ textWrap: 'balance' } as any} - > - Spliit is a minimalist application to track and share expenses with - your friends and family. - </p> - <div className="mt-8 md:mt-6 w-full grid grid-cols-2 sm:grid-cols-3 gap-2 sm:gap-4 text-left"> - <Feature - Icon={Users} - name="Groups" - description="Create a group for a travel, an event, a gift…" - /> - <Feature - Icon={List} - name="Expenses" - description="Create and list expenses in your group." - /> - <Feature - Icon={FolderTree} - name="Categories" - description="Assign categories to your expenses." - /> - <Feature - Icon={Divide} - name="Advanced split" - description="Split expenses by percentage, shares or amount." - /> - <Feature - Icon={Share} - name="Share" - description="Send the group link to participants." - /> - <Feature - Icon={BarChartHorizontalBig} - name="Balances" - description="Visualize how much each participant spent." - /> - <Feature - Icon={CircleDollarSign} - name="Reimbursements" - description="Optimize money transfers between participants." - /> - <Feature - Icon={ShieldX} - name="No ads" - description="No account. No limitation. No problem." - /> - </div> - </div> - </section> - - <section className="py-16 md:py-24 lg:py-32"> - <div className="container flex max-w-screen-md flex-col items-center text-center"> - <h2 className="font-bold text-3xl leading-[1.1] sm:text-3xl md:text-6xl"> - Proudly Open Source - </h2> - <p - className="mt-2 leading-normal text-muted-foreground sm:text-lg sm:leading-7" - style={{ textWrap: 'balance' } as any} - > - Spliit is open source and lives thanks to amazing{' '} - <a - className="underline" - target="_blank" - href="https://github.com/scastiel/spliit2/graphs/contributors" - > - contributors - </a> - ! - </p> - <ul className="flex gap-4 mt-6"> - {[ - { - avatar: - 'https://avatars.githubusercontent.com/u/301948?s=120&v=4', - user: 'scastiel', - name: 'Sebastien Castiel', - }, - { - avatar: - 'https://avatars.githubusercontent.com/u/3932568?s=120&v=4', - user: 'ChristopherJohnston', - name: 'Chris Johnston', - }, - { - avatar: - 'https://avatars.githubusercontent.com/u/11523186?s=120&v=4', - user: 'acuteengle', - name: 'Brandon Eng', - }, - { - avatar: - 'https://avatars.githubusercontent.com/u/24687853?s=120&v=4', - user: 'Max-TheCat', - name: 'Max', - }, - { - avatar: - 'https://avatars.githubusercontent.com/u/10518723?s=120&v=4', - user: 'ankitbahl', - name: 'Ankit Bahl', - }, - { - avatar: - 'https://avatars.githubusercontent.com/u/13032812?s=120&v=4', - user: '174n', - name: 'Ivan Alexandrov', - }, - ].map((contributor) => ( - <li key={contributor.user}> - <a - href={`https://github.com/${contributor.user}`} - target="_blank" - rel="nofollow" - > - <Image - src={contributor.avatar} - width={60} - height={60} - alt={contributor.user} - className="rounded-full border hover:scale-110 transition-transform" - /> - </a> - </li> - ))} - </ul> - <div className="mt-4 md:mt-6"> - <Button asChild variant="secondary" size="lg"> - <a - target="_blank" - rel="noreferrer" - href="https://github.com/scastiel/spliit2" - > + <Button asChild variant="secondary"> + <Link href="https://github.com/spliit-app/spliit"> <Github className="w-4 h-4 mr-2" /> GitHub - </a> + </Link> </Button> </div> </div> diff --git a/src/components/feedback-button/feedback-button-actions.tsx b/src/components/feedback-button/feedback-button-actions.tsx deleted file mode 100644 index 00c8748..0000000 --- a/src/components/feedback-button/feedback-button-actions.tsx +++ /dev/null @@ -1,23 +0,0 @@ -'use server' -import { formSchema } from '@/components/feedback-button/feedback-button-common' -import { FeedbackButtonEmail } from '@/components/feedback-button/feedback-button-email' -import { getResend } from '@/lib/resend' -import { env } from 'process' - -export async function sendFeedback(values: unknown) { - 'use server' - const { email, message } = formSchema.parse(values) - const resend = getResend() - if (!resend || !env.FEEDBACK_EMAIL_FROM || !env.FEEDBACK_EMAIL_TO) { - console.warn( - 'Resend is not properly configured. Feedback email won’t be sent.', - ) - return - } - await resend.emails.send({ - from: env.FEEDBACK_EMAIL_FROM, - to: env.FEEDBACK_EMAIL_TO, - subject: `Spliit: new feedback from ${email || 'anonymous user'}`, - react: <FeedbackButtonEmail email={email} message={message} />, - }) -} diff --git a/src/components/feedback-button/feedback-button-common.tsx b/src/components/feedback-button/feedback-button-common.tsx deleted file mode 100644 index 04567ef..0000000 --- a/src/components/feedback-button/feedback-button-common.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { z } from 'zod' - -export const formSchema = z.object({ - email: z.union([ - z.string().email('Please enter a valid email address.'), - z.string().max(0), - ]), - message: z.string().min(10, 'Please enter at least 10 characters.').max(5000), -}) diff --git a/src/components/feedback-button/feedback-button-email.tsx b/src/components/feedback-button/feedback-button-email.tsx deleted file mode 100644 index ecb8c7d..0000000 --- a/src/components/feedback-button/feedback-button-email.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { Heading } from '@react-email/heading' -import { Html } from '@react-email/html' -import { Preview } from '@react-email/preview' -import { Text } from '@react-email/text' - -type Props = { - email?: string - message: string -} - -export function FeedbackButtonEmail({ email, message }: Props) { - return ( - <Html> - <Preview>New feedback from {email || 'anonymous user'}</Preview> - <Heading>New feedback on Spliit</Heading> - <Text> - Email address: <strong>{email || 'not provided'}</strong> - </Text> - <pre style={{ padding: 16, borderLeft: '2px solid lightgray' }}> - {message} - </pre> - </Html> - ) -} diff --git a/src/components/feedback-button/feedback-button.tsx b/src/components/feedback-button/feedback-button.tsx deleted file mode 100644 index b1b6ae2..0000000 --- a/src/components/feedback-button/feedback-button.tsx +++ /dev/null @@ -1,277 +0,0 @@ -'use client' -import { sendFeedback } from '@/components/feedback-button/feedback-button-actions' -import { formSchema } from '@/components/feedback-button/feedback-button-common' -import { Button, ButtonProps } from '@/components/ui/button' -import { Dialog, DialogContent, DialogTrigger } from '@/components/ui/dialog' -import { Drawer, DrawerContent, DrawerTrigger } from '@/components/ui/drawer' -import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form' -import { Input } from '@/components/ui/input' -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' -import { Textarea } from '@/components/ui/textarea' -import { useToast } from '@/components/ui/use-toast' -import { useMediaQuery } from '@/lib/hooks' -import { zodResolver } from '@hookform/resolvers/zod' -import { Heart, HeartIcon, Loader2, MessageCircle } from 'lucide-react' -import { PropsWithChildren, ReactNode, SetStateAction, useState } from 'react' -import { useForm } from 'react-hook-form' -import * as z from 'zod' - -type FormValues = z.infer<typeof formSchema> - -type Props = { - donationUrl: string - defaultTab?: 'feedback' | 'support' -} - -export function FeedbackModal({ - donationUrl, - defaultTab = 'feedback', - children, -}: PropsWithChildren<Props>) { - const { toast } = useToast() - const isDesktop = useMediaQuery('(min-width: 640px)') - const [open, setOpen] = useState(false) - - async function onSubmit(values: FormValues) { - await sendFeedback(values) - toast({ - title: 'Thank you for your feedback!', - description: - 'We will have a look at it as soon as possible, and will get back to you if needed.', - }) - } - - const Wrapper = isDesktop ? FeedbackDialog : FeedbackDrawer - - return ( - <Wrapper open={open} setOpen={setOpen} button={children}> - <FeedbackContent - onSubmit={async (values) => { - await onSubmit(values) - setOpen(false) - }} - donationUrl={donationUrl} - defaultTab={defaultTab} - /> - </Wrapper> - ) -} - -function FeedbackDrawer({ - children, - open, - setOpen, - button, -}: PropsWithChildren<{ - open: boolean - setOpen: (open: SetStateAction<boolean>) => void - button: ReactNode -}>) { - return ( - <Drawer open={open} onOpenChange={setOpen}> - <DrawerTrigger asChild>{button}</DrawerTrigger> - <DrawerContent> - <div className="p-4">{children}</div> - </DrawerContent> - </Drawer> - ) -} - -function FeedbackDialog({ - children, - open, - setOpen, - button, -}: PropsWithChildren<{ - open: boolean - setOpen: (open: SetStateAction<boolean>) => void - button: ReactNode -}>) { - return ( - <Dialog open={open} onOpenChange={setOpen}> - <DialogTrigger asChild>{button}</DialogTrigger> - <DialogContent> - <div className="pt-4">{children}</div> - </DialogContent> - </Dialog> - ) -} - -function FeedbackContent({ - onSubmit, - donationUrl, - defaultTab, -}: { - onSubmit: (values: FormValues) => Promise<void> - donationUrl: string - defaultTab: 'feedback' | 'support' -}) { - return ( - <Tabs defaultValue={defaultTab}> - <div className="mt-2 mb-6"> - <TabsList className="grid w-full grid-cols-2"> - <TabsTrigger value="feedback">Give feedback</TabsTrigger> - <TabsTrigger value="support">Support us</TabsTrigger> - </TabsList> - </div> - <TabsContent value="feedback"> - <FeedbackForm onSubmit={onSubmit} /> - </TabsContent> - <TabsContent value="support"> - <DonationForm donationUrl={donationUrl} /> - </TabsContent> - </Tabs> - ) -} - -function FeedbackForm({ - onSubmit, -}: { - onSubmit: (values: FormValues) => Promise<void> -}) { - const form = useForm<FormValues>({ - resolver: zodResolver(formSchema), - defaultValues: { email: '', message: '' }, - }) - - const isSubmitting = form.formState.isSubmitting - return ( - <Form {...form}> - <form onSubmit={form.handleSubmit(onSubmit)} className="grid gap-4"> - <div> - <h2 className="text-lg font-semibold leading-none tracking-tight pb-1.5"> - Give us your feedback - </h2> - <p className="text-sm text-muted-foreground"> - We are always working to improve the user experience, and your - feedback helps us a lot. - </p> - </div> - <div className="grid gap-4"> - <FormField - control={form.control} - name="email" - render={({ field }) => ( - <FormItem> - <FormLabel>Your email address</FormLabel> - <FormControl> - <Input - placeholder="your@email.com" - className="text-base" - {...field} - /> - </FormControl> - <FormDescription> - Optional. Provide it if you want us to get back to you. - </FormDescription> - <FormMessage /> - </FormItem> - )} - /> - <FormField - control={form.control} - name="message" - render={({ field }) => ( - <FormItem> - <FormLabel>Your feedback</FormLabel> - <FormControl> - <Textarea - placeholder="Enter your feedback" - className="text-base" - {...field} - /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - </div> - <div className="text-center mt-1"> - <Button type="submit" disabled={isSubmitting}> - {isSubmitting ? ( - <> - <Loader2 className="w-4 h-4 mr-2 animate-spin" /> Submitting… - </> - ) : ( - <> - <MessageCircle className="w-4 h-4 mr-2" /> Send - </> - )} - </Button> - </div> - </form> - </Form> - ) -} - -function DonationForm({ donationUrl }: { donationUrl: string }) { - return ( - <div className="flex flex-col gap-4"> - <div> - <h2 className="text-lg font-semibold leading-none tracking-tight pb-1.5"> - Support us - </h2> - <p className="text-sm text-muted-foreground"> - Help keep <strong>Spliit</strong> free and without ads! - </p> - </div> - <div className="prose prose-sm dark:prose-invert"> - <p> - Spliit is offered for free, but costs money and energy. If you like - the app, you can choose to support it by buying me (Sebastien) a - coffee with a one-time small donation. - </p> - <p>By supporting Spliit:</p> - <ul> - <li> - You contribute to the <strong>hosting costs</strong> for the app - (currently ~$150/year). - </li> - <li> - You help us keeping the application{' '} - <strong>free and without ads</strong>. - </li> - <li> - You give me energy to build <strong>new features</strong> and - improve the application. - </li> - </ul> - <p> - You will be redirected to <strong>Stripe</strong>, our payment - provider, where you can choose an amount to donate and complete the - payment. - </p> - </div> - <div className="text-center"> - <Button - asChild - className="bg-pink-700 hover:bg-pink-600 dark:bg-pink-500 dark:hover:bg-pink-600" - > - <a href={donationUrl} target="_blank"> - <Heart className="w-4 h-4 mr-2" /> Support us - </a> - </Button> - </div> - </div> - ) -} - -export function FeedbackButton({ ...props }: ButtonProps) { - return ( - <Button - className="bg-pink-700 hover:bg-pink-600 dark:bg-pink-500 dark:hover:bg-pink-600 fixed right-0 bottom-4 rounded-r-none gap-2" - {...props} - > - <MessageCircle className="w-4 h-4" /> - <HeartIcon className="w-4 h-4" /> - </Button> - ) -} diff --git a/src/lib/env.ts b/src/lib/env.ts index bc0c0d7..af5616c 100644 --- a/src/lib/env.ts +++ b/src/lib/env.ts @@ -4,11 +4,6 @@ const envSchema = z.object({ NEXT_PUBLIC_BASE_URL: z.string().url(), POSTGRES_URL_NON_POOLING: z.string().url(), POSTGRES_PRISMA_URL: z.string().url(), - PLAUSIBLE_DOMAIN: z.string().optional(), - FEEDBACK_EMAIL_FROM: z.string().email().optional(), - FEEDBACK_EMAIL_TO: z.string().email().optional(), - RESEND_API_KEY: z.string().optional(), - STRIPE_DONATION_LINK: z.string().optional().default('https://example.com'), }) export const env = envSchema.parse(process.env) diff --git a/src/lib/resend.ts b/src/lib/resend.ts deleted file mode 100644 index 48c5fb7..0000000 --- a/src/lib/resend.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { env } from '@/lib/env' -import { Resend } from 'resend' - -export const getResend = () => - env.RESEND_API_KEY ? new Resend(env.RESEND_API_KEY) : null |
