diff --git a/apps/api/Dockerfile b/apps/api/Dockerfile index f7ddae84..8158360c 100644 --- a/apps/api/Dockerfile +++ b/apps/api/Dockerfile @@ -29,6 +29,9 @@ ENV NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=$NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY ARG CLERK_SECRET_KEY ENV CLERK_SECRET_KEY=$CLERK_SECRET_KEY +ARG SEVENTY_SEVEN_API_KEY +ENV SEVENTY_SEVEN_API_KEY=$SEVENTY_SEVEN_API_KEY + ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" diff --git a/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/layout-menu.tsx b/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/layout-menu.tsx index 4c84d9fe..222c9be3 100644 --- a/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/layout-menu.tsx +++ b/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/layout-menu.tsx @@ -74,6 +74,7 @@ export default function LayoutMenu({ dashboards }: LayoutMenuProps) { if (hasProjectId) { user?.update({ unsafeMetadata: { + ...user.unsafeMetadata, projectId: params.projectId, }, }); diff --git a/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/layout.tsx b/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/layout.tsx index b49720df..8d825e96 100644 --- a/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/layout.tsx +++ b/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/layout.tsx @@ -7,6 +7,7 @@ import { } from '@openpanel/db'; import { LayoutSidebar } from './layout-sidebar'; +import SideEffects from './side-effects'; interface AppLayoutProps { children: React.ReactNode; @@ -48,6 +49,7 @@ export default async function AppLayout({ {...{ organizationSlug, projectId, organizations, dashboards }} />
{children}
+ ); } diff --git a/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/side-effects.tsx b/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/side-effects.tsx new file mode 100644 index 00000000..3f69ff27 --- /dev/null +++ b/apps/dashboard/src/app/(app)/[organizationSlug]/[projectId]/side-effects.tsx @@ -0,0 +1,38 @@ +'use client'; + +import { useEffect } from 'react'; +import { pushModal, useOnPushModal } from '@/modals'; +import { useUser } from '@clerk/nextjs'; +import { differenceInDays } from 'date-fns'; + +import { trackEvent } from '@openpanel/nextjs'; + +export default function SideEffects() { + const { user } = useUser(); + const accountAgeInDays = differenceInDays( + new Date(), + user?.createdAt || new Date() + ); + useOnPushModal('Testimonial', (open) => { + if (!open) { + user?.update({ + unsafeMetadata: { + ...user.unsafeMetadata, + testimonial: new Date().toISOString(), + }, + }); + } + }); + + const showTestimonial = + user && !user.unsafeMetadata.testimonial && accountAgeInDays > 7; + + useEffect(() => { + if (showTestimonial) { + pushModal('Testimonial'); + trackEvent('testimonials_shown'); + } + }, [showTestimonial]); + + return null; +} diff --git a/apps/dashboard/src/app/providers.tsx b/apps/dashboard/src/app/providers.tsx index 717dffa2..84ed1d35 100644 --- a/apps/dashboard/src/app/providers.tsx +++ b/apps/dashboard/src/app/providers.tsx @@ -8,7 +8,7 @@ import makeStore from '@/redux'; import { api } from '@/trpc/client'; import { ClerkProvider, useAuth } from '@clerk/nextjs'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { httpLink, unstable_httpBatchStreamLink } from '@trpc/client'; +import { unstable_httpBatchStreamLink } from '@trpc/client'; import { ThemeProvider } from 'next-themes'; import { Provider as ReduxProvider } from 'react-redux'; import { Toaster } from 'sonner'; diff --git a/apps/dashboard/src/components/ui/textarea.tsx b/apps/dashboard/src/components/ui/textarea.tsx new file mode 100644 index 00000000..abe00e07 --- /dev/null +++ b/apps/dashboard/src/components/ui/textarea.tsx @@ -0,0 +1,24 @@ +import * as React from "react" + +import { cn } from "@/utils/cn" + +export interface TextareaProps + extends React.TextareaHTMLAttributes {} + +const Textarea = React.forwardRef( + ({ className, ...props }, ref) => { + return ( +