[WEB-3203] fix: dashboard widgets' empty state content and assets (#6450)

* fix: empty state content

* chore: replace margin with padding
This commit is contained in:
Aaryan Khandelwal 2025-01-24 15:23:41 +05:30 committed by GitHub
parent 0b53912295
commit d08c03f557
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 157 additions and 162 deletions

View file

@ -105,14 +105,14 @@ ul[data-type="taskList"] li > div {
} }
ul[data-type="taskList"] li > label input[type="checkbox"] { ul[data-type="taskList"] li > label input[type="checkbox"] {
border: 1px solid rgba(var(--color-border-300)) !important; border: 1px solid rgba(var(--color-text-100), 0.2) !important;
outline: none; outline: none;
border-radius: 2px; border-radius: 2px;
transform: scale(1.05); transform: scale(1.05);
} }
.ProseMirror[contenteditable="true"] input[type="checkbox"]:hover { .ProseMirror[contenteditable="true"] input[type="checkbox"]:hover {
background-color: rgba(var(--color-background-80)); background-color: rgba(var(--color-text-100), 0.1);
} }
.ProseMirror[contenteditable="false"] input[type="checkbox"] { .ProseMirror[contenteditable="false"] input[type="checkbox"] {

View file

@ -12,7 +12,7 @@ import { getEditorFileHandlers } from "@/helpers/editor.helper";
// plane web hooks // plane web hooks
import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging"; import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging";
import { useFileSize } from "@/plane-web/hooks/use-file-size"; import { useFileSize } from "@/plane-web/hooks/use-file-size";
import { Toolbar } from "./toolbar"; import { StickyEditorToolbar } from "./toolbar";
interface StickyEditorWrapperProps interface StickyEditorWrapperProps
extends Omit<ILiteTextEditor, "disabledExtensions" | "fileHandler" | "mentionHandler"> { extends Omit<ILiteTextEditor, "disabledExtensions" | "fileHandler" | "mentionHandler"> {
@ -87,7 +87,7 @@ export const StickyEditor = React.forwardRef<EditorRefApi, StickyEditorWrapperPr
isFocused ? "max-h-[200px] opacity-100 scale-y-100 mt-3" : "max-h-0 opacity-0 scale-y-0 invisible" isFocused ? "max-h-[200px] opacity-100 scale-y-100 mt-3" : "max-h-0 opacity-0 scale-y-0 invisible"
)} )}
> >
<Toolbar <StickyEditorToolbar
executeCommand={(item) => { executeCommand={(item) => {
// TODO: update this while toolbar homogenization // TODO: update this while toolbar homogenization
// @ts-expect-error type mismatch here // @ts-expect-error type mismatch here

View file

@ -23,7 +23,7 @@ type Props = {
const toolbarItems = TOOLBAR_ITEMS.sticky; const toolbarItems = TOOLBAR_ITEMS.sticky;
export const Toolbar: React.FC<Props> = (props) => { export const StickyEditorToolbar: React.FC<Props> = (props) => {
const { executeCommand, editorRef, handleColorChange, handleDelete } = props; const { executeCommand, editorRef, handleColorChange, handleDelete } = props;
// State to manage active states of toolbar items // State to manage active states of toolbar items
@ -69,7 +69,11 @@ export const Toolbar: React.FC<Props> = (props) => {
</p> </p>
} }
> >
<button onClick={() => setShowColorPalette(!showColorPalette)} className="flex text-custom-text-300"> <button
type="button"
onClick={() => setShowColorPalette(!showColorPalette)}
className="flex text-custom-text-100/50"
>
<Palette className="size-4 my-auto" /> <Palette className="size-4 my-auto" />
</button> </button>
</Tooltip> </Tooltip>
@ -95,7 +99,7 @@ export const Toolbar: React.FC<Props> = (props) => {
type="button" type="button"
onClick={() => executeCommand(item)} onClick={() => executeCommand(item)}
className={cn( className={cn(
"grid place-items-center aspect-square rounded-sm p-0.5 text-custom-text-300", "grid place-items-center aspect-square rounded-sm p-0.5 text-custom-text-100/50",
{} {}
)} )}
> >
@ -122,7 +126,7 @@ export const Toolbar: React.FC<Props> = (props) => {
</p> </p>
} }
> >
<button onClick={handleDelete} className="my-auto text-custom-text-300"> <button type="button" onClick={handleDelete} className="my-auto text-custom-text-100/50">
<Trash2 className="size-4" /> <Trash2 className="size-4" />
</button> </button>
</Tooltip> </Tooltip>

View file

@ -1 +1,4 @@
export * from "./root"; export * from "./links";
export * from "./no-projects";
export * from "./recents";
export * from "./stickies";

View file

@ -1,12 +1,10 @@
import { Link2 } from "lucide-react"; import { Link2 } from "lucide-react";
export const LinksEmptyState = () => ( export const LinksEmptyState = () => (
<div className="min-h-[110px] flex w-full justify-center py-6 bg-custom-border-100 rounded"> <div className="min-h-[110px] w-full flex items-center justify-center gap-2 py-6 bg-custom-background-90 text-custom-text-400 rounded">
<div className="m-auto flex gap-2"> <div className="flex-shrink-0 size-[30px] grid place-items-center">
<Link2 size={30} className="text-custom-text-400/40 -rotate-45" /> <Link2 className="size-6 -rotate-45" />
<div className="text-custom-text-400 text-sm text-center my-auto">
Add any links you need for quick access to your work.
</div>
</div> </div>
<p className="text-sm text-center font-medium">Save links to work things that you{"'"}d like handy.</p>
</div> </div>
); );

View file

@ -2,6 +2,8 @@ import React from "react";
import Link from "next/link"; import Link from "next/link";
import { useParams } from "next/navigation"; import { useParams } from "next/navigation";
import { Briefcase, Hotel, Users } from "lucide-react"; import { Briefcase, Hotel, Users } from "lucide-react";
// plane ui
import { Avatar } from "@plane/ui";
// helpers // helpers
import { getFileURL } from "@/helpers/file.helper"; import { getFileURL } from "@/helpers/file.helper";
// hooks // hooks
@ -9,7 +11,7 @@ import { useCommandPalette, useEventTracker, useUser, useUserPermissions } from
// plane web constants // plane web constants
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants"; import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants";
export const EmptyWorkspace = () => { export const NoProjectsEmptyState = () => {
// navigation // navigation
const { workspaceSlug } = useParams(); const { workspaceSlug } = useParams();
// store hooks // store hooks
@ -26,11 +28,11 @@ export const EmptyWorkspace = () => {
const EMPTY_STATE_DATA = [ const EMPTY_STATE_DATA = [
{ {
id: "create-project", id: "create-project",
title: "Create a project", title: "Create a project.",
description: "Create your first project now to get started", description: "Most things start with a project in Plane.",
icon: <Briefcase className="w-[40px] h-[40px] text-custom-primary-100" />, icon: <Briefcase className="size-12 text-custom-primary-100" />,
cta: { cta: {
text: "Create Project", text: "Get started",
onClick: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => { onClick: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
if (!canCreateProject) return; if (!canCreateProject) return;
e.preventDefault(); e.preventDefault();
@ -42,66 +44,56 @@ export const EmptyWorkspace = () => {
}, },
{ {
id: "invite-team", id: "invite-team",
title: "Invite your team", title: "Invite your team.",
description: "The sub text will be of two lines and that will be placed.", description: "Build, ship, and manage with coworkers.",
icon: <Users className="w-[40px] h-[40px] text-custom-primary-100" />, icon: <Users className="size-12 text-custom-primary-100" />,
cta: { cta: {
text: "Invite now", text: "Get them in",
link: `/${workspaceSlug}/settings/members`, link: `/${workspaceSlug}/settings/members`,
}, },
}, },
{ {
id: "configure-workspace", id: "configure-workspace",
title: "Configure your workspace", title: "Set up your workspace.",
description: "The sub text will be of two lines and that will be placed.", description: "Turn features on or off or go beyond that.",
icon: <Hotel className="w-[40px] h-[40px] text-custom-primary-100" />, icon: <Hotel className="size-12 text-custom-primary-100" />,
cta: { cta: {
text: "Configure workspace", text: "Configure this workspace",
link: "settings", link: "settings",
}, },
}, },
{ {
id: "personalize-account", id: "personalize-account",
title: "Personalize your account", title: "Make Plane yours.",
description: "The sub text will be of two lines and that will be placed.", description: "Choose your picture, colors, and more.",
icon: icon: (
currentUser?.avatar_url && currentUser?.avatar_url.trim() !== "" ? ( <Avatar
<Link href={`/${workspaceSlug}/profile/${currentUser?.id}`}> src={getFileURL(currentUser?.avatar_url ?? "")}
<span className="relative flex h-6 w-6 items-center justify-center rounded-full p-4 capitalize text-white"> name={currentUser?.display_name}
<img size={48}
src={getFileURL(currentUser?.avatar_url)} className="text-xl"
className="absolute left-0 top-0 h-full w-full rounded-full object-cover" showTooltip={false}
alt={currentUser?.display_name || currentUser?.email}
/> />
</span>
</Link>
) : (
<Link href={`/${workspaceSlug}/profile/${currentUser?.id}`}>
<span className="relative flex h-6 w-6 items-center justify-center rounded-full bg-gray-700 p-4 capitalize text-white text-sm">
{(currentUser?.email ?? currentUser?.display_name ?? "?")[0]}
</span>
</Link>
), ),
cta: { cta: {
text: "Personalize account", text: "Personalize now",
link: "/profile", link: "/profile",
}, },
}, },
]; ];
return ( return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4">
{EMPTY_STATE_DATA.map((item) => ( {EMPTY_STATE_DATA.map((item) => (
<div <div
key={item.id} key={item.id}
className="flex flex-col items-center justify-center py-8 bg-custom-background-100 rounded-lg text-center border border-custom-border-200/40" className="flex flex-col items-center justify-center p-6 bg-custom-background-100 rounded-lg text-center border border-custom-border-200/40"
> >
<div className="flex items-center justify-center bg-custom-primary-100/10 rounded-full w-[80px] h-[80px] mb-4"> <div className="grid place-items-center bg-custom-primary-100/10 rounded-full size-24 mb-3">
<span className="text-3xl my-auto">{item.icon}</span> <span className="text-3xl my-auto">{item.icon}</span>
</div> </div>
<h3 className="text-lg font-medium text-custom-text-100 mb-2">{item.title}</h3> <h3 className="text-base font-medium text-custom-text-100 mb-2">{item.title}</h3>
<p className="text-sm text-custom-text-200 mb-4 w-[80%] flex-1">{item.description}</p> <p className="text-sm text-custom-text-300 mb-2">{item.description}</p>
{item.cta.link ? ( {item.cta.link ? (
<Link <Link
href={item.cta.link} href={item.cta.link}
@ -111,6 +103,7 @@ export const EmptyWorkspace = () => {
</Link> </Link>
) : ( ) : (
<button <button
type="button"
className="text-custom-primary-100 hover:text-custom-primary-200 text-sm font-medium" className="text-custom-primary-100 hover:text-custom-primary-200 text-sm font-medium"
onClick={item.cta.onClick} onClick={item.cta.onClick}
> >

View file

@ -1,38 +1,41 @@
import { Briefcase, FileText, History } from "lucide-react"; import { Briefcase, FileText, History } from "lucide-react";
// plane ui
import { LayersIcon } from "@plane/ui"; import { LayersIcon } from "@plane/ui";
export const RecentsEmptyState = ({ type }: { type: string }) => { const getDisplayContent = (type: string) => {
const getDisplayContent = () => {
switch (type) { switch (type) {
case "project": case "project":
return { return {
icon: <Briefcase size={30} className="text-custom-text-400/40" />, icon: Briefcase,
text: "Your recent projects will appear here once you visit one.", text: "Projects you go into or have assigned work in will show up here.",
}; };
case "page": case "page":
return { return {
icon: <FileText size={30} className="text-custom-text-400/40" />, icon: FileText,
text: "Your recent pages will appear here once you visit one.", text: "Create, see, or change something on pages you have access to and see them here.",
}; };
case "issue": case "issue":
return { return {
icon: <LayersIcon className="text-custom-text-400/40 w-[30px] h-[30px]" />, icon: LayersIcon,
text: "Your recent issues will appear here once you visit one.", text: "Let's see some issues to see them show up here.",
}; };
default: default:
return { return {
icon: <History size={30} className="text-custom-text-400/40" />, icon: History,
text: "You dont have any recent items yet.", text: "Whatever you see and act on in Plane will show up here.",
}; };
} }
}; };
const { icon, text } = getDisplayContent();
export const RecentsEmptyState = ({ type }: { type: string }) => {
const displayContent = getDisplayContent(type);
return ( return (
<div className="min-h-[120px] flex w-full justify-center py-6 bg-custom-border-100 rounded"> <div className="min-h-[110px] w-full flex items-center justify-center gap-2 py-6 bg-custom-background-90 text-custom-text-400 rounded">
<div className="m-auto flex gap-2"> <div className="flex-shrink-0 size-[30px] grid place-items-center">
{icon} <div className="text-custom-text-400 text-sm text-center my-auto">{text}</div> <displayContent.icon className="size-6" />
</div> </div>
<p className="text-sm text-center font-medium">{displayContent.text}</p>
</div> </div>
); );
}; };

View file

@ -2,12 +2,12 @@
import { RecentStickyIcon } from "@plane/ui"; import { RecentStickyIcon } from "@plane/ui";
export const StickiesEmptyState = () => ( export const StickiesEmptyState = () => (
<div className="min-h-[110px] flex w-full justify-center py-6 bg-custom-border-100 rounded"> <div className="min-h-[110px] w-full flex items-center justify-center gap-2 py-6 bg-custom-background-90 text-custom-text-400 rounded">
<div className="m-auto flex gap-2"> <div className="flex-shrink-0 size-[30px] grid place-items-center">
<RecentStickyIcon className="h-[30px] w-[30px] text-custom-text-400/40" /> <RecentStickyIcon className="size-6" />
<div className="text-custom-text-400 text-sm text-center my-auto">
No stickies yet. Add one to start making quick notes.
</div>
</div> </div>
<p className="text-sm text-center font-medium">
Jot down an idea, capture an aha, or record a brainwave. Add a sticky to get started.
</p>
</div> </div>
); );

View file

@ -14,7 +14,7 @@ export const AddLink = (props: TProps) => {
<div className="rounded p-2 bg-custom-background-80/40 w-8 h-8 my-auto"> <div className="rounded p-2 bg-custom-background-80/40 w-8 h-8 my-auto">
<PlusIcon className="h-4 w-4 stroke-2 text-custom-text-350" /> <PlusIcon className="h-4 w-4 stroke-2 text-custom-text-350" />
</div> </div>
<div className="text-sm font-medium my-auto">Add quick Link</div> <div className="text-sm font-medium my-auto">Add quicklink</div>
</button> </button>
); );
}; };

View file

@ -66,9 +66,7 @@ export const LinkCreateUpdateModal: FC<TLinkCreateEditModal> = observer((props)
<ModalCore isOpen={isModalOpen} handleClose={onClose}> <ModalCore isOpen={isModalOpen} handleClose={onClose}>
<form onSubmit={handleSubmit(handleFormSubmit)}> <form onSubmit={handleSubmit(handleFormSubmit)}>
<div className="space-y-5 p-5"> <div className="space-y-5 p-5">
<h3 className="text-xl font-medium text-custom-text-200"> <h3 className="text-xl font-medium text-custom-text-200">{preloadedData?.id ? "Update" : "Add"} quicklink</h3>
{preloadedData?.id ? "Update" : "Add"} quick link
</h3>
<div className="mt-2 space-y-3"> <div className="mt-2 space-y-3">
<div> <div>
<label htmlFor="url" className="mb-2 text-custom-text-200 text-base font-medium"> <label htmlFor="url" className="mb-2 text-custom-text-200 text-base font-medium">

View file

@ -1,15 +1,17 @@
"use client"; "use client";
import { FC } from "react"; import { FC } from "react";
// hooks
// ui
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { Pencil, Trash2, ExternalLink, EllipsisVertical, Link2, Link } from "lucide-react"; import { Pencil, Trash2, ExternalLink, Link2, Link } from "lucide-react";
// plane ui
import { TOAST_TYPE, setToast, CustomMenu, TContextMenuItem } from "@plane/ui"; import { TOAST_TYPE, setToast, CustomMenu, TContextMenuItem } from "@plane/ui";
// helpers // plane utils
import { cn, copyTextToClipboard } from "@plane/utils"; import { cn, copyTextToClipboard } from "@plane/utils";
// helpers
import { calculateTimeAgo } from "@/helpers/date-time.helper"; import { calculateTimeAgo } from "@/helpers/date-time.helper";
// hooks
import { useHome } from "@/hooks/store/use-home"; import { useHome } from "@/hooks/store/use-home";
// types
import { TLinkOperations } from "./use-links"; import { TLinkOperations } from "./use-links";
export type TProjectLinkDetail = { export type TProjectLinkDetail = {
@ -75,25 +77,17 @@ export const ProjectLinkDetail: FC<TProjectLinkDetail> = observer((props) => {
return ( return (
<div <div
onClick={handleOpenInNewTab} onClick={handleOpenInNewTab}
className="cursor-pointer group btn btn-primary flex bg-custom-background-100 px-4 w-[230px] h-[56px] border-[0.5px] border-custom-border-200 rounded-md gap-4 hover:shadow-md" className="cursor-pointer group flex items-center bg-custom-background-100 px-4 w-[230px] h-[56px] border-[0.5px] border-custom-border-200 rounded-md gap-4 hover:shadow-md transition-shadow"
> >
<div className="rounded p-2 bg-custom-background-80/40 w-8 h-8 my-auto"> <div className="flex-shrink-0 size-8 rounded p-2 bg-custom-background-80 grid place-items-center">
<Link2 className="h-4 w-4 stroke-2 text-custom-text-350 -rotate-45" /> <Link2 className="size-4 stroke-2 text-custom-text-350 -rotate-45" />
</div> </div>
<div className="my-auto flex-1"> <div className="flex-1 truncate">
<div className="text-sm font-medium truncate">{linkDetail.title || linkDetail.url}</div> <div className="text-sm font-medium truncate">{linkDetail.title || linkDetail.url}</div>
<div className="text-xs font-medium text-custom-text-400">{calculateTimeAgo(linkDetail.created_at)}</div> <div className="text-xs font-medium text-custom-text-400">{calculateTimeAgo(linkDetail.created_at)}</div>
</div> </div>
<div className="hidden group-hover:block">
<CustomMenu <CustomMenu placement="bottom-end" menuItemsClassName="z-20" closeOnSelect verticalEllipsis>
customButton={
<EllipsisVertical className="opacity-0 h-4 w-4 stroke-2 text-custom-text-350 group-hover:opacity-100" />
}
placement="bottom-end"
menuItemsClassName="z-20"
closeOnSelect
className=" my-auto"
>
{MENU_ITEMS.map((item) => ( {MENU_ITEMS.map((item) => (
<CustomMenu.MenuItem <CustomMenu.MenuItem
key={item.key} key={item.key}
@ -124,5 +118,6 @@ export const ProjectLinkDetail: FC<TProjectLinkDetail> = observer((props) => {
))} ))}
</CustomMenu> </CustomMenu>
</div> </div>
</div>
); );
}); });

View file

@ -38,9 +38,9 @@ export const ProjectLinkList: FC<TProjectLinkList> = observer((props) => {
buttonClassName="bg-custom-background-90/20" buttonClassName="bg-custom-background-90/20"
> >
<div className="flex gap-2 mb-2 flex-wrap flex-1"> <div className="flex gap-2 mb-2 flex-wrap flex-1">
{links && {links.map((linkId) => (
links.length > 0 && <ProjectLinkDetail key={linkId} linkId={linkId} linkOperations={linkOperations} />
links.map((linkId) => <ProjectLinkDetail key={linkId} linkId={linkId} linkOperations={linkOperations} />)} ))}
</div> </div>
</ContentOverflowWrapper> </ContentOverflowWrapper>
</div> </div>

View file

@ -11,8 +11,7 @@ import { LayersIcon } from "@plane/ui";
import { ContentOverflowWrapper } from "@/components/core/content-overflow-HOC"; import { ContentOverflowWrapper } from "@/components/core/content-overflow-HOC";
import { useProject } from "@/hooks/store"; import { useProject } from "@/hooks/store";
import { WorkspaceService } from "@/plane-web/services"; import { WorkspaceService } from "@/plane-web/services";
import { EmptyWorkspace } from "../empty-states"; import { NoProjectsEmptyState, RecentsEmptyState } from "../empty-states";
import { RecentsEmptyState } from "../empty-states/recents";
import { EWidgetKeys, WidgetLoader } from "../loaders"; import { EWidgetKeys, WidgetLoader } from "../loaders";
import { FiltersDropdown } from "./filters"; import { FiltersDropdown } from "./filters";
import { RecentIssue } from "./issue"; import { RecentIssue } from "./issue";
@ -34,6 +33,7 @@ export const RecentActivityWidget: React.FC<THomeWidgetProps> = observer((props)
const [filter, setFilter] = useState<TRecentActivityFilterKeys>(filters[0].name); const [filter, setFilter] = useState<TRecentActivityFilterKeys>(filters[0].name);
// ref // ref
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
// store hooks
const { joinedProjectIds, loader } = useProject(); const { joinedProjectIds, loader } = useProject();
const { data: recents, isLoading } = useSWR( const { data: recents, isLoading } = useSWR(
@ -65,7 +65,8 @@ export const RecentActivityWidget: React.FC<THomeWidgetProps> = observer((props)
} }
}; };
if (!loader && joinedProjectIds?.length === 0) return <EmptyWorkspace />; if (!loader && joinedProjectIds?.length === 0) return <NoProjectsEmptyState />;
if (!isLoading && recents?.length === 0) if (!isLoading && recents?.length === 0)
return ( return (
<div ref={ref} className="max-h-[500px] overflow-y-scroll"> <div ref={ref} className="max-h-[500px] overflow-y-scroll">
@ -94,10 +95,9 @@ export const RecentActivityWidget: React.FC<THomeWidgetProps> = observer((props)
<div className="min-h-[250px] flex flex-col"> <div className="min-h-[250px] flex flex-col">
{isLoading && <WidgetLoader widgetKey={WIDGET_KEY} />} {isLoading && <WidgetLoader widgetKey={WIDGET_KEY} />}
{!isLoading && {!isLoading &&
recents?.length > 0 &&
recents recents
.filter((recent: TActivityEntityData) => recent.entity_data) ?.filter((recent) => recent.entity_data)
.map((activity: TActivityEntityData) => <div key={activity.id}>{resolveRecent(activity)}</div>)} .map((activity) => <div key={activity.id}>{resolveRecent(activity)}</div>)}
</div> </div>
</ContentOverflowWrapper> </ContentOverflowWrapper>
); );

View file

@ -84,7 +84,7 @@ export const StickiesList = observer((props: TProps) => {
if (loader === "loaded" && workspaceStickyIds.length === 0) { if (loader === "loaded" && workspaceStickyIds.length === 0) {
return ( return (
<div className="size-full grid place-items-center"> <div className="size-full grid place-items-center px-2">
{isStickiesPage || searchQuery ? ( {isStickiesPage || searchQuery ? (
<EmptyState <EmptyState
type={searchQuery ? EmptyStateType.STICKIES_SEARCH : EmptyStateType.STICKIES} type={searchQuery ? EmptyStateType.STICKIES_SEARCH : EmptyStateType.STICKIES}

View file

@ -922,7 +922,7 @@ const emptyStateDetails: Record<EmptyStateType, EmptyStateDetails> = {
key: EmptyStateType.STICKIES, key: EmptyStateType.STICKIES,
title: "Stickies are quick notes and to-dos you take down on the fly.", title: "Stickies are quick notes and to-dos you take down on the fly.",
description: description:
"Capture your thoughts and ideas effortlessly by creating stickies that you can access anytime and from anywhere.", "Capture ideas, ahas, brainwaves, light-bulb moments without scrambling for a pen and paper, hunting for the recorder app on your phone, or firing up a notes app only to forget all about it later. Keep them all right next to your work so you can easily come back, build them up, or well, discard them.",
path: "/empty-state/stickies/stickies", path: "/empty-state/stickies/stickies",
primaryButton: { primaryButton: {
icon: <Plus className="size-4" />, icon: <Plus className="size-4" />,
@ -945,8 +945,8 @@ const emptyStateDetails: Record<EmptyStateType, EmptyStateDetails> = {
}, },
[EmptyStateType.HOME_WIDGETS]: { [EmptyStateType.HOME_WIDGETS]: {
key: EmptyStateType.HOME_WIDGETS, key: EmptyStateType.HOME_WIDGETS,
title: "It's Quiet Without Widgets, Turn Them On", title: "So much to add, yet such empty",
description: "It looks like all your widgets are turned off. Enable them\nnow to enhance your experience!", description: "You have turned off all your widgets. Turn some or\nall of them back on to see delightful things.",
path: "/empty-state/dashboard/widgets", path: "/empty-state/dashboard/widgets",
primaryButton: { primaryButton: {
icon: <Shapes className="size-4" />, icon: <Shapes className="size-4" />,

View file

@ -16,6 +16,7 @@ import {
TSearchResponse, TSearchResponse,
TSearchEntityRequestPayload, TSearchEntityRequestPayload,
TWidgetEntityData, TWidgetEntityData,
TActivityEntityData,
} from "@plane/types"; } from "@plane/types";
import { APIService } from "@/services/api.service"; import { APIService } from "@/services/api.service";
// helpers // helpers
@ -329,7 +330,7 @@ export class WorkspaceService extends APIService {
} }
// recents // recents
async fetchWorkspaceRecents(workspaceSlug: string, entity_name?: string) { async fetchWorkspaceRecents(workspaceSlug: string, entity_name?: string): Promise<TActivityEntityData[]> {
return this.get(`/api/workspaces/${workspaceSlug}/recent-visits/`, { return this.get(`/api/workspaces/${workspaceSlug}/recent-visits/`, {
params: { params: {
entity_name, entity_name,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 83 KiB

Before After
Before After