feat: add docs feedback widget (#21131)

Add a docs feedback widget to allow upvoting and downvoting pages.

Votes (completely anonymized, barely more than a counter) go to a database in the main supabase-com project. If the user has accepted telemetry, the votes also go to Logflare with a bit more info. Post-vote, logged-in users can leave a comment, which goes to the `platform/feedback/send` endpoint. There is a warning in the feedback modal that the feedback is not anonymous.
This commit is contained in:
Charis authored and GitHub committed 2024-03-11 20:28:25 -04:00
1 parent ce0ebafab0
commit 3124f3dad7
14 files changed
+427 -31

No files matched your search

+26 -13
View File
@@ -1,11 +1,13 @@
import { useTheme } from 'next-themes'
import Image from 'next/legacy/image'
import Link from 'next/link'
import { type CSSProperties, type PropsWithChildren, memo, useEffect } from 'react'
import { cn } from 'ui'
import Footer from '~/components/Navigation/Footer'
import NavigationMenu, { type MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
import TopNavBar from '~/components/Navigation/NavigationMenu/TopNavBar'
import { PropsWithChildren, memo, useEffect } from 'react'
import Footer from '~/components/Navigation/Footer'
import { menuState, useMenuMobileOpen } from '~/hooks/useMenuState'
const levelsData = {
@@ -149,13 +151,13 @@ const MobileHeader = memo(function MobileHeader({ menuId }: { menuId: MenuId })
return (
<div
className={[
className={cn(
'transition-all ease-out z-10',
'top-0',
mobileMenuOpen && 'absolute',
'flex items-center h-[40px]',
mobileMenuOpen ? 'gap-0' : 'gap-3',
].join(' ')}
'flex items-center h-[var(--mobile-header-height,40px)]',
mobileMenuOpen ? 'gap-0' : 'gap-3'
)}
>
<button
className={['mr-2', mobileMenuOpen && 'mt-0.5'].join(' ')}
@@ -257,23 +259,27 @@ const HeaderLogo = memo(function HeaderLogo() {
)
})
const Container = memo(function Container(props: PropsWithChildren) {
const Container = memo(function Container({
children,
style,
}: PropsWithChildren<{ style?: CSSProperties }>) {
const mobileMenuOpen = useMenuMobileOpen()
return (
<div
// #docs-content-container is used by layout to scroll to top
id="docs-content-container"
className={[
className={cn(
// 'overflow-x-auto',
'w-full transition-all ease-out',
// 'absolute lg:relative',
mobileMenuOpen ? 'ml-[75%] sm:ml-[50%] md:ml-[33%] overflow-hidden' : 'overflow-auto',
// desktop override any margin styles
'lg:ml-0',
].join(' ')}
'lg:ml-0'
)}
style={style}
>
<div className="flex flex-col relative">{props.children}</div>
<div className="flex flex-col relative">{children}</div>
</div>
)
})
@@ -348,7 +354,14 @@ function MainSkeleton({ children, menuId }: PropsWithChildren<{ menuId: MenuId }
return (
<div className="flex flex-row h-full">
<NavContainer menuId={menuId} />
<Container>
<Container
style={
{
'--desktop-header-height': '60px',
'--mobile-header-height': '40px',
} as CSSProperties
}
>
<div className={['lg:sticky top-0 z-10 overflow-hidden'].join(' ')}>
<TopNavBar />
</div>
+14 -11
View File
@@ -4,16 +4,19 @@ import Head from 'next/head'
import { usePathname } from 'next/navigation'
import { useRouter } from 'next/router'
import { FC, useEffect, useRef, useState } from 'react'
import { IconExternalLink } from 'ui'
import { IconExternalLink, cn } from 'ui'
import { ExpandableVideo } from 'ui-patterns/ExpandableVideo'
import components from '~/components'
import { highlightSelectedTocItem } from '~/components/CustomHTMLElements/CustomHTMLElements.utils'
import { FooterHelpCalloutType } from '~/components/FooterHelpCallout'
import GuidesTableOfContents from '~/components/GuidesTableOfContents'
import { type MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
import useHash from '~/hooks/useHash'
import { LayoutMainContent } from '../DefaultLayout'
import { MainSkeleton } from '../MainSkeleton'
import { Feedback } from '~/components/Feedback'
import { LayoutMainContent } from '~/layouts/DefaultLayout'
import { MainSkeleton } from '~/layouts/MainSkeleton'
interface Props {
meta: {
@@ -69,7 +72,7 @@ const Layout: FC<Props> = (props) => {
return { text, link, level }
})
setTocList(newHeadings)
}, [pathname]) // Needed to recalculate the ToC when the page changes
}, [pathname]) // needed to recalculate the toc when path changes
const hasTableOfContents = tocList.length > 0
const tocVideoPreview = `http://img.youtube.com/vi/${props.meta?.tocVideo}/0.jpg`
@@ -162,13 +165,12 @@ const Layout: FC<Props> = (props) => {
</div>
{!props.hideToc && hasTableOfContents && !props.meta?.hide_table_of_contents && (
<div
className={[
'col-span-3',
'border-overlay bg-background table-of-contents-height',
'thin-scrollbar overflow-y-auto sticky hidden md:block md:col-span-3 px-2',
'transition-all ease-out',
'duration-100',
].join(' ')}
className={cn(
'col-span-3 self-start',
'border-overlay bg-background',
'thin-scrollbar overflow-y-auto hidden md:block md:col-span-3 px-2',
'sticky top-[calc(var(--mobile-header-height,40px)+2rem)] lg:top-[calc(var(--desktop-header-height,60px)+2rem)]'
)}
>
<div className="border-l">
{props.meta?.tocVideo && !!tocVideoPreview && (
@@ -176,6 +178,7 @@ const Layout: FC<Props> = (props) => {
<ExpandableVideo imgUrl={tocVideoPreview} videoId={props.meta.tocVideo} />
</div>
)}
<Feedback key={pathname} />
<span className="block font-mono text-xs uppercase text-foreground px-5 mb-6">
On this page
</span>