Skip to content

Commit 0bbad33

Browse files
committed
Docs: enable feedback
1 parent fa5b908 commit 0bbad33

File tree

23 files changed

+1395
-1172
lines changed

23 files changed

+1395
-1172
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
'use client';
2+
import { cn } from '@/lib/cn';
3+
import { buttonVariants } from 'fumadocs-ui/components/ui/button';
4+
import posthog from 'posthog-js';
5+
import { ThumbsDown, ThumbsUp } from 'lucide-react';
6+
import { useEffect, useState } from 'react';
7+
import {
8+
Collapsible,
9+
CollapsibleContent,
10+
} from 'fumadocs-ui/components/ui/collapsible';
11+
import { cva } from 'class-variance-authority';
12+
13+
const rateButtonVariants = cva(
14+
'inline-flex items-center gap-2 px-3 py-2 rounded-full border bg-fd-secondary text-fd-secondary-foreground text-sm [&_svg]:size-4 disabled:cursor-not-allowed',
15+
{
16+
variants: {
17+
active: {
18+
true: 'bg-fd-primary text-fd-primary-foreground font-medium [&_svg]:fill-current',
19+
false: 'disabled:text-fd-muted-foreground',
20+
},
21+
},
22+
},
23+
);
24+
25+
interface Feedback {
26+
opinion: 'good' | 'bad';
27+
message: string;
28+
}
29+
30+
function get(): Feedback | null {
31+
const url = window.location.pathname;
32+
const item = localStorage.getItem(`docs-feedback-${url}`);
33+
34+
if (item === null) return null;
35+
return JSON.parse(item) as Feedback;
36+
}
37+
38+
function set(feedback: Feedback) {
39+
const url = window.location.pathname;
40+
41+
localStorage.setItem(`docs-feedback-${url}`, JSON.stringify(feedback));
42+
}
43+
44+
export function Rate() {
45+
const [previous, setPrevious] = useState<Feedback | null>(null);
46+
const [opinion, setOpinion] = useState<'good' | 'bad' | false>(false);
47+
const [message, setMessage] = useState('');
48+
49+
useEffect(() => {
50+
setPrevious(get());
51+
}, []);
52+
53+
return (
54+
<Collapsible
55+
open={opinion !== false || previous !== null}
56+
onOpenChange={(v) => {
57+
if (!v) setOpinion(false);
58+
else setOpinion('good');
59+
}}
60+
>
61+
<div className="flex flex-row items-center gap-2 border-y py-2">
62+
<p className="text-fd-muted-foreground text-sm font-medium pe-2">
63+
Rate this guide?
64+
</p>
65+
<button
66+
disabled={previous !== null}
67+
className={cn(
68+
rateButtonVariants({
69+
active: opinion === 'good',
70+
}),
71+
)}
72+
onClick={() => {
73+
setOpinion('good');
74+
}}
75+
>
76+
<ThumbsUp />
77+
Good
78+
</button>
79+
<button
80+
disabled={previous !== null}
81+
className={cn(
82+
rateButtonVariants({
83+
active: opinion === 'bad',
84+
}),
85+
)}
86+
onClick={() => {
87+
setOpinion('bad');
88+
}}
89+
>
90+
<ThumbsDown />
91+
Bad
92+
</button>
93+
</div>
94+
<CollapsibleContent>
95+
{previous ? (
96+
<div className="mt-4 p-3 min-h-[100px] bg-fd-card text-fd-card-foreground text-sm text-center content-center rounded-xl text-fd-muted-foreground">
97+
Submitted, thank you for your feedback!
98+
</div>
99+
) : (
100+
<form
101+
className="flex flex-col gap-4 pt-4"
102+
onSubmit={(e) => {
103+
e.preventDefault();
104+
105+
if (opinion !== 'good' && opinion !== 'bad') return;
106+
const feedback: Feedback = {
107+
opinion,
108+
message,
109+
};
110+
posthog.capture('on_rate_docs', feedback);
111+
set(feedback);
112+
setPrevious(feedback);
113+
}}
114+
>
115+
<textarea
116+
value={message}
117+
onChange={(e) => setMessage(e.target.value)}
118+
className="border rounded-xl bg-fd-secondary text-fd-secondary-foreground p-3 text-sm resize-none focus-visible:outline-none placeholder:text-fd-muted-foreground"
119+
placeholder="Leave your feedback..."
120+
/>
121+
<button
122+
type="submit"
123+
className={cn(buttonVariants({ color: 'primary' }), 'w-fit px-4')}
124+
>
125+
Submit
126+
</button>
127+
</form>
128+
)}
129+
</CollapsibleContent>
130+
</Collapsible>
131+
);
132+
}

