mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
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:
1 parent
ce0ebafab0
commit
3124f3dad7
14 files changed
+427
-31
No files matched your search
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in new issue
Block a user