From 6eb72507a59afe954b86277fe4bb5c8ce20a0734 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Thu, 20 Jul 2023 10:52:52 +0530 Subject: [PATCH] fix: notification card (#1583) --- .../notifications/notification-card.tsx | 163 ++++++------ .../notifications/notification-popover.tsx | 4 +- .../app/components/workspace/help-section.tsx | 199 ++++---------- .../components/workspace/sidebar-dropdown.tsx | 6 +- .../workspace/upgrade-to-pro-modal.tsx | 248 ------------------ apps/app/helpers/date-time.helper.ts | 2 +- .../[workspaceSlug]/settings/billing.tsx | 14 +- 7 files changed, 145 insertions(+), 491 deletions(-) delete mode 100644 apps/app/components/workspace/upgrade-to-pro-modal.tsx diff --git a/apps/app/components/notifications/notification-card.tsx b/apps/app/components/notifications/notification-card.tsx index 40728d804..321e9694c 100644 --- a/apps/app/components/notifications/notification-card.tsx +++ b/apps/app/components/notifications/notification-card.tsx @@ -10,7 +10,7 @@ import useToast from "hooks/use-toast"; import { CustomMenu, Icon, Tooltip } from "components/ui"; // helper -import { stripHTML, replaceUnderscoreIfSnakeCase } from "helpers/string.helper"; +import { stripHTML, replaceUnderscoreIfSnakeCase, truncateText } from "helpers/string.helper"; import { formatDateDistance, render12HourFormatTime, @@ -32,7 +32,7 @@ type NotificationCardProps = { const snoozeOptions = [ { - label: "1 days", + label: "1 day", value: new Date(new Date().getTime() + 24 * 60 * 60 * 1000), }, { @@ -79,99 +79,100 @@ export const NotificationCard: React.FC = (props) => { `/${workspaceSlug}/projects/${notification.project}/issues/${notification.data.issue.id}` ); }} - className={`group relative py-3 px-6 cursor-pointer ${ + className={`group w-full flex items-center gap-4 p-3 pl-6 relative cursor-pointer ${ notification.read_at === null ? "bg-custom-primary-70/5" : "hover:bg-custom-background-200" }`} > {notification.read_at === null && ( )} -
-
- {notification.triggered_by_details.avatar && - notification.triggered_by_details.avatar !== "" ? ( -
- Profile Image -
- ) : ( -
- - {notification.triggered_by_details.first_name[0].toUpperCase()} - -
- )} -
-
-
- - {notification.triggered_by_details.first_name}{" "} - {notification.triggered_by_details.last_name}{" "} +
+ {notification.triggered_by_details.avatar && + notification.triggered_by_details.avatar !== "" ? ( +
+ Profile Image +
+ ) : ( +
+ + {notification.triggered_by_details.first_name[0].toUpperCase()} - {notification.data.issue_activity.field !== "comment" && - notification.data.issue_activity.verb}{" "} - {notification.data.issue_activity.field === "comment" - ? "commented" - : notification.data.issue_activity.field === "None" - ? null - : replaceUnderscoreIfSnakeCase(notification.data.issue_activity.field)}{" "} - {notification.data.issue_activity.field !== "comment" && - notification.data.issue_activity.field !== "None" - ? "to" - : ""} - - {" "} - {notification.data.issue_activity.field !== "None" ? ( - notification.data.issue_activity.field !== "comment" ? ( - notification.data.issue_activity.field === "target_date" ? ( - renderShortDateWithYearFormat(notification.data.issue_activity.new_value) - ) : notification.data.issue_activity.field === "attachment" ? ( - "the issue" - ) : stripHTML(notification.data.issue_activity.new_value).length > 55 ? ( - stripHTML(notification.data.issue_activity.new_value).slice(0, 50) + "..." - ) : ( - stripHTML(notification.data.issue_activity.new_value) - ) +
+ )} +
+
+
+ + {notification.triggered_by_details.first_name}{" "} + {notification.triggered_by_details.last_name}{" "} + + {notification.data.issue_activity.field !== "comment" && + notification.data.issue_activity.verb}{" "} + {notification.data.issue_activity.field === "comment" + ? "commented" + : notification.data.issue_activity.field === "None" + ? null + : replaceUnderscoreIfSnakeCase(notification.data.issue_activity.field)}{" "} + {notification.data.issue_activity.field !== "comment" && + notification.data.issue_activity.field !== "None" + ? "to" + : ""} + + {" "} + {notification.data.issue_activity.field !== "None" ? ( + notification.data.issue_activity.field !== "comment" ? ( + notification.data.issue_activity.field === "target_date" ? ( + renderShortDateWithYearFormat(notification.data.issue_activity.new_value) + ) : notification.data.issue_activity.field === "attachment" ? ( + "the issue" + ) : stripHTML(notification.data.issue_activity.new_value).length > 55 ? ( + stripHTML(notification.data.issue_activity.new_value).slice(0, 50) + "..." ) : ( - - {`"`} - {notification.data.issue_activity.new_value.length > 55 - ? notification?.data?.issue_activity?.issue_comment?.slice(0, 50) + "..." - : notification.data.issue_activity.issue_comment} - {`"`} - + stripHTML(notification.data.issue_activity.new_value) ) ) : ( - "the issue and assigned it to you." - )} - -
- -
-

- {notification.data.issue.identifier}-{notification.data.issue.sequence_id}{" "} - {notification.data.issue.name} -

- {notification.snoozed_till ? ( -

- - Till {renderShortDate(notification.snoozed_till)},{" "} - {render12HourFormatTime(notification.snoozed_till)} + {`"`} + {notification.data.issue_activity.new_value.length > 55 + ? notification?.data?.issue_activity?.issue_comment?.slice(0, 50) + "..." + : notification.data.issue_activity.issue_comment} + {`"`} -

+ ) ) : ( -

{formatDateDistance(notification.created_at)}

+ "the issue and assigned it to you." )} -
+ +
+ +
+

+ {truncateText( + `${notification.data.issue.identifier}-${notification.data.issue.sequence_id} ${notification.data.issue.name}`, + 50 + )} +

+ {notification.snoozed_till ? ( +

+ + + Till {renderShortDate(notification.snoozed_till)},{" "} + {render12HourFormatTime(notification.snoozed_till)} + +

+ ) : ( +

+ {formatDateDistance(notification.created_at)} +

+ )}
-
{[ { @@ -192,7 +193,7 @@ export const NotificationCard: React.FC = (props) => { { id: 2, name: notification.archived_at ? "Unarchive" : "Archive", - icon: "archive", + icon: notification.archived_at ? "unarchive" : "archive", onClick: () => { markNotificationArchivedStatus(notification.id).then(() => { setToastAlert({ @@ -213,7 +214,7 @@ export const NotificationCard: React.FC = (props) => { item.onClick(); }} key={item.id} - className="text-sm flex w-full items-center gap-x-2 bg-custom-background-80 hover:bg-custom-background-100 p-0.5 rounded" + className="text-sm flex w-full items-center gap-x-2 bg-custom-background-80 hover:bg-custom-background-100 p-0.5 rounded outline-none" > diff --git a/apps/app/components/notifications/notification-popover.tsx b/apps/app/components/notifications/notification-popover.tsx index 001669721..a7369bbb1 100644 --- a/apps/app/components/notifications/notification-popover.tsx +++ b/apps/app/components/notifications/notification-popover.tsx @@ -96,7 +96,7 @@ export const NotificationPopover = () => { className={`group flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-sm font-medium outline-none ${ isActive ? "bg-custom-primary-100/10 text-custom-primary-100" - : "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80 focus:bg-custom-sidebar-background-80" + : "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80" } ${sidebarCollapse ? "justify-center" : ""}`} > @@ -282,7 +282,7 @@ export const NotificationPopover = () => {
) ) : ( - + diff --git a/apps/app/components/workspace/help-section.tsx b/apps/app/components/workspace/help-section.tsx index 5c00b8490..0819a50d0 100644 --- a/apps/app/components/workspace/help-section.tsx +++ b/apps/app/components/workspace/help-section.tsx @@ -1,33 +1,17 @@ import React, { useRef, useState } from "react"; -import { useRouter } from "next/router"; - import Link from "next/link"; -import useSWR from "swr"; - // headless ui import { Transition } from "@headlessui/react"; -// services -import workspaceService from "services/workspace.service"; // hooks import useTheme from "hooks/use-theme"; -import useUser from "hooks/use-user"; import useOutsideClickDetector from "hooks/use-outside-click-detector"; -// components -import UpgradeToProModal from "./upgrade-to-pro-modal"; // ui -import { CircularProgress, Icon } from "components/ui"; +import { Icon } from "components/ui"; // icons -import { - ArrowLongLeftIcon, - ChatBubbleOvalLeftEllipsisIcon, - ArrowUpCircleIcon, - XMarkIcon, -} from "@heroicons/react/24/outline"; +import { ArrowLongLeftIcon, ChatBubbleOvalLeftEllipsisIcon } from "@heroicons/react/24/outline"; import { QuestionMarkCircleIcon, DocumentIcon, DiscordIcon, GithubIcon } from "components/icons"; -// fetch-keys -import { WORKSPACE_DETAILS } from "constants/fetch-keys"; const helpOptions = [ { @@ -58,150 +42,77 @@ export interface WorkspaceHelpSectionProps { } export const WorkspaceHelpSection: React.FC = ({ setSidebarActive }) => { - const [alert, setAlert] = useState(false); const [isNeedHelpOpen, setIsNeedHelpOpen] = useState(false); const helpOptionsRef = useRef(null); - const router = useRouter(); - const { workspaceSlug } = router.query; - const { collapsed: sidebarCollapse, toggleCollapsed } = useTheme(); useOutsideClickDetector(helpOptionsRef, () => setIsNeedHelpOpen(false)); - const { user } = useUser(); - - const [upgradeModal, setUpgradeModal] = useState(false); - - const { data: workspaceDetails } = useSWR( - workspaceSlug ? WORKSPACE_DETAILS(workspaceSlug as string) : null, - workspaceSlug ? () => workspaceService.getWorkspace(workspaceSlug as string) : null - ); - - const issueNumber = workspaceDetails?.total_issues || 0; - return ( <> - setUpgradeModal(false)} - user={user} - issueNumber={issueNumber} - /> - {!sidebarCollapse && (alert || issueNumber >= 750) && ( -
= 750 - ? "bg-red-500/10 text-red-600" - : issueNumber >= 500 - ? "bg-yellow-500/10 text-yellow-600" - : "text-green-600" - }`} - > -
- -
Free Plan
- {issueNumber < 750 && ( -
setAlert(false)} - > - -
- )} -
-
- This workspace has used {issueNumber} of its 1024 issues creation limit ( - {((issueNumber / 1024) * 100).toFixed(0)} - %). -
-
- )}
- {alert || issueNumber >= 750 ? ( - - ) : ( - + {!sidebarCollapse && ( +
+ Free Plan +
)} - - - - + onClick={() => { + const e = new KeyboardEvent("keydown", { + key: "h", + }); + document.dispatchEvent(e); + }} + title="Shortcuts" + > + + + + + +
[ export const WorkspaceSidebarDropdown = () => { const router = useRouter(); const { workspaceSlug } = router.query; - // fetching user details + const { user, mutateUser } = useUser(); const { collapsed: sidebarCollapse } = useThemeHook(); @@ -139,8 +139,8 @@ export const WorkspaceSidebarDropdown = () => { leaveTo="transform opacity-0 scale-95" >
{user?.email}
diff --git a/apps/app/components/workspace/upgrade-to-pro-modal.tsx b/apps/app/components/workspace/upgrade-to-pro-modal.tsx deleted file mode 100644 index 99190c9cc..000000000 --- a/apps/app/components/workspace/upgrade-to-pro-modal.tsx +++ /dev/null @@ -1,248 +0,0 @@ -import React, { useState, useEffect } from "react"; -// headless ui -import { Dialog, Transition } from "@headlessui/react"; -// icons -import { XCircleIcon, RocketLaunchIcon } from "@heroicons/react/24/outline"; -import { CheckCircleIcon } from "@heroicons/react/24/solid"; -// ui -import { CircularProgress } from "components/ui"; -// types -import type { ICurrentUserResponse, IWorkspace } from "types"; - -declare global { - interface Window { - supabase: any; - } -} - -type Props = { - isOpen: boolean; - onClose: () => void; - user: ICurrentUserResponse | undefined; - issueNumber: number; -}; - -const UpgradeToProModal: React.FC = ({ isOpen, onClose, user, issueNumber }) => { - const [supabaseClient, setSupabaseClient] = useState(null); - - useEffect(() => { - // Create a Supabase client - if (process.env.NEXT_PUBLIC_SUPABASE_URL && process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY) { - const { createClient } = window.supabase; - const supabase = createClient( - process.env.NEXT_PUBLIC_SUPABASE_URL, - process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY, - { - auth: { - autoRefreshToken: false, - persistSession: false, - }, - } - ); - - if (supabase) { - setSupabaseClient(supabase); - } - } - }, []); - - const [isLoading, setIsLoading] = useState(false); - - const handleClose = () => { - onClose(); - setIsLoading(false); - }; - - const proFeatures = [ - "Everything in free", - "Unlimited users", - "Unlimited file uploads", - "Priority Support", - "Custom Theming", - "Access to Roadmap", - "Plane AI (GPT unlimited)", - ]; - - const [errorMessage, setErrorMessage] = useState( - null - ); - const [loader, setLoader] = useState(false); - const submitEmail = async () => { - setLoader(true); - const payload = { email: user?.email || "" }; - - if (supabaseClient) { - if (payload?.email) { - const emailExists = await supabaseClient - .from("web-waitlist") - .select("id,email,count") - .eq("email", payload?.email); - if (emailExists.data.length === 0) { - const emailCreation = await supabaseClient - .from("web-waitlist") - .insert([{ email: payload?.email, count: 1, last_visited: new Date() }]) - .select("id,email,count"); - if (emailCreation.status === 201) - setErrorMessage({ status: "success", message: "Successfully registered." }); - else setErrorMessage({ status: "insert_error", message: "Insertion Error." }); - } else { - const emailCountUpdate = await supabaseClient - .from("web-waitlist") - .upsert({ - id: emailExists.data[0]?.id, - count: emailExists.data[0]?.count + 1, - last_visited: new Date(), - }) - .select("id,email,count"); - if (emailCountUpdate.status === 201) - setErrorMessage({ - status: "email_already_exists", - message: "Email already exists.", - }); - else setErrorMessage({ status: "update_error", message: "Update Error." }); - } - } else setErrorMessage({ status: "email_required", message: "Please provide email." }); - } else - setErrorMessage({ - status: "supabase_error", - message: "Network error. Please try again later.", - }); - - setLoader(false); - }; - - return ( - - - -
- - -
-
- - -
-
-
-
= 750 - ? "text-red-600" - : issueNumber >= 500 - ? "text-yellow-600" - : "text-green-600" - }`} - title="Shortcuts" - > - 100 ? 100 : (issueNumber / 1024) * 100 - } - /> -
-
-
Upgrade to pro
-
- This workspace has used {issueNumber} of its 1024 issues creation limit ( - {((issueNumber / 1024) * 100).toFixed(2)}%). -
-
-
- -
-
-
-
- -
-
-
Order summary
-
- Priority support, file uploads, and access to premium features. -
- -
- {proFeatures.map((feature, index) => ( -
-
- -
-
{feature}
-
- ))} -
-
-
-
-
-
-
Summary
-
- -
-
-
- Plane application is currently in dev-mode. We will soon introduce Pro plans - once general availability has been established. Stay tuned for more updates. - In the meantime, Plane remains free and unrestricted. -

- We{"'"}ll ensure a smooth transition from the community version to the Pro - plan for you. -
- - {errorMessage && ( -
- {errorMessage?.message} -
- )} -
-
-
-
-
-
-
-
- ); -}; - -export default UpgradeToProModal; diff --git a/apps/app/helpers/date-time.helper.ts b/apps/app/helpers/date-time.helper.ts index ea2170afb..a98f08cb7 100644 --- a/apps/app/helpers/date-time.helper.ts +++ b/apps/app/helpers/date-time.helper.ts @@ -217,7 +217,7 @@ export const render12HourFormatTime = (date: string | Date): string => { if (hours > 12) hours -= 12; } - return hours + ":" + minutes + " " + period; + return hours + ":" + (minutes < 10 ? `0${minutes}` : minutes) + " " + period; }; export const render24HourFormatTime = (date: string | Date): string => { diff --git a/apps/app/pages/[workspaceSlug]/settings/billing.tsx b/apps/app/pages/[workspaceSlug]/settings/billing.tsx index 3c2934c6c..2367b94b4 100644 --- a/apps/app/pages/[workspaceSlug]/settings/billing.tsx +++ b/apps/app/pages/[workspaceSlug]/settings/billing.tsx @@ -44,28 +44,18 @@ const BillingSettings: NextPage = () => {

Billing & Plans

-

[Free launch preview] plan Pro

+

Free launch preview

-
-
-

Payment due

-

--

-
-

Current plan

You are currently using the free plan

- View Plans and Upgrade + View Plans
-
-

Billing history

-

There are no invoices to display

-