Skip to content

Commit

Permalink
Merge pull request #3 from fredygerman/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
fredygerman authored Oct 3, 2023
2 parents 981cef8 + a2da2ab commit 9bfd971
Show file tree
Hide file tree
Showing 19 changed files with 1,282 additions and 1,096 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"typescript.tsdk": "../../node_modules/.pnpm/[email protected]/node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
"typescript.enablePromptUseWorkspaceTsdk": true,
"cSpell.words": ["posthog"]
}
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,12 @@ pnpm run dev
- Tailwind CSS class sorting, merging and linting.
- Next js Server Side Components (SSC) & Client Side Components (CSC)
- Next js Form Actions
- Analytics with Posthog

## Roadmap

- [] Add Login and Register
- [ ] Add Analytics
- [] Add Analytics
- [ ] Add Installation Instructions Documentation
- [ ] Add Dashboard Page
- [ ] Add Simple Blog
Expand Down
37 changes: 26 additions & 11 deletions app/(app)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import Link from "next/link"
import posthog from "posthog-js"

import { env } from "@/env.mjs"
import { siteConfig } from "@/config/site"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
import { AdvancedLink } from "@/components/advanced/advanced-link"

async function getGitHubStars(): Promise<string | null> {
try {
Expand Down Expand Up @@ -39,16 +40,18 @@ export default async function IndexPage() {
<>
<section className="space-y-6 pb-8 pt-6 md:pb-12 md:pt-10 lg:py-32">
<div className="container flex max-w-[64rem] flex-col items-center gap-4 text-center">
<Link
<AdvancedLink
href={
siteConfig.socials.find((social) => social.name === "Twitter")
?.url ?? "#"
}
className="rounded-2xl bg-muted px-4 py-1.5 text-sm font-medium"
target="_blank"
analyticsValue="clicked_follow_on_twitter"
analyticsProperties={{ source: "home_page" }}
>
Follow along on Twitter
</Link>
</AdvancedLink>
<h1 className="font-heading text-3xl font-extrabold leading-tight tracking-tighter sm:text-5xl md:text-6xl lg:text-7xl">
Next.js 13 & Directus CMS Starter 🚀
</h1>
Expand All @@ -60,15 +63,23 @@ export default async function IndexPage() {
updates.
</p>
<div className="space-x-4">
<Link href="/login" className={cn(buttonVariants({ size: "lg" }))}>
<AdvancedLink
href="/login"
className={cn(buttonVariants({ size: "lg" }))}
analyticsValue="clicked_get_started"
analyticsProperties={{ source: "home_page" }}
>
Get Started
</Link>
<Link
</AdvancedLink>

<AdvancedLink
href="#features"
className={cn(buttonVariants({ variant: "outline", size: "lg" }))}
analyticsValue="clicked_features"
analyticsProperties={{ source: "home_page" }}
>
Features
</Link>
</AdvancedLink>
</div>
</div>
</section>
Expand Down Expand Up @@ -195,25 +206,29 @@ export default async function IndexPage() {
<p className="max-w-[85%] leading-normal text-muted-foreground sm:text-lg sm:leading-7">
The main objective is to help developers who use Directus and Next
Js to build their apps faster.
<Link
<AdvancedLink
href={
siteConfig.socials.find((social) => social.name === "Github")
?.url ?? "#"
}
target="_blank"
rel="noreferrer"
className="underline underline-offset-4"
analyticsValue="github"
analyticsProperties={{ source: "home_page" }}
>
GitHub
</Link>
</AdvancedLink>
.{" "}
</p>
{stars && (
<Link
<AdvancedLink
href={`https:/github.com/${siteConfig.gitHubApiRepoName}` ?? "#"}
target="_blank"
rel="noreferrer"
className="flex"
analyticsValue="github"
analyticsProperties={{ source: "home_page" }}
>
<div className="flex h-10 w-10 items-center justify-center space-x-2 rounded-md border border-muted bg-muted">
<svg
Expand All @@ -231,7 +246,7 @@ export default async function IndexPage() {
{stars} stars on GitHub
</div>
</div>
</Link>
</AdvancedLink>
)}
</div>
</section>
Expand Down
13 changes: 8 additions & 5 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { RootLayoutProps } from "@/types/general"
import { siteConfig } from "@/config/site"
import { fontSans } from "@/lib/fonts"
import { cn } from "@/lib/utils"
import { CustomPostHogProvider } from "@/components/misc/posthog-provider"
import { ProgressBar } from "@/components/misc/progress"
import CustomProvider from "@/components/misc/state-provider"
// import Seo from "@/components/misc/seo"
Expand Down Expand Up @@ -76,11 +77,13 @@ export default function RootLayout({ children }: RootLayoutProps) {
>
{/* <Seo> */}
<ProgressBar />
<CustomProvider>
<div className="relative flex min-h-screen flex-col">
{children}
</div>
</CustomProvider>
<CustomPostHogProvider>
<CustomProvider>
<div className="relative flex min-h-screen flex-col">
{children}
</div>
</CustomProvider>
</CustomPostHogProvider>
<ToasterWrapper />
<TailwindIndicator />
{/* </Seo> */}
Expand Down
47 changes: 47 additions & 0 deletions components/advanced/advanced-link.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"use client"

import { ReactNode, useEffect } from "react"
import Link from "next/link"
import posthog from "posthog-js"

export function AdvancedLink({
children,
href, // Extract href directly
className, // Extract className directly
onClick, // Extract onClick directly
analyticsValue, // Extract analyticsValue directly
analyticsProperties, // Extract analyticsProperties directly
// any other props that come through
...props
}: {
children: ReactNode
href: string
className?: string
onClick?: () => void
analyticsValue?: string
analyticsProperties?: object | null
[key: string]: any
}) {
const captureEvent = () => {
// Add Posthog tracking when the component is clicked.
if (analyticsValue) {
posthog?.capture(`${analyticsValue ?? "clicked_link"}`, {
properties: analyticsProperties ?? {},
})
}
}

return (
<Link
href={href}
className={className ?? ""}
onClick={(e) => {
captureEvent()
onClick && onClick()
}}
{...props}
>
{children}
</Link>
)
}
2 changes: 2 additions & 0 deletions components/auth/forgot-password/ForgotPasswordAuthForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { useRouter } from "next/navigation"
import { forgotPassword } from "@/actions/authForms"
import { zodResolver } from "@hookform/resolvers/zod"
import posthog from "posthog-js"
import { useForm } from "react-hook-form"
import * as z from "zod"

Expand Down Expand Up @@ -36,6 +37,7 @@ export function ForgotPasswordAuthForm() {

const onSubmit = async (data: AccountFormValues) => {
const { email } = data
posthog?.capture("user_forgot_password")
const result = await forgotPassword(email)
console.log(result)
toast({
Expand Down
13 changes: 11 additions & 2 deletions components/auth/login/LoginAuthForm.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
"use client"

import { useEffect } from "react"
import Link from "next/link"
import { useRouter } from "next/navigation"
import { login } from "@/actions/authForms"
import { logoutUser, setAuthState } from "@/store/slices/auth"
import { logoutUser, setAuthState, storedUser } from "@/store/slices/auth"
import { zodResolver } from "@hookform/resolvers/zod"
import { format } from "date-fns"
import { usePostHog } from "posthog-js/react"
import { useDirectus } from "react-directus"
import { useForm } from "react-hook-form"
import * as z from "zod"

import { cn } from "@/lib/utils"
import { useStoreDispatch } from "@/hooks/useStore"
import { useStoreDispatch, useStoreSelector } from "@/hooks/useStore"
import { Button } from "@/components/ui/button"
import {
Form,
Expand Down Expand Up @@ -61,8 +63,10 @@ export function LoginAuthForm() {
localStorage.removeItem("logged-out")
}
}
const posthog = usePostHog()

const router = useRouter()
let user = useStoreSelector(storedUser)
const form = useForm<AccountFormValues>({
resolver: zodResolver(accountFormSchema),
defaultValues,
Expand All @@ -86,6 +90,11 @@ export function LoginAuthForm() {

if (loginResult.success) {
dispatch(setAuthState(loginResult.data))
posthog?.identify(loginResult.data.user.id, {
email: loginResult.data.user.email,
})
posthog?.group("role", loginResult.data.user.role)
posthog?.capture("user_log_in")
// await 2 seconds to allow the auth state to be set
await new Promise((resolve) => setTimeout(resolve, 2000))
router.push("/")
Expand Down
7 changes: 7 additions & 0 deletions components/auth/signIn/SignInAuthForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useRouter } from "next/navigation"
import { register } from "@/actions/authForms"
import { setAuthState } from "@/store/slices/auth"
import { zodResolver } from "@hookform/resolvers/zod"
import { usePostHog } from "posthog-js/react"
import { useForm } from "react-hook-form"
import * as z from "zod"

Expand Down Expand Up @@ -56,6 +57,7 @@ export function SignInAuthForm() {
resolver: zodResolver(accountFormSchema),
defaultValues,
})
const posthog = usePostHog()

const onSubmit = async (data: AccountFormValues) => {
const { email, password, first_name, last_name } = data
Expand All @@ -79,6 +81,11 @@ export function SignInAuthForm() {

if (registerResult.success) {
dispatch(setAuthState(registerResult.data))
posthog?.identify(registerResult.data.user.id, {
email: registerResult.data.user.email,
})
posthog?.group("role", registerResult.data.user.role)
posthog?.capture("user_sign_in")
// wait for 2 seconds
await new Promise((resolve) => setTimeout(resolve, 2000))
router.push("/")
Expand Down
21 changes: 21 additions & 0 deletions components/misc/posthog-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"use client"

import posthog from "posthog-js"
import { PostHogProvider } from "posthog-js/react"

export function CustomPostHogProvider({ children }: any) {
// Check that PostHog is client-side (used to handle Next.js SSR)
if (typeof window !== "undefined") {
posthog.init(process.env.POSTHOG_KEY || "", {
api_host: process.env.POSTHOG_HOST_URL || "https://app.posthog.com",
// Enable debug mode in development
loaded: (posthog) => {
if (process.env.NODE_ENV === "development") posthog.debug()
console.log("PostHog debug enabled")
},
capture_pageview: true,
})
}

return <PostHogProvider client={posthog}>{children}</PostHogProvider>
}
6 changes: 6 additions & 0 deletions components/misc/profile-icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
storedToken,
storedUser,
} from "@/store/slices/auth"
import { usePostHog } from "posthog-js/react"

import { useStoreDispatch, useStoreSelector } from "@/hooks/useStore"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
Expand All @@ -27,6 +28,7 @@ export function ProfileIcon() {
const user = useStoreSelector(storedUser)
const auth = useStoreSelector(storedAuth)
const token = useStoreSelector(storedToken)
const posthog = usePostHog()

// we can not get the user or auth from the store, so we will log them out and we return null
if (!user || !auth || !token) {
Expand All @@ -38,6 +40,10 @@ export function ProfileIcon() {
dispatch(logoutUser())
// wait 1 second to show the toast
setTimeout(() => {}, 1000)
posthog?.identify(user.id, {
email: user.email,
})
posthog?.capture("user_log_out")
toast({
title: "Logged Out 👋 ",
variant: "default",
Expand Down
10 changes: 9 additions & 1 deletion components/misc/theme-toggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import * as React from "react"
import { Moon, Sun } from "lucide-react"
import { useTheme } from "next-themes"
import posthog from "posthog-js"

import { Button } from "@/components/ui/button"

Expand All @@ -13,7 +14,14 @@ export function ThemeToggle() {
<Button
variant="ghost"
size="icon"
onClick={() => setTheme(theme === "light" ? "dark" : "light")}
onClick={() => {
setTheme(theme === "light" ? "dark" : "light")
posthog?.capture("theme_changed", {
properties: {
theme: theme === "light" ? "dark" : "light",
},
})
}}
>
<Sun className="h-[1.5rem] w-[1.3rem] dark:hidden" />
<Moon className="hidden h-5 w-5 dark:block" />
Expand Down
Loading

0 comments on commit 9bfd971

Please sign in to comment.