apps/docs/app/docs/[...slug]/page.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { AutoTypeTable } from '@/components/type-table';
2727
import { metadataImage } from '@/lib/metadata-image';
2828
import { File, Folder, Files } from 'fumadocs-ui/components/files';
2929
import { Mermaid } from '@theguild/remark-mermaid/mermaid';
30+
import { Rate } from '@/app/docs/[...slug]/page.client';
3031

3132
function PreviewRenderer({ preview }: { preview: string }): ReactNode {
3233
if (preview && preview in Preview) {
@@ -102,6 +103,7 @@ export default async function Page(props: {
102103
/>
103104
{page.data.index ? <DocsCategory page={page} from={source} /> : null}
104105
</DocsBody>
106+
<Rate />
105107
</DocsPage>
106108
);
107109
}

apps/docs/app/provider.tsx

+24-7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import { RootProvider } from 'fumadocs-ui/provider';
44
import dynamic from 'next/dynamic';
55
import type { ReactNode } from 'react';
66
import { TooltipProvider } from '@radix-ui/react-tooltip';
7+
import posthog from 'posthog-js';
8+
import { PostHogProvider as PHProvider } from 'posthog-js/react';
9+
import { useEffect } from 'react';
710

811
const SearchDialog = dynamic(() => import('@/components/search'), {
912
ssr: false,
@@ -35,13 +38,27 @@ export function Provider({
3538
SearchDialog,
3639
}}
3740
>
38-
<TooltipProvider>
39-
<script
40-
suppressHydrationWarning
41-
dangerouslySetInnerHTML={{ __html: inject }}
42-
/>
43-
{children}
44-
</TooltipProvider>
41+
<PostHogProvider>
42+
<TooltipProvider>
43+
<script
44+
suppressHydrationWarning
45+
dangerouslySetInnerHTML={{ __html: inject }}
46+
/>
47+
{children}
48+
</TooltipProvider>
49+
</PostHogProvider>
4550
</RootProvider>
4651
);
4752
}
53+
54+
function PostHogProvider({ children }: { children: ReactNode }) {
55+
useEffect(() => {
56+
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
57+
api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
58+
capture_pageview: false,
59+
autocapture: false,
60+
});
61+
}, []);
62+
63+
return <PHProvider client={posthog}>{children}</PHProvider>;
64+
}

apps/docs/package.json

+16-15
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
"@radix-ui/react-popover": "^1.1.6",
2020
"@radix-ui/react-slot": "^1.1.2",
2121
"@radix-ui/react-tooltip": "^1.1.8",
22-
"@shikijs/rehype": "^2.3.2",
23-
"@shikijs/twoslash": "^2.3.2",
22+
"@shikijs/rehype": "^2.4.1",
23+
"@shikijs/twoslash": "^2.4.1",
2424
"@theguild/remark-mermaid": "^0.2.0",
2525
"class-variance-authority": "^0.7.1",
2626
"fumadocs-core": "workspace:*",
@@ -34,40 +34,41 @@
3434
"hast-util-to-jsx-runtime": "^2.3.2",
3535
"katex": "^0.16.21",
3636
"lucide-react": "^0.475.0",
37-
"next": "15.1.6",
38-
"oxc-transform": "^0.48.2",
37+
"next": "15.1.7",
38+
"oxc-transform": "^0.51.0",
3939
"phenomenon": "^1.6.0",
40+
"posthog-js": "^1.218.2",
4041
"react": "^19.0.0",
4142
"react-dom": "^19.0.0",
4243
"rehype-katex": "^7.0.1",
4344
"remark": "^15.0.1",
44-
"remark-gfm": "^4.0.0",
45+
"remark-gfm": "^4.0.1",
4546
"remark-math": "^6.0.0",
4647
"remark-mdx": "^3.1.0",
4748
"remark-rehype": "^11.1.1",
4849
"remark-stringify": "^11.0.0",
4950
"scroll-into-view-if-needed": "^3.1.0",
50-
"shiki": "^2.3.2",
51+
"shiki": "^2.4.1",
5152
"tailwind-merge": "^3.0.1",
52-
"zod": "^3.24.1"
53+
"zod": "^3.24.2"
5354
},
5455
"devDependencies": {
5556
"@fumadocs/cli": "workspace:*",
56-
"@next/bundle-analyzer": "15.1.6",
57-
"@next/env": "15.1.6",
58-
"@next/eslint-plugin-next": "15.1.6",
59-
"@tailwindcss/postcss": "^4.0.5",
57+
"@next/bundle-analyzer": "15.1.7",
58+
"@next/env": "15.1.7",
59+
"@next/eslint-plugin-next": "15.1.7",
60+
"@tailwindcss/postcss": "^4.0.6",
6061
"@types/hast": "^3.0.4",
6162
"@types/mdx": "^2.0.13",
62-
"@types/node": "22.13.1",
63-
"@types/react": "^19.0.8",
63+
"@types/node": "22.13.4",
64+
"@types/react": "^19.0.9",
6465
"@types/react-dom": "^19.0.3",
6566
"eslint-config-custom": "workspace:*",
6667
"fast-glob": "^3.3.3",
6768
"gray-matter": "^4.0.3",
6869
"next-validate-link": "^1.4.1",
69-
"postcss": "^8.5.1",
70-
"tailwindcss": "^4.0.5",
70+
"postcss": "^8.5.2",
71+
"tailwindcss": "^4.0.6",
7172
"tailwindcss-animate": "^1.0.7",
7273
"ts-morph": "^25.0.1",
7374
"tsconfig": "workspace:*",

examples/content-collections/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"@fumadocs/content-collections": "workspace:*",
1212
"fumadocs-core": "workspace:*",
1313
"fumadocs-ui": "workspace:*",
14-
"next": "^15.1.6",
14+
"next": "^15.1.7",
1515
"react": "19.0.0",
1616
"react-dom": "19.0.0"
1717
},
@@ -20,7 +20,7 @@
2020
"@content-collections/mdx": "^0.2.0",
2121
"@content-collections/next": "^0.2.4",
2222
"@types/mdx": "^2.0.13",
23-
"@types/react": "^19.0.8",
23+
"@types/react": "^19.0.9",
2424
"@types/react-dom": "^19.0.3",
2525
"typescript": "^5.7.3"
2626
}

examples/i18n/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@
1313
"fumadocs-core": "workspace:*",
1414
"fumadocs-mdx": "workspace:*",
1515
"fumadocs-ui": "workspace:*",
16-
"next": "15.1.6",
16+
"next": "15.1.7",
1717
"react": "19.0.0",
1818
"react-dom": "19.0.0"
1919
},
2020
"devDependencies": {
2121
"@types/mdx": "^2.0.13",
22-
"@types/react": "^19.0.8",
22+
"@types/react": "^19.0.9",
2323
"@types/react-dom": "^19.0.3",
2424
"typescript": "^5.7.3"
2525
}

examples/next-mdx/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@
1111
"fumadocs-core": "workspace:*",
1212
"fumadocs-mdx": "workspace:*",
1313
"fumadocs-ui": "workspace:*",
14-
"next": "15.1.6",
14+
"next": "15.1.7",
1515
"react": "19.0.0",
1616
"react-dom": "19.0.0"
1717
},
1818
"devDependencies": {
1919
"@types/mdx": "^2.0.13",
20-
"@types/react": "^19.0.8",
20+
"@types/react": "^19.0.9",
2121
"@types/react-dom": "^19.0.3",
2222
"algoliasearch": "4.24.0",
2323
"typescript": "^5.7.3"

examples/openapi/package.json

+8-8
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,25 @@
1010
"types:check": "fumadocs-mdx && tsc --noEmit"
1111
},
1212
"dependencies": {
13-
"@scalar/api-client-react": "^1.1.25",
13+
"@scalar/api-client-react": "^1.1.27",
1414
"fumadocs-core": "workspace:*",
1515
"fumadocs-mdx": "workspace:*",
1616
"fumadocs-openapi": "workspace:*",
1717
"fumadocs-ui": "workspace:*",
18-
"next": "15.1.6",
18+
"next": "15.1.7",
1919
"react": "19.0.0",
2020
"react-dom": "19.0.0",
21-
"shiki": "^2.3.2",
22-
"zod": "^3.24.1"
21+
"shiki": "^2.4.1",
22+
"zod": "^3.24.2"
2323
},
2424
"devDependencies": {
25-
"@tailwindcss/postcss": "^4.0.5",
25+
"@tailwindcss/postcss": "^4.0.6",
2626
"@types/mdx": "^2.0.13",
27-
"@types/react": "^19.0.8",
27+
"@types/react": "^19.0.9",
2828
"@types/react-dom": "^19.0.3",
29-
"postcss": "^8.5.1",
29+
"postcss": "^8.5.2",
3030
"rimraf": "^6.0.1",
31-
"tailwindcss": "^4.0.5",
31+
"tailwindcss": "^4.0.6",
3232
"typescript": "^5.7.3"
3333
}
3434
}

examples/remote-mdx/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@
1212
"fast-glob": "^3.3.3",
1313
"fumadocs-core": "workspace:*",
1414
"fumadocs-ui": "workspace:*",
15-
"next": "15.1.6",
15+
"next": "15.1.7",
1616
"react": "19.0.0",
1717
"react-dom": "19.0.0"
1818
},
1919
"devDependencies": {
2020
"@types/mdx": "^2.0.13",
21-
"@types/react": "^19.0.8",
21+
"@types/react": "^19.0.9",
2222
"@types/react-dom": "^19.0.3",
2323
"typescript": "^5.7.3"
2424
}

package.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,16 @@
2121
"@eslint/js": "^9.20.0",
2222
"@types/eslint__js": "^8.42.3",
2323
"concurrently": "^9.1.2",
24-
"eslint": "^9.20.0",
24+
"eslint": "^9.20.1",
2525
"eslint-plugin-react": "^7.37.4",
2626
"eslint-plugin-react-hooks": "5.1.0",
2727
"eslint-plugin-tailwindcss": "^3.18.0",
28-
"prettier": "^3.5.0",
28+
"prettier": "^3.5.1",
2929
"rimraf": "^6.0.1",
3030
"tsup": "8.3.6",
31-
"turbo": "2.4.0",
31+
"turbo": "2.4.2",
3232
"typescript": "^5.7.3",
33-
"typescript-eslint": "^8.23.0",
33+
"typescript-eslint": "^8.24.0",
3434
"vitest": "^3.0.4"
3535
},
3636
"packageManager": "[email protected]",

packages/cli/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@
4343
},
4444
"devDependencies": {
4545
"@types/cross-spawn": "^6.0.6",
46-
"@types/node": "22.13.1",
47-
"@types/react": "^19.0.8",
46+
"@types/node": "22.13.4",
47+
"@types/react": "^19.0.9",
4848
"eslint-config-custom": "workspace:*",
4949
"fast-glob": "^3.3.3",
5050
"tsconfig": "workspace:*",

0 commit comments

Comments
 (0)