Path: blob/main/components/dashboard/src/repositories/coachmarks/MigrationCoachmark.tsx
2501 views
/**1* Copyright (c) 2024 Gitpod GmbH. All rights reserved.2* Licensed under the GNU Affero General Public License (AGPL).3* See License.AGPL.txt in the project root for license information.4*/56import { Button } from "@podkit/buttons/Button";7import { Popover, PopoverArrow, PopoverContent, PopoverTrigger } from "@podkit/popover/Popover";8import { Text } from "@podkit/typography/Text";9import { Truck } from "lucide-react";10import { PropsWithChildren, useCallback, useMemo, useState } from "react";11import { Link, useHistory } from "react-router-dom";12import { useUserLoader } from "../../hooks/use-user-loader";13import { useUpdateCurrentUserMutation } from "../../data/current-user/update-mutation";14import dayjs from "dayjs";15import { trackEvent } from "../../Analytics";1617const COACHMARK_KEY = "projects_configuration_migration";1819type Props = PropsWithChildren<{}>;20export const ConfigurationsMigrationCoachmark = ({ children }: Props) => {21const [isOpen, setIsOpen] = useState(true);2223const history = useHistory();2425const { user } = useUserLoader();26const { mutate: updateUser } = useUpdateCurrentUserMutation();2728const dismiss = useCallback(() => {29updateUser(30{31additionalData: { profile: { coachmarksDismissals: { [COACHMARK_KEY]: dayjs().toISOString() } } },32},33{34onSettled: (_, error) => {35trackEvent("coachmark_dismissed", {36name: COACHMARK_KEY,37success: !(error instanceof Error),38});39},40},41);42}, [updateUser]);4344const show = useMemo<boolean>(() => {45if (!isOpen || !user) {46return false;47}4849// For the users signing up after our launch of configurations, don't show it50if (user.createdAt && user.createdAt.toDate() > new Date("2/21/2024")) {51return false;52}5354// User already knows about the feature55if (history.location.pathname.startsWith("/repositories")) {56dismiss();57return false;58}5960return !user.profile?.coachmarksDismissals[COACHMARK_KEY];61}, [dismiss, history.location.pathname, isOpen, user]);6263const handleClose = useCallback(() => {64setIsOpen(false);65// do not store the dismissal if the popover is not shown66if (show) {67dismiss();68}69}, [dismiss, show]);7071return (72<Popover open={show}>73<PopoverTrigger onClick={handleClose}>{children}</PopoverTrigger>74<PopoverContent align={"start"} className="border-pk-border-base relative flex flex-col">75<PopoverArrow asChild>76<div className="mb-[6px] ml-2 inline-block overflow-hidden rotate-180 relative">77<div className="h-3 w-5 origin-bottom-left rotate-45 transform border border-pk-border-base bg-pk-surface-primary before:absolute before:bottom-0 before:left-0 before:w-full before:h-[1px] before:bg-pk-surface-primary" />78</div>79</PopoverArrow>8081<Text className="flex flex-row gap-2 text-lg font-bold items-center pt-3">82<Truck /> Projects have moved83</Text>84<Text className="text-pk-content-secondary text-base pb-4 pt-2">85Projects are now called “86<Link to={"/repositories"} className="gp-link">87Repository settings88</Link>89”. You can find them in your organization menu.90</Text>91<Button className="self-end" variant={"secondary"} onClick={handleClose}>92Dismiss93</Button>94</PopoverContent>95</Popover>96);97};9899100