mirror of
https://github.com/lukevella/rallly.git
synced 2025-04-28 17:56:37 +02:00
📓 Add changelog widget (#798)
This commit is contained in:
parent
bc53e97f93
commit
d08ed6d0f7
14 changed files with 158 additions and 100 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -35,4 +35,7 @@ yarn-error.log*
|
|||
tsconfig.tsbuildinfo
|
||||
|
||||
# Turbo
|
||||
.turbo
|
||||
.turbo
|
||||
|
||||
# Jetbrains IDE
|
||||
.idea
|
4
apps/landing/declarations/environment.d.ts
vendored
4
apps/landing/declarations/environment.d.ts
vendored
|
@ -33,10 +33,6 @@ declare global {
|
|||
* Crisp website ID
|
||||
*/
|
||||
NEXT_PUBLIC_CRISP_WEBSITE_ID?: string;
|
||||
/**
|
||||
* When defined users will be able to send feedback to this email address
|
||||
*/
|
||||
NEXT_PUBLIC_FEEDBACK_EMAIL?: string;
|
||||
/**
|
||||
* Users of your instance will see this as their support email
|
||||
*/
|
||||
|
|
4
apps/web/declarations/environment.d.ts
vendored
4
apps/web/declarations/environment.d.ts
vendored
|
@ -34,9 +34,9 @@ declare global {
|
|||
*/
|
||||
NEXT_PUBLIC_CRISP_WEBSITE_ID?: string;
|
||||
/**
|
||||
* When defined users will be able to send feedback to this email address
|
||||
* When `true` it will show the feedback button and pull the changelog from featurebase
|
||||
*/
|
||||
NEXT_PUBLIC_FEEDBACK_EMAIL?: string;
|
||||
NEXT_PUBLIC_FEEDBACK_ENABLED?: string;
|
||||
/**
|
||||
* Users of your instance will see this as their support email
|
||||
*/
|
||||
|
|
|
@ -121,7 +121,6 @@
|
|||
"inviteLink": "Invite Link",
|
||||
"inviteParticipantLinkInfo": "Anyone with this link will be able to vote on your poll.",
|
||||
"support": "Support",
|
||||
"changelog": "Change log",
|
||||
"billing": "Billing",
|
||||
"guestPollAlertDescription": "<0>Create an account</0> or <1>login</1> to claim this poll.",
|
||||
"guestPollAlertTitle": "Your administrator rights can be lost if you clear your cookies",
|
||||
|
@ -209,5 +208,6 @@
|
|||
"clockPreferencesDescription": "Set your preferred time zone and time format.",
|
||||
"featureRequest": "Request a Feature",
|
||||
"bugReport": "Report an Issue",
|
||||
"getSupport": "Get Support"
|
||||
"getSupport": "Get Support",
|
||||
"feedback": "Feedback"
|
||||
}
|
||||
|
|
|
@ -1,22 +1,74 @@
|
|||
import { trpc } from "@rallly/backend";
|
||||
import { HelpCircleIcon } from "@rallly/icons";
|
||||
import { cn } from "@rallly/ui";
|
||||
import { Button } from "@rallly/ui/button";
|
||||
import Script from "next/script";
|
||||
import React from "react";
|
||||
|
||||
import { useUser } from "@/components/user-provider";
|
||||
import { Trans } from "@/components/trans";
|
||||
import { isFeedbackEnabled } from "@/utils/constants";
|
||||
|
||||
export const FeaturebaseProvider = ({ children }: React.PropsWithChildren) => {
|
||||
const { user } = useUser();
|
||||
const FeaturebaseScript = () => (
|
||||
<Script src="https://do.featurebase.app/js/sdk.js" id="featurebase-sdk" />
|
||||
);
|
||||
|
||||
export const Changelog = ({ className }: { className?: string }) => {
|
||||
React.useEffect(() => {
|
||||
if (user.isGuest) return;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const win = window as any;
|
||||
|
||||
if (typeof win.Featurebase !== "function") {
|
||||
win.Featurebase = function (...args: unknown[]) {
|
||||
(win.Featurebase.q = win.Featurebase.q || []).push(args);
|
||||
};
|
||||
}
|
||||
|
||||
win.Featurebase("initialize_changelog_widget", {
|
||||
organization: "rallly", // Replace this with your featurebase organization name
|
||||
placement: "bottom", // Choose between right, left, top, bottom placement
|
||||
theme: "light", // Choose between dark or light theme
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (!isFeedbackEnabled) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<FeaturebaseScript />
|
||||
<Button
|
||||
className={cn("[&>*]:pointer-events-none", className)}
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
data-featurebase-changelog
|
||||
>
|
||||
<HelpCircleIcon className="h-4 w-4" />
|
||||
<span className="hidden sm:block">
|
||||
<Trans key="whatsNew" defaults="What's new?" />
|
||||
</span>
|
||||
<span
|
||||
id="fb-update-badge"
|
||||
className="bg-primary rounded-md px-1 py-px text-xs text-gray-100 empty:hidden"
|
||||
/>
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const FeaturebaseIdentify = () => {
|
||||
const { data: user } = trpc.whoami.get.useQuery();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (user?.isGuest !== false || !isFeedbackEnabled) return;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const win = window as any;
|
||||
if (typeof win.Featurebase !== "function") {
|
||||
win.Featurebase = function () {
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
(win.Featurebase.q = win.Featurebase.q || []).push(arguments);
|
||||
};
|
||||
}
|
||||
|
||||
win.Featurebase(
|
||||
"identify",
|
||||
{
|
||||
|
@ -35,10 +87,6 @@ export const FeaturebaseProvider = ({ children }: React.PropsWithChildren) => {
|
|||
},
|
||||
);
|
||||
}, [user]);
|
||||
return (
|
||||
<>
|
||||
<Script src="https://do.featurebase.app/js/sdk.js" id="featurebase-sdk" />
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
|
||||
return <FeaturebaseScript />;
|
||||
};
|
||||
|
|
|
@ -16,60 +16,53 @@ import {
|
|||
import Link from "next/link";
|
||||
import { Trans } from "next-i18next";
|
||||
|
||||
import { FeaturebaseProvider } from "@/components/featurebase";
|
||||
|
||||
const FeedbackButton = () => {
|
||||
return (
|
||||
<FeaturebaseProvider>
|
||||
<DropdownMenu modal={false}>
|
||||
<DropdownMenuTrigger className="shadow-huge fixed bottom-8 right-6 hidden h-12 w-12 items-center justify-center rounded-full bg-gray-800 hover:bg-gray-700 active:shadow-none sm:inline-flex">
|
||||
<MegaphoneIcon className="h-5 text-white" />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent sideOffset={10} align="end">
|
||||
<DropdownMenuLabel>
|
||||
<Trans i18nKey="menu" />
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
href={`https://rallly.featurebase.app/?b=feedback`}
|
||||
target={"_blank"}
|
||||
>
|
||||
<SmileIcon className="mr-2 h-4 w-4" />
|
||||
<Trans i18nKey="sendFeedback" defaults="Send Feedback" />
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
href={`https://rallly.featurebase.app/?b=feature-request`}
|
||||
target={"_blank"}
|
||||
>
|
||||
<LightbulbIcon className="mr-2 h-4 w-4" />
|
||||
<Trans
|
||||
i18nKey={"featureRequest"}
|
||||
defaults={"Request a Feature"}
|
||||
/>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
href={`https://rallly.featurebase.app/?b=bug-reports`}
|
||||
target={"_blank"}
|
||||
>
|
||||
<BugIcon className="mr-2 h-4 w-4" />
|
||||
<Trans i18nKey={"bugReport"} defaults={"Report an Issue"} />
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem asChild>
|
||||
<Link href={`https://support.rallly.co`} target={"_blank"}>
|
||||
<LifeBuoyIcon className="mr-2 h-4 w-4" />
|
||||
<Trans i18nKey={"getSupport"} defaults={"Get Support"} />
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</FeaturebaseProvider>
|
||||
<DropdownMenu modal={false}>
|
||||
<DropdownMenuTrigger className="shadow-huge fixed bottom-8 right-6 hidden h-12 w-12 items-center justify-center rounded-full bg-gray-800 hover:bg-gray-700 active:shadow-none sm:inline-flex">
|
||||
<MegaphoneIcon className="h-5 text-white" />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent sideOffset={10} align="end">
|
||||
<DropdownMenuLabel>
|
||||
<Trans i18nKey="menu" />
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
href={`https://rallly.featurebase.app/?b=feedback`}
|
||||
target={"_blank"}
|
||||
>
|
||||
<SmileIcon className="mr-2 h-4 w-4" />
|
||||
<Trans i18nKey="sendFeedback" defaults="Send Feedback" />
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
href={`https://rallly.featurebase.app/?b=feature-request`}
|
||||
target={"_blank"}
|
||||
>
|
||||
<LightbulbIcon className="mr-2 h-4 w-4" />
|
||||
<Trans i18nKey={"featureRequest"} defaults={"Request a Feature"} />
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
href={`https://rallly.featurebase.app/?b=bug-reports`}
|
||||
target={"_blank"}
|
||||
>
|
||||
<BugIcon className="mr-2 h-4 w-4" />
|
||||
<Trans i18nKey={"bugReport"} defaults={"Report an Issue"} />
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem asChild>
|
||||
<Link href={`https://support.rallly.co`} target={"_blank"}>
|
||||
<LifeBuoyIcon className="mr-2 h-4 w-4" />
|
||||
<Trans i18nKey={"getSupport"} defaults={"Get Support"} />
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import "react-big-calendar/lib/css/react-big-calendar.css";
|
||||
|
||||
import { XIcon } from "@rallly/icons";
|
||||
import dayjs from "dayjs";
|
||||
import React from "react";
|
||||
|
|
|
@ -11,14 +11,16 @@ import { Toaster } from "react-hot-toast";
|
|||
|
||||
import { Clock, ClockPreferences } from "@/components/clock";
|
||||
import { Container } from "@/components/container";
|
||||
import { Changelog, FeaturebaseIdentify } from "@/components/featurebase";
|
||||
import FeedbackButton from "@/components/feedback";
|
||||
import { Spinner } from "@/components/spinner";
|
||||
import { Trans } from "@/components/trans";
|
||||
import { UserDropdown } from "@/components/user-dropdown";
|
||||
import { isFeedbackEnabled } from "@/utils/constants";
|
||||
|
||||
import { IconComponent, NextPageWithLayout } from "../../types";
|
||||
import ModalProvider from "../modal/modal-provider";
|
||||
import { IfGuest, UserProvider } from "../user-provider";
|
||||
import { IfGuest } from "../user-provider";
|
||||
|
||||
const NavMenuItem = ({
|
||||
href,
|
||||
|
@ -35,12 +37,11 @@ const NavMenuItem = ({
|
|||
}) => {
|
||||
const router = useRouter();
|
||||
return (
|
||||
<Button variant="ghost" asChild>
|
||||
<Button variant="ghost" size="sm" asChild>
|
||||
<Link
|
||||
target={target}
|
||||
href={href}
|
||||
className={cn(
|
||||
"flex items-center gap-2.5 px-2.5 py-1.5 text-sm font-medium",
|
||||
router.asPath === href
|
||||
? "text-foreground"
|
||||
: "text-muted-foreground hover:text-foreground active:bg-gray-200/50",
|
||||
|
@ -119,17 +120,23 @@ const MainNav = () => {
|
|||
</nav>
|
||||
</div>
|
||||
<div className="flex items-center gap-x-4">
|
||||
<nav className="flex gap-x-2">
|
||||
<nav className="flex items-center gap-x-1 sm:gap-x-2">
|
||||
<IfGuest>
|
||||
<NavMenuItem
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
asChild
|
||||
className="hidden sm:flex"
|
||||
icon={LogInIcon}
|
||||
href="/login"
|
||||
label={<Trans i18nKey="login" defaults="Login" />}
|
||||
/>
|
||||
>
|
||||
<Link href="/login">
|
||||
<LogInIcon className="h-4 w-4" />
|
||||
<Trans i18nKey="login" defaults="Login" />
|
||||
</Link>
|
||||
</Button>
|
||||
</IfGuest>
|
||||
<Changelog />
|
||||
<ClockPreferences>
|
||||
<Button className="text-muted-foreground" variant="ghost">
|
||||
<Button size="sm" variant="ghost">
|
||||
<Clock />
|
||||
</Button>
|
||||
</ClockPreferences>
|
||||
|
@ -146,8 +153,9 @@ export const StandardLayout: React.FunctionComponent<{
|
|||
hideNav?: boolean;
|
||||
}> = ({ children, hideNav, ...rest }) => {
|
||||
const key = hideNav ? "no-nav" : "nav";
|
||||
|
||||
return (
|
||||
<UserProvider>
|
||||
<>
|
||||
<Toaster />
|
||||
<ModalProvider>
|
||||
<div className="flex min-h-screen flex-col" {...rest}>
|
||||
|
@ -169,9 +177,14 @@ export const StandardLayout: React.FunctionComponent<{
|
|||
</m.div>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
{process.env.NEXT_PUBLIC_FEEDBACK_EMAIL ? <FeedbackButton /> : null}
|
||||
{isFeedbackEnabled ? (
|
||||
<>
|
||||
<FeaturebaseIdentify />
|
||||
<FeedbackButton />
|
||||
</>
|
||||
) : null}
|
||||
</ModalProvider>
|
||||
</UserProvider>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@ import {
|
|||
ListIcon,
|
||||
LogInIcon,
|
||||
LogOutIcon,
|
||||
MegaphoneIcon,
|
||||
RefreshCcwIcon,
|
||||
ScrollTextIcon,
|
||||
Settings2Icon,
|
||||
UserIcon,
|
||||
UserPlusIcon,
|
||||
|
@ -26,6 +26,7 @@ import Link from "next/link";
|
|||
|
||||
import { Trans } from "@/components/trans";
|
||||
import { CurrentUserAvatar } from "@/components/user";
|
||||
import { isFeedbackEnabled } from "@/utils/constants";
|
||||
|
||||
import { IfAuthenticated, IfGuest, useUser } from "./user-provider";
|
||||
|
||||
|
@ -121,16 +122,18 @@ export const UserDropdown = () => {
|
|||
<Trans i18nKey="support" defaults="Support" />
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem asChild={true}>
|
||||
<Link
|
||||
target="_blank"
|
||||
href="https://rallly.co/blog"
|
||||
className="flex items-center gap-x-2"
|
||||
>
|
||||
<ScrollTextIcon className="h-4 w-4" />
|
||||
<Trans i18nKey="changelog" defaults="Change log" />
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
{isFeedbackEnabled ? (
|
||||
<DropdownMenuItem asChild={true}>
|
||||
<Link
|
||||
target="_blank"
|
||||
href="https://rallly.featurebase.app"
|
||||
className="flex items-center gap-x-2"
|
||||
>
|
||||
<MegaphoneIcon className="h-4 w-4" />
|
||||
<Trans i18nKey="feedback" defaults="Feedback" />
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
) : null}
|
||||
<DropdownMenuSeparator />
|
||||
<IfGuest>
|
||||
<DropdownMenuItem asChild={true}>
|
||||
|
|
|
@ -158,6 +158,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
.rbc-time-view {
|
||||
@apply border-0 border-b border-t border-gray-100;
|
||||
}
|
||||
|
|
|
@ -3,3 +3,6 @@ export const plusPlanIdMonthly = process.env
|
|||
|
||||
export const plusPlanIdYearly = process.env
|
||||
.NEXT_PUBLIC_PRO_PLAN_ID_MONTHLY as string;
|
||||
|
||||
export const isFeedbackEnabled =
|
||||
process.env.NEXT_PUBLIC_FEEDBACK_ENABLED === "true";
|
||||
|
|
2
packages/tsconfig/environment.d.ts
vendored
2
packages/tsconfig/environment.d.ts
vendored
|
@ -36,7 +36,7 @@ declare global {
|
|||
/**
|
||||
* When defined users will be able to send feedback to this email address
|
||||
*/
|
||||
NEXT_PUBLIC_FEEDBACK_EMAIL?: string;
|
||||
NEXT_PUBLIC_FEEDBACK_ENABLED?: string;
|
||||
/**
|
||||
* Users of your instance will see this as their support email
|
||||
*/
|
||||
|
|
|
@ -23,7 +23,7 @@ const buttonVariants = cva(
|
|||
},
|
||||
size: {
|
||||
default: "h-9 px-2.5 text-sm",
|
||||
sm: "h-7 text-xs px-2 rounded-md",
|
||||
sm: "h-7 text-xs px-1.5 rounded-md",
|
||||
lg: "h-11 text-base px-4 rounded-md",
|
||||
},
|
||||
},
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
"NEXT_PUBLIC_CRISP_WEBSITE_ID",
|
||||
"NEXT_PUBLIC_ENABLE_ANALYTICS",
|
||||
"NEXT_PUBLIC_ENABLE_FINALIZATION",
|
||||
"NEXT_PUBLIC_FEEDBACK_EMAIL",
|
||||
"NEXT_PUBLIC_FEEDBACK_ENABLED",
|
||||
"NEXT_PUBLIC_LANDING_PAGE_URL",
|
||||
"NEXT_PUBLIC_MAINTENANCE_MODE",
|
||||
"NEXT_PUBLIC_PADDLE_SANDBOX",
|
||||
|
|
Loading…
Add table
Reference in a new issue