Skip to content

Commit

Permalink
Merge pull request #39 from complexdatacollective/feature/feedback-bu…
Browse files Browse the repository at this point in the history
…tton

Feature: Floating feedback button
  • Loading branch information
mrkarimoff authored Dec 12, 2023
2 parents a3f8390 + e37a14f commit 7b61cf4
Show file tree
Hide file tree
Showing 6 changed files with 240 additions and 7 deletions.
2 changes: 1 addition & 1 deletion app/(onboard)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Link from 'next/link';
export default function Layout({ children }: PropsWithChildren) {
return (
<>
<div className="relative z-10 flex h-[100dvh] w-[100dvw] flex-col">
<div className="relative z-10 flex h-[90dvh] w-[100dvw] flex-col">
<div className="p-6">
<Link href="/">
<Image
Expand Down
13 changes: 7 additions & 6 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/* eslint-disable no-console */
import '~/styles/globals.scss';
import Providers from '../providers/Providers';
import { revalidatePath, revalidateTag } from 'next/cache';
import FeedbackBanner from '~/components/FeedbackBanner/FeedbackBanner';
import RedirectWrapper from '~/components/RedirectWrapper';
import { getServerSession } from '~/utils/auth';
import { api } from '~/trpc/server';
import { Toaster } from '~/components/ui/toaster';
import { revalidatePath, revalidateTag } from 'next/cache';
import '~/styles/globals.scss';
import { api } from '~/trpc/server';
import { getServerSession } from '~/utils/auth';
import Providers from '../providers/Providers';

export const metadata = {
title: 'Network Canvas Fresco',
Expand All @@ -28,6 +28,7 @@ async function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<FeedbackBanner />
<RedirectWrapper
configured={!!appSettings?.configured}
expired={!!appSettings?.expired}
Expand Down
47 changes: 47 additions & 0 deletions components/FeedbackBanner/FeedbackBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import FeedbackButton from './FeedbackButton';

export default function FeedbackBanner() {
return (
<div className="sticky top-0 isolate z-50 flex w-full items-center justify-center gap-x-6 overflow-hidden bg-gray-50 px-6 py-2 sm:px-3.5 ">
<div
className="absolute left-[max(-7rem,calc(50%-52rem))] top-1/2 -z-10 -translate-y-1/2 transform-gpu blur-2xl"
aria-hidden="true"
>
<div
className="aspect-[577/310] w-[36.0625rem] bg-gradient-to-r from-[#ff80b5] to-[#9089fc] opacity-30"
style={{
clipPath:
'polygon(74.8% 41.9%, 97.2% 73.2%, 100% 34.9%, 92.5% 0.4%, 87.5% 0%, 75% 28.6%, 58.5% 54.6%, 50.1% 56.8%, 46.9% 44%, 48.3% 17.4%, 24.7% 53.9%, 0% 27.9%, 11.9% 74.2%, 24.9% 54.1%, 68.6% 100%, 74.8% 41.9%)',
}}
/>
</div>
<div
className="absolute left-[max(45rem,calc(50%+8rem))] top-1/2 -z-10 -translate-y-1/2 transform-gpu blur-2xl"
aria-hidden="true"
>
<div
className="aspect-[577/310] w-[36.0625rem] bg-gradient-to-r from-[#ff80b5] to-[#9089fc] opacity-30"
style={{
clipPath:
'polygon(74.8% 41.9%, 97.2% 73.2%, 100% 34.9%, 92.5% 0.4%, 87.5% 0%, 75% 28.6%, 58.5% 54.6%, 50.1% 56.8%, 46.9% 44%, 48.3% 17.4%, 24.7% 53.9%, 0% 27.9%, 11.9% 74.2%, 24.9% 54.1%, 68.6% 100%, 74.8% 41.9%)',
}}
/>
</div>
<div className="flex flex-wrap items-center gap-x-4 gap-y-2">
<p className="text-sm leading-6 text-gray-900">
<strong className="font-semibold">🤖 Fresco is Alpha software</strong>
<svg
viewBox="0 0 2 2"
className="mx-2 inline h-0.5 w-0.5 fill-current"
aria-hidden="true"
>
<circle cx={1} cy={1} r={1} />
</svg>
We would appreciate input about how we can improve it, and reports or
any issues you find.
</p>
<FeedbackButton />
</div>
</div>
);
}
22 changes: 22 additions & 0 deletions components/FeedbackBanner/FeedbackButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use client';

import { useState } from 'react';
import FeedbackModal from './FeedbackModal';

const FeedbackButton = () => {
const [open, setOpen] = useState(false);

return (
<>
<FeedbackModal open={open} setOpen={setOpen} />
<button
className="flex-none rounded-full bg-gray-900 px-3.5 py-1 text-sm font-semibold text-white shadow-sm hover:bg-gray-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-900"
onClick={() => setOpen(true)}
>
Provide Feedback <span aria-hidden="true">&rarr;</span>
</button>
</>
);
};

export default FeedbackButton;
23 changes: 23 additions & 0 deletions components/FeedbackBanner/FeedbackModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { type Dispatch, type SetStateAction } from 'react';
import { Sheet, SheetContent } from '~/components/ui/sheet';

type FeedbackModalProps = {
open: boolean;
setOpen: Dispatch<SetStateAction<boolean>>;
};

const FeedbackModal = ({ open, setOpen }: FeedbackModalProps) => {
return (
<Sheet open={open} onOpenChange={setOpen}>
<SheetContent className="bg-[#f7f8f9] p-0">
<iframe
className="h-[100dvh] w-full"
title="Feedback form"
src="https://forms.clickup.com/3464225/f/39q11-6131/OIJSIULQV2EUZFLUOA"
></iframe>
</SheetContent>
</Sheet>
);
};

export default FeedbackModal;
140 changes: 140 additions & 0 deletions components/ui/sheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
'use client';

import * as React from 'react';
import * as SheetPrimitive from '@radix-ui/react-dialog';
import { cva, type VariantProps } from 'class-variance-authority';
import { X } from 'lucide-react';

import { cn } from '~/utils/shadcn';

const Sheet = SheetPrimitive.Root;

const SheetTrigger = SheetPrimitive.Trigger;

const SheetClose = SheetPrimitive.Close;

const SheetPortal = SheetPrimitive.Portal;

const SheetOverlay = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Overlay
className={cn(
'fixed inset-0 z-50 bg-slate-400 opacity-90 backdrop-blur-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
className,
)}
{...props}
ref={ref}
/>
));
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;

const sheetVariants = cva(
'fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500',
{
variants: {
side: {
top: 'inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top',
bottom:
'inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom',
left: 'inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm',
right:
'inset-y-0 right-0 h-full w-[40%] border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right',
},
},
defaultVariants: {
side: 'right',
},
},
);

interface SheetContentProps
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
VariantProps<typeof sheetVariants> {}

const SheetContent = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Content>,
SheetContentProps
>(({ side = 'right', className, children, ...props }, ref) => (
<SheetPortal>
<SheetOverlay />
<SheetPrimitive.Content
ref={ref}
className={cn(sheetVariants({ side }), className)}
{...props}
>
{children}
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</SheetPrimitive.Close>
</SheetPrimitive.Content>
</SheetPortal>
));
SheetContent.displayName = SheetPrimitive.Content.displayName;

const SheetHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
'flex flex-col space-y-2 text-center sm:text-left',
className,
)}
{...props}
/>
);
SheetHeader.displayName = 'SheetHeader';

const SheetFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
className,
)}
{...props}
/>
);
SheetFooter.displayName = 'SheetFooter';

const SheetTitle = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Title
ref={ref}
className={cn('text-lg font-semibold text-foreground', className)}
{...props}
/>
));
SheetTitle.displayName = SheetPrimitive.Title.displayName;

const SheetDescription = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Description
ref={ref}
className={cn('text-sm text-muted-foreground', className)}
{...props}
/>
));
SheetDescription.displayName = SheetPrimitive.Description.displayName;

export {
Sheet,
SheetPortal,
SheetOverlay,
SheetTrigger,
SheetClose,
SheetContent,
SheetHeader,
SheetFooter,
SheetTitle,
SheetDescription,
};

0 comments on commit 7b61cf4

Please sign in to comment.