[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:
parent
0b53912295
commit
d08c03f557
20 changed files with 157 additions and 162 deletions
|
|
@ -105,14 +105,14 @@ ul[data-type="taskList"] li > div {
|
|||
}
|
||||
|
||||
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;
|
||||
border-radius: 2px;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.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"] {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { getEditorFileHandlers } from "@/helpers/editor.helper";
|
|||
// plane web hooks
|
||||
import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging";
|
||||
import { useFileSize } from "@/plane-web/hooks/use-file-size";
|
||||
import { Toolbar } from "./toolbar";
|
||||
import { StickyEditorToolbar } from "./toolbar";
|
||||
|
||||
interface StickyEditorWrapperProps
|
||||
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"
|
||||
)}
|
||||
>
|
||||
<Toolbar
|
||||
<StickyEditorToolbar
|
||||
executeCommand={(item) => {
|
||||
// TODO: update this while toolbar homogenization
|
||||
// @ts-expect-error type mismatch here
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ type Props = {
|
|||
|
||||
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;
|
||||
|
||||
// State to manage active states of toolbar items
|
||||
|
|
@ -69,7 +69,11 @@ export const Toolbar: React.FC<Props> = (props) => {
|
|||
</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" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
|
@ -95,7 +99,7 @@ export const Toolbar: React.FC<Props> = (props) => {
|
|||
type="button"
|
||||
onClick={() => executeCommand(item)}
|
||||
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>
|
||||
}
|
||||
>
|
||||
<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" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export const HOME_WIDGETS_LIST: {
|
|||
quick_links: {
|
||||
component: DashboardQuickLinks,
|
||||
fullWidth: false,
|
||||
title: "Quick links",
|
||||
title: "Quicklinks",
|
||||
},
|
||||
recents: {
|
||||
component: RecentActivityWidget,
|
||||
|
|
|
|||
|
|
@ -1 +1,4 @@
|
|||
export * from "./root";
|
||||
export * from "./links";
|
||||
export * from "./no-projects";
|
||||
export * from "./recents";
|
||||
export * from "./stickies";
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
import { Link2 } from "lucide-react";
|
||||
|
||||
export const LinksEmptyState = () => (
|
||||
<div className="min-h-[110px] flex w-full justify-center py-6 bg-custom-border-100 rounded">
|
||||
<div className="m-auto flex gap-2">
|
||||
<Link2 size={30} className="text-custom-text-400/40 -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 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="flex-shrink-0 size-[30px] grid place-items-center">
|
||||
<Link2 className="size-6 -rotate-45" />
|
||||
</div>
|
||||
);
|
||||
<p className="text-sm text-center font-medium">Save links to work things that you{"'"}d like handy.</p>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import React from "react";
|
|||
import Link from "next/link";
|
||||
import { useParams } from "next/navigation";
|
||||
import { Briefcase, Hotel, Users } from "lucide-react";
|
||||
// plane ui
|
||||
import { Avatar } from "@plane/ui";
|
||||
// helpers
|
||||
import { getFileURL } from "@/helpers/file.helper";
|
||||
// hooks
|
||||
|
|
@ -9,7 +11,7 @@ import { useCommandPalette, useEventTracker, useUser, useUserPermissions } from
|
|||
// plane web constants
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants";
|
||||
|
||||
export const EmptyWorkspace = () => {
|
||||
export const NoProjectsEmptyState = () => {
|
||||
// navigation
|
||||
const { workspaceSlug } = useParams();
|
||||
// store hooks
|
||||
|
|
@ -26,11 +28,11 @@ export const EmptyWorkspace = () => {
|
|||
const EMPTY_STATE_DATA = [
|
||||
{
|
||||
id: "create-project",
|
||||
title: "Create a project",
|
||||
description: "Create your first project now to get started",
|
||||
icon: <Briefcase className="w-[40px] h-[40px] text-custom-primary-100" />,
|
||||
title: "Create a project.",
|
||||
description: "Most things start with a project in Plane.",
|
||||
icon: <Briefcase className="size-12 text-custom-primary-100" />,
|
||||
cta: {
|
||||
text: "Create Project",
|
||||
text: "Get started",
|
||||
onClick: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
if (!canCreateProject) return;
|
||||
e.preventDefault();
|
||||
|
|
@ -42,66 +44,56 @@ export const EmptyWorkspace = () => {
|
|||
},
|
||||
{
|
||||
id: "invite-team",
|
||||
title: "Invite your team",
|
||||
description: "The sub text will be of two lines and that will be placed.",
|
||||
icon: <Users className="w-[40px] h-[40px] text-custom-primary-100" />,
|
||||
title: "Invite your team.",
|
||||
description: "Build, ship, and manage with coworkers.",
|
||||
icon: <Users className="size-12 text-custom-primary-100" />,
|
||||
cta: {
|
||||
text: "Invite now",
|
||||
text: "Get them in",
|
||||
link: `/${workspaceSlug}/settings/members`,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "configure-workspace",
|
||||
title: "Configure your workspace",
|
||||
description: "The sub text will be of two lines and that will be placed.",
|
||||
icon: <Hotel className="w-[40px] h-[40px] text-custom-primary-100" />,
|
||||
title: "Set up your workspace.",
|
||||
description: "Turn features on or off or go beyond that.",
|
||||
icon: <Hotel className="size-12 text-custom-primary-100" />,
|
||||
cta: {
|
||||
text: "Configure workspace",
|
||||
text: "Configure this workspace",
|
||||
link: "settings",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "personalize-account",
|
||||
title: "Personalize your account",
|
||||
description: "The sub text will be of two lines and that will be placed.",
|
||||
icon:
|
||||
currentUser?.avatar_url && currentUser?.avatar_url.trim() !== "" ? (
|
||||
<Link href={`/${workspaceSlug}/profile/${currentUser?.id}`}>
|
||||
<span className="relative flex h-6 w-6 items-center justify-center rounded-full p-4 capitalize text-white">
|
||||
<img
|
||||
src={getFileURL(currentUser?.avatar_url)}
|
||||
className="absolute left-0 top-0 h-full w-full rounded-full object-cover"
|
||||
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>
|
||||
),
|
||||
title: "Make Plane yours.",
|
||||
description: "Choose your picture, colors, and more.",
|
||||
icon: (
|
||||
<Avatar
|
||||
src={getFileURL(currentUser?.avatar_url ?? "")}
|
||||
name={currentUser?.display_name}
|
||||
size={48}
|
||||
className="text-xl"
|
||||
showTooltip={false}
|
||||
/>
|
||||
),
|
||||
cta: {
|
||||
text: "Personalize account",
|
||||
text: "Personalize now",
|
||||
link: "/profile",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
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) => (
|
||||
<div
|
||||
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>
|
||||
</div>
|
||||
<h3 className="text-lg 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>
|
||||
|
||||
<h3 className="text-base font-medium text-custom-text-100 mb-2">{item.title}</h3>
|
||||
<p className="text-sm text-custom-text-300 mb-2">{item.description}</p>
|
||||
{item.cta.link ? (
|
||||
<Link
|
||||
href={item.cta.link}
|
||||
|
|
@ -111,6 +103,7 @@ export const EmptyWorkspace = () => {
|
|||
</Link>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
className="text-custom-primary-100 hover:text-custom-primary-200 text-sm font-medium"
|
||||
onClick={item.cta.onClick}
|
||||
>
|
||||
|
|
@ -1,38 +1,41 @@
|
|||
import { Briefcase, FileText, History } from "lucide-react";
|
||||
// plane ui
|
||||
import { LayersIcon } from "@plane/ui";
|
||||
|
||||
const getDisplayContent = (type: string) => {
|
||||
switch (type) {
|
||||
case "project":
|
||||
return {
|
||||
icon: Briefcase,
|
||||
text: "Projects you go into or have assigned work in will show up here.",
|
||||
};
|
||||
case "page":
|
||||
return {
|
||||
icon: FileText,
|
||||
text: "Create, see, or change something on pages you have access to and see them here.",
|
||||
};
|
||||
case "issue":
|
||||
return {
|
||||
icon: LayersIcon,
|
||||
text: "Let's see some issues to see them show up here.",
|
||||
};
|
||||
default:
|
||||
return {
|
||||
icon: History,
|
||||
text: "Whatever you see and act on in Plane will show up here.",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export const RecentsEmptyState = ({ type }: { type: string }) => {
|
||||
const getDisplayContent = () => {
|
||||
switch (type) {
|
||||
case "project":
|
||||
return {
|
||||
icon: <Briefcase size={30} className="text-custom-text-400/40" />,
|
||||
text: "Your recent projects will appear here once you visit one.",
|
||||
};
|
||||
case "page":
|
||||
return {
|
||||
icon: <FileText size={30} className="text-custom-text-400/40" />,
|
||||
text: "Your recent pages will appear here once you visit one.",
|
||||
};
|
||||
case "issue":
|
||||
return {
|
||||
icon: <LayersIcon className="text-custom-text-400/40 w-[30px] h-[30px]" />,
|
||||
text: "Your recent issues will appear here once you visit one.",
|
||||
};
|
||||
default:
|
||||
return {
|
||||
icon: <History size={30} className="text-custom-text-400/40" />,
|
||||
text: "You don’t have any recent items yet.",
|
||||
};
|
||||
}
|
||||
};
|
||||
const { icon, text } = getDisplayContent();
|
||||
const displayContent = getDisplayContent(type);
|
||||
|
||||
return (
|
||||
<div className="min-h-[120px] flex w-full justify-center py-6 bg-custom-border-100 rounded">
|
||||
<div className="m-auto flex gap-2">
|
||||
{icon} <div className="text-custom-text-400 text-sm text-center my-auto">{text}</div>
|
||||
<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="flex-shrink-0 size-[30px] grid place-items-center">
|
||||
<displayContent.icon className="size-6" />
|
||||
</div>
|
||||
<p className="text-sm text-center font-medium">{displayContent.text}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@
|
|||
import { RecentStickyIcon } from "@plane/ui";
|
||||
|
||||
export const StickiesEmptyState = () => (
|
||||
<div className="min-h-[110px] flex w-full justify-center py-6 bg-custom-border-100 rounded">
|
||||
<div className="m-auto flex gap-2">
|
||||
<RecentStickyIcon className="h-[30px] w-[30px] text-custom-text-400/40" />
|
||||
<div className="text-custom-text-400 text-sm text-center my-auto">
|
||||
No stickies yet. Add one to start making quick notes.
|
||||
</div>
|
||||
<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="flex-shrink-0 size-[30px] grid place-items-center">
|
||||
<RecentStickyIcon className="size-6" />
|
||||
</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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
<PlusIcon className="h-4 w-4 stroke-2 text-custom-text-350" />
|
||||
</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>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -66,9 +66,7 @@ export const LinkCreateUpdateModal: FC<TLinkCreateEditModal> = observer((props)
|
|||
<ModalCore isOpen={isModalOpen} handleClose={onClose}>
|
||||
<form onSubmit={handleSubmit(handleFormSubmit)}>
|
||||
<div className="space-y-5 p-5">
|
||||
<h3 className="text-xl font-medium text-custom-text-200">
|
||||
{preloadedData?.id ? "Update" : "Add"} quick link
|
||||
</h3>
|
||||
<h3 className="text-xl font-medium text-custom-text-200">{preloadedData?.id ? "Update" : "Add"} quicklink</h3>
|
||||
<div className="mt-2 space-y-3">
|
||||
<div>
|
||||
<label htmlFor="url" className="mb-2 text-custom-text-200 text-base font-medium">
|
||||
|
|
@ -124,7 +122,7 @@ export const LinkCreateUpdateModal: FC<TLinkCreateEditModal> = observer((props)
|
|||
Cancel
|
||||
</Button>
|
||||
<Button variant="primary" size="sm" type="submit" loading={isSubmitting}>
|
||||
{preloadedData?.id ? (isSubmitting ? "Updating" : "Update") : isSubmitting ? "Adding" : "Add"} quick link
|
||||
{preloadedData?.id ? (isSubmitting ? "Updating" : "Update") : isSubmitting ? "Adding" : "Add"} quicklink
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
"use client";
|
||||
|
||||
import { FC } from "react";
|
||||
// hooks
|
||||
// ui
|
||||
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";
|
||||
// helpers
|
||||
// plane utils
|
||||
import { cn, copyTextToClipboard } from "@plane/utils";
|
||||
// helpers
|
||||
import { calculateTimeAgo } from "@/helpers/date-time.helper";
|
||||
// hooks
|
||||
import { useHome } from "@/hooks/store/use-home";
|
||||
// types
|
||||
import { TLinkOperations } from "./use-links";
|
||||
|
||||
export type TProjectLinkDetail = {
|
||||
|
|
@ -75,54 +77,47 @@ export const ProjectLinkDetail: FC<TProjectLinkDetail> = observer((props) => {
|
|||
return (
|
||||
<div
|
||||
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">
|
||||
<Link2 className="h-4 w-4 stroke-2 text-custom-text-350 -rotate-45" />
|
||||
<div className="flex-shrink-0 size-8 rounded p-2 bg-custom-background-80 grid place-items-center">
|
||||
<Link2 className="size-4 stroke-2 text-custom-text-350 -rotate-45" />
|
||||
</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-xs font-medium text-custom-text-400">{calculateTimeAgo(linkDetail.created_at)}</div>
|
||||
</div>
|
||||
|
||||
<CustomMenu
|
||||
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) => (
|
||||
<CustomMenu.MenuItem
|
||||
key={item.key}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
item.action();
|
||||
}}
|
||||
className={cn("flex items-center gap-2 w-full ", {
|
||||
"text-custom-text-400": item.disabled,
|
||||
})}
|
||||
disabled={item.disabled}
|
||||
>
|
||||
{item.icon && <item.icon className={cn("h-3 w-3", item.iconClassName)} />}
|
||||
<div>
|
||||
<h5>{item.title}</h5>
|
||||
{item.description && (
|
||||
<p
|
||||
className={cn("text-custom-text-300 whitespace-pre-line", {
|
||||
"text-custom-text-400": item.disabled,
|
||||
})}
|
||||
>
|
||||
{item.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
))}
|
||||
</CustomMenu>
|
||||
<div className="hidden group-hover:block">
|
||||
<CustomMenu placement="bottom-end" menuItemsClassName="z-20" closeOnSelect verticalEllipsis>
|
||||
{MENU_ITEMS.map((item) => (
|
||||
<CustomMenu.MenuItem
|
||||
key={item.key}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
item.action();
|
||||
}}
|
||||
className={cn("flex items-center gap-2 w-full ", {
|
||||
"text-custom-text-400": item.disabled,
|
||||
})}
|
||||
disabled={item.disabled}
|
||||
>
|
||||
{item.icon && <item.icon className={cn("h-3 w-3", item.iconClassName)} />}
|
||||
<div>
|
||||
<h5>{item.title}</h5>
|
||||
{item.description && (
|
||||
<p
|
||||
className={cn("text-custom-text-300 whitespace-pre-line", {
|
||||
"text-custom-text-400": item.disabled,
|
||||
})}
|
||||
>
|
||||
{item.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
))}
|
||||
</CustomMenu>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -38,9 +38,9 @@ export const ProjectLinkList: FC<TProjectLinkList> = observer((props) => {
|
|||
buttonClassName="bg-custom-background-90/20"
|
||||
>
|
||||
<div className="flex gap-2 mb-2 flex-wrap flex-1">
|
||||
{links &&
|
||||
links.length > 0 &&
|
||||
links.map((linkId) => <ProjectLinkDetail key={linkId} linkId={linkId} linkOperations={linkOperations} />)}
|
||||
{links.map((linkId) => (
|
||||
<ProjectLinkDetail key={linkId} linkId={linkId} linkOperations={linkOperations} />
|
||||
))}
|
||||
</div>
|
||||
</ContentOverflowWrapper>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -34,14 +34,14 @@ export const DashboardQuickLinks = observer((props: THomeWidgetProps) => {
|
|||
/>
|
||||
<div className="mb-2">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="text-base font-semibold text-custom-text-350">Quick links</div>
|
||||
<div className="text-base font-semibold text-custom-text-350">Quicklinks</div>
|
||||
<button
|
||||
onClick={() => {
|
||||
toggleLinkModal(true);
|
||||
}}
|
||||
className="flex gap-1 text-sm font-medium text-custom-primary-100 my-auto"
|
||||
>
|
||||
<Plus className="size-4 my-auto" /> <span>Add quick link</span>
|
||||
<Plus className="size-4 my-auto" /> <span>Add quicklink</span>
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex flex-wrap w-full">
|
||||
|
|
|
|||
|
|
@ -11,8 +11,7 @@ import { LayersIcon } from "@plane/ui";
|
|||
import { ContentOverflowWrapper } from "@/components/core/content-overflow-HOC";
|
||||
import { useProject } from "@/hooks/store";
|
||||
import { WorkspaceService } from "@/plane-web/services";
|
||||
import { EmptyWorkspace } from "../empty-states";
|
||||
import { RecentsEmptyState } from "../empty-states/recents";
|
||||
import { NoProjectsEmptyState, RecentsEmptyState } from "../empty-states";
|
||||
import { EWidgetKeys, WidgetLoader } from "../loaders";
|
||||
import { FiltersDropdown } from "./filters";
|
||||
import { RecentIssue } from "./issue";
|
||||
|
|
@ -34,6 +33,7 @@ export const RecentActivityWidget: React.FC<THomeWidgetProps> = observer((props)
|
|||
const [filter, setFilter] = useState<TRecentActivityFilterKeys>(filters[0].name);
|
||||
// ref
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
// store hooks
|
||||
const { joinedProjectIds, loader } = useProject();
|
||||
|
||||
const { data: recents, isLoading } = useSWR(
|
||||
|
|
@ -65,10 +65,11 @@ 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)
|
||||
return (
|
||||
<div ref={ref} className=" max-h-[500px] overflow-y-scroll">
|
||||
<div ref={ref} className="max-h-[500px] overflow-y-scroll">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="text-base font-semibold text-custom-text-350">Recents</div>
|
||||
<FiltersDropdown filters={filters} activeFilter={filter} setActiveFilter={setFilter} />
|
||||
|
|
@ -94,10 +95,9 @@ export const RecentActivityWidget: React.FC<THomeWidgetProps> = observer((props)
|
|||
<div className="min-h-[250px] flex flex-col">
|
||||
{isLoading && <WidgetLoader widgetKey={WIDGET_KEY} />}
|
||||
{!isLoading &&
|
||||
recents?.length > 0 &&
|
||||
recents
|
||||
.filter((recent: TActivityEntityData) => recent.entity_data)
|
||||
.map((activity: TActivityEntityData) => <div key={activity.id}>{resolveRecent(activity)}</div>)}
|
||||
?.filter((recent) => recent.entity_data)
|
||||
.map((activity) => <div key={activity.id}>{resolveRecent(activity)}</div>)}
|
||||
</div>
|
||||
</ContentOverflowWrapper>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ export const StickiesList = observer((props: TProps) => {
|
|||
|
||||
if (loader === "loaded" && workspaceStickyIds.length === 0) {
|
||||
return (
|
||||
<div className="size-full grid place-items-center">
|
||||
<div className="size-full grid place-items-center px-2">
|
||||
{isStickiesPage || searchQuery ? (
|
||||
<EmptyState
|
||||
type={searchQuery ? EmptyStateType.STICKIES_SEARCH : EmptyStateType.STICKIES}
|
||||
|
|
|
|||
|
|
@ -922,7 +922,7 @@ const emptyStateDetails: Record<EmptyStateType, EmptyStateDetails> = {
|
|||
key: EmptyStateType.STICKIES,
|
||||
title: "Stickies are quick notes and to-dos you take down on the fly.",
|
||||
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",
|
||||
primaryButton: {
|
||||
icon: <Plus className="size-4" />,
|
||||
|
|
@ -945,8 +945,8 @@ const emptyStateDetails: Record<EmptyStateType, EmptyStateDetails> = {
|
|||
},
|
||||
[EmptyStateType.HOME_WIDGETS]: {
|
||||
key: EmptyStateType.HOME_WIDGETS,
|
||||
title: "It's Quiet Without Widgets, Turn Them On",
|
||||
description: "It looks like all your widgets are turned off. Enable them\nnow to enhance your experience!",
|
||||
title: "So much to add, yet such empty",
|
||||
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",
|
||||
primaryButton: {
|
||||
icon: <Shapes className="size-4" />,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import {
|
|||
TSearchResponse,
|
||||
TSearchEntityRequestPayload,
|
||||
TWidgetEntityData,
|
||||
TActivityEntityData,
|
||||
} from "@plane/types";
|
||||
import { APIService } from "@/services/api.service";
|
||||
// helpers
|
||||
|
|
@ -282,7 +283,7 @@ export class WorkspaceService extends APIService {
|
|||
});
|
||||
}
|
||||
|
||||
// quick links
|
||||
// quicklinks
|
||||
async fetchWorkspaceLinks(workspaceSlug: string): Promise<TLink[]> {
|
||||
return this.get(`/api/workspaces/${workspaceSlug}/quick-links/`)
|
||||
.then((response) => response?.data)
|
||||
|
|
@ -329,7 +330,7 @@ export class WorkspaceService extends APIService {
|
|||
}
|
||||
|
||||
// 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/`, {
|
||||
params: {
|
||||
entity_name,
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 76 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 83 KiB |
Loading…
Add table
Add a link
Reference in a new issue