[WEB-504] chore: command k and issue relation modal empty state (#3955)
* chore: empty state asset updated * chore: empty state asset updated * chore: empty state config file updated * chore: notification empty state updated * chore: command-k, bulk delete and issue relation modal empty state updated * chore: code refactor * chore: code refactor
@ -4,9 +4,19 @@ import { observer } from "mobx-react-lite";
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { Dialog, Transition } from "@headlessui/react";
|
import { Dialog, Transition } from "@headlessui/react";
|
||||||
|
// icons
|
||||||
import { FolderPlus, Search, Settings } from "lucide-react";
|
import { FolderPlus, Search, Settings } from "lucide-react";
|
||||||
// hooks
|
// hooks
|
||||||
|
import { useApplication, useEventTracker, useProject } from "hooks/store";
|
||||||
|
import { usePlatformOS } from "hooks/use-platform-os";
|
||||||
|
import useDebounce from "hooks/use-debounce";
|
||||||
|
// services
|
||||||
|
import { IssueService } from "services/issue";
|
||||||
|
import { WorkspaceService } from "services/workspace.service";
|
||||||
|
// ui
|
||||||
import { LayersIcon, Loader, ToggleSwitch, Tooltip } from "@plane/ui";
|
import { LayersIcon, Loader, ToggleSwitch, Tooltip } from "@plane/ui";
|
||||||
|
// components
|
||||||
|
import { EmptyState } from "components/empty-state";
|
||||||
import {
|
import {
|
||||||
CommandPaletteThemeActions,
|
CommandPaletteThemeActions,
|
||||||
ChangeIssueAssignee,
|
ChangeIssueAssignee,
|
||||||
@ -18,18 +28,13 @@ import {
|
|||||||
CommandPaletteWorkspaceSettingsActions,
|
CommandPaletteWorkspaceSettingsActions,
|
||||||
CommandPaletteSearchResults,
|
CommandPaletteSearchResults,
|
||||||
} from "components/command-palette";
|
} from "components/command-palette";
|
||||||
import { ISSUE_DETAILS } from "constants/fetch-keys";
|
|
||||||
import { useApplication, useEventTracker, useProject } from "hooks/store";
|
|
||||||
import { usePlatformOS } from "hooks/use-platform-os";
|
|
||||||
// services
|
|
||||||
import useDebounce from "hooks/use-debounce";
|
|
||||||
import { IssueService } from "services/issue";
|
|
||||||
import { WorkspaceService } from "services/workspace.service";
|
|
||||||
// types
|
// types
|
||||||
import { IWorkspaceSearchResults } from "@plane/types";
|
import { IWorkspaceSearchResults } from "@plane/types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
|
// constants
|
||||||
|
import { EmptyStateType } from "constants/empty-state";
|
||||||
|
import { ISSUE_DETAILS } from "constants/fetch-keys";
|
||||||
|
|
||||||
// services
|
|
||||||
const workspaceService = new WorkspaceService();
|
const workspaceService = new WorkspaceService();
|
||||||
const issueService = new IssueService();
|
const issueService = new IssueService();
|
||||||
|
|
||||||
@ -244,7 +249,9 @@ export const CommandModal: React.FC = observer(() => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{!isLoading && resultsCount === 0 && searchTerm !== "" && debouncedSearchTerm !== "" && (
|
{!isLoading && resultsCount === 0 && searchTerm !== "" && debouncedSearchTerm !== "" && (
|
||||||
<div className="my-4 text-center text-sm text-custom-text-200">No results found.</div>
|
<div className="flex flex-col items-center justify-center px-3 py-8 text-center">
|
||||||
|
<EmptyState type={EmptyStateType.COMMAND_K_SEARCH_EMPTY_STATE} layout="screen-simple" />
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{(isLoading || isSearching) && (
|
{(isLoading || isSearching) && (
|
||||||
|
@ -5,22 +5,22 @@ import { SubmitHandler, useForm } from "react-hook-form";
|
|||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { Combobox, Dialog, Transition } from "@headlessui/react";
|
import { Combobox, Dialog, Transition } from "@headlessui/react";
|
||||||
// services
|
// services
|
||||||
import { Search } from "lucide-react";
|
|
||||||
import { Button, LayersIcon, TOAST_TYPE, setToast } from "@plane/ui";
|
|
||||||
|
|
||||||
import { PROJECT_ISSUES_LIST } from "constants/fetch-keys";
|
|
||||||
import { EIssuesStoreType } from "constants/issue";
|
|
||||||
import { useIssues, useProject } from "hooks/store";
|
|
||||||
import { IssueService } from "services/issue";
|
import { IssueService } from "services/issue";
|
||||||
// ui
|
// ui
|
||||||
|
import { Button, TOAST_TYPE, setToast } from "@plane/ui";
|
||||||
// icons
|
// icons
|
||||||
|
import { Search } from "lucide-react";
|
||||||
// types
|
// types
|
||||||
import { IUser, TIssue } from "@plane/types";
|
import { IUser, TIssue } from "@plane/types";
|
||||||
// fetch keys
|
|
||||||
// store hooks
|
// store hooks
|
||||||
|
import { useIssues, useProject } from "hooks/store";
|
||||||
// components
|
// components
|
||||||
import { BulkDeleteIssuesModalItem } from "./bulk-delete-issues-modal-item";
|
import { BulkDeleteIssuesModalItem } from "./bulk-delete-issues-modal-item";
|
||||||
|
import { EmptyState } from "components/empty-state";
|
||||||
// constants
|
// constants
|
||||||
|
import { PROJECT_ISSUES_LIST } from "constants/fetch-keys";
|
||||||
|
import { EIssuesStoreType } from "constants/issue";
|
||||||
|
import { EmptyStateType } from "constants/empty-state";
|
||||||
|
|
||||||
type FormInput = {
|
type FormInput = {
|
||||||
delete_issue_ids: string[];
|
delete_issue_ids: string[];
|
||||||
@ -178,12 +178,15 @@ export const BulkDeleteIssuesModal: React.FC<Props> = observer((props) => {
|
|||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col items-center justify-center gap-4 px-3 py-8 text-center">
|
<div className="flex flex-col items-center justify-center px-3 py-8 text-center">
|
||||||
<LayersIcon height="56" width="56" />
|
<EmptyState
|
||||||
<h3 className="text-custom-text-200">
|
type={
|
||||||
No issues found. Create a new issue with{" "}
|
query === ""
|
||||||
<pre className="inline rounded bg-custom-background-80 px-2 py-1">C</pre>.
|
? EmptyStateType.ISSUE_RELATION_EMPTY_STATE
|
||||||
</h3>
|
: EmptyStateType.ISSUE_RELATION_SEARCH_EMPTY_STATE
|
||||||
|
}
|
||||||
|
layout="screen-simple"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Combobox.Options>
|
</Combobox.Options>
|
||||||
|
@ -2,12 +2,14 @@ import React, { useEffect, useState } from "react";
|
|||||||
import { Combobox, Dialog, Transition } from "@headlessui/react";
|
import { Combobox, Dialog, Transition } from "@headlessui/react";
|
||||||
import { Rocket, Search, X } from "lucide-react";
|
import { Rocket, Search, X } from "lucide-react";
|
||||||
// services
|
// services
|
||||||
import { Button, LayersIcon, Loader, ToggleSwitch, Tooltip, TOAST_TYPE, setToast } from "@plane/ui";
|
import { ProjectService } from "services/project";
|
||||||
|
// hooks
|
||||||
import useDebounce from "hooks/use-debounce";
|
import useDebounce from "hooks/use-debounce";
|
||||||
import { usePlatformOS } from "hooks/use-platform-os";
|
import { usePlatformOS } from "hooks/use-platform-os";
|
||||||
import { ProjectService } from "services/project";
|
// components
|
||||||
|
import { IssueSearchModalEmptyState } from "./issue-search-modal-empty-state";
|
||||||
// ui
|
// ui
|
||||||
|
import { Button, Loader, ToggleSwitch, Tooltip, TOAST_TYPE, setToast } from "@plane/ui";
|
||||||
// types
|
// types
|
||||||
import { ISearchIssueResponse, TProjectIssuesSearchParams } from "@plane/types";
|
import { ISearchIssueResponse, TProjectIssuesSearchParams } from "@plane/types";
|
||||||
|
|
||||||
@ -192,15 +194,12 @@ export const ExistingIssuesListModal: React.FC<Props> = (props) => {
|
|||||||
</h5>
|
</h5>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!isSearching && issues.length === 0 && searchTerm !== "" && debouncedSearchTerm !== "" && (
|
<IssueSearchModalEmptyState
|
||||||
<div className="flex flex-col items-center justify-center gap-4 px-3 py-8 text-center">
|
debouncedSearchTerm={debouncedSearchTerm}
|
||||||
<LayersIcon height="52" width="52" />
|
isSearching={isSearching}
|
||||||
<h3 className="text-custom-text-200">
|
issues={issues}
|
||||||
No issues found. Create a new issue with{" "}
|
searchTerm={searchTerm}
|
||||||
<pre className="inline rounded bg-custom-background-80 px-2 py-1 text-sm">C</pre>.
|
/>
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isSearching ? (
|
{isSearching ? (
|
||||||
<Loader className="space-y-3 p-3">
|
<Loader className="space-y-3 p-3">
|
||||||
|
@ -4,3 +4,4 @@ export * from "./gpt-assistant-popover";
|
|||||||
export * from "./link-modal";
|
export * from "./link-modal";
|
||||||
export * from "./user-image-upload-modal";
|
export * from "./user-image-upload-modal";
|
||||||
export * from "./workspace-image-upload-modal";
|
export * from "./workspace-image-upload-modal";
|
||||||
|
export * from "./issue-search-modal-empty-state";
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
import React from "react";
|
||||||
|
// components
|
||||||
|
import { EmptyState } from "components/empty-state";
|
||||||
|
// types
|
||||||
|
import { ISearchIssueResponse } from "@plane/types";
|
||||||
|
// constants
|
||||||
|
import { EmptyStateType } from "constants/empty-state";
|
||||||
|
|
||||||
|
interface EmptyStateProps {
|
||||||
|
issues: ISearchIssueResponse[];
|
||||||
|
searchTerm: string;
|
||||||
|
debouncedSearchTerm: string;
|
||||||
|
isSearching: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const IssueSearchModalEmptyState: React.FC<EmptyStateProps> = ({
|
||||||
|
issues,
|
||||||
|
searchTerm,
|
||||||
|
debouncedSearchTerm,
|
||||||
|
isSearching,
|
||||||
|
}) => {
|
||||||
|
const renderEmptyState = (type: EmptyStateType) => (
|
||||||
|
<div className="flex flex-col items-center justify-center px-3 py-8 text-center">
|
||||||
|
<EmptyState type={type} layout="screen-simple" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const emptyState =
|
||||||
|
issues.length === 0 && searchTerm !== "" && debouncedSearchTerm !== "" && !isSearching
|
||||||
|
? renderEmptyState(EmptyStateType.ISSUE_RELATION_SEARCH_EMPTY_STATE)
|
||||||
|
: issues.length === 0
|
||||||
|
? renderEmptyState(EmptyStateType.ISSUE_RELATION_EMPTY_STATE)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return emptyState;
|
||||||
|
};
|
@ -16,7 +16,7 @@ import { cn } from "helpers/common.helper";
|
|||||||
export type EmptyStateProps = {
|
export type EmptyStateProps = {
|
||||||
type: EmptyStateType;
|
type: EmptyStateType;
|
||||||
size?: "sm" | "md" | "lg";
|
size?: "sm" | "md" | "lg";
|
||||||
layout?: "widget-simple" | "screen-detailed" | "screen-simple";
|
layout?: "screen-detailed" | "screen-simple";
|
||||||
additionalPath?: string;
|
additionalPath?: string;
|
||||||
primaryButtonOnClick?: () => void;
|
primaryButtonOnClick?: () => void;
|
||||||
primaryButtonLink?: string;
|
primaryButtonLink?: string;
|
||||||
@ -149,6 +149,28 @@ export const EmptyState: React.FC<EmptyStateProps> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{layout === "screen-simple" && (
|
||||||
|
<div className="text-center flex flex-col gap-2.5 items-center">
|
||||||
|
<div className="h-28 w-28">
|
||||||
|
<Image
|
||||||
|
src={resolvedEmptyStatePath}
|
||||||
|
alt={key || "button image"}
|
||||||
|
width={96}
|
||||||
|
height={96}
|
||||||
|
layout="responsive"
|
||||||
|
lazyBoundary="100%"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{description ? (
|
||||||
|
<>
|
||||||
|
<h3 className="text-lg font-medium text-custom-text-300 whitespace-pre-line">{title}</h3>
|
||||||
|
<p className="text-base font-medium text-custom-text-400 whitespace-pre-line">{description}</p>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<h3 className="text-sm font-medium text-custom-text-400 whitespace-pre-line">{title}</h3>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -2,15 +2,19 @@ import React, { useEffect, useState } from "react";
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { Combobox, Dialog, Transition } from "@headlessui/react";
|
import { Combobox, Dialog, Transition } from "@headlessui/react";
|
||||||
|
// hooks
|
||||||
|
import { useProject, useProjectState } from "hooks/store";
|
||||||
// icons
|
// icons
|
||||||
import { Search } from "lucide-react";
|
import { Search } from "lucide-react";
|
||||||
|
// components
|
||||||
|
import { EmptyState } from "components/empty-state";
|
||||||
// ui
|
// ui
|
||||||
import { Button, LayersIcon, TOAST_TYPE, setToast } from "@plane/ui";
|
import { Button, TOAST_TYPE, setToast } from "@plane/ui";
|
||||||
// fetch-keys
|
|
||||||
import { PROJECT_ISSUES_LIST } from "constants/fetch-keys";
|
|
||||||
import { useProject, useProjectState } from "hooks/store";
|
|
||||||
// services
|
// services
|
||||||
import { IssueService } from "services/issue";
|
import { IssueService } from "services/issue";
|
||||||
|
// constants
|
||||||
|
import { PROJECT_ISSUES_LIST } from "constants/fetch-keys";
|
||||||
|
import { EmptyStateType } from "constants/empty-state";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@ -158,12 +162,15 @@ export const SelectDuplicateInboxIssueModal: React.FC<Props> = (props) => {
|
|||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col items-center justify-center gap-4 px-3 py-8 text-center">
|
<div className="flex flex-col items-center justify-center px-3 py-8 text-center">
|
||||||
<LayersIcon height="56" width="56" />
|
<EmptyState
|
||||||
<h3 className="text-sm text-custom-text-200">
|
type={
|
||||||
No issues found. Create a new issue with{" "}
|
query === ""
|
||||||
<pre className="inline rounded bg-custom-background-80 px-2 py-1">C</pre>.
|
? EmptyStateType.ISSUE_RELATION_EMPTY_STATE
|
||||||
</h3>
|
: EmptyStateType.ISSUE_RELATION_SEARCH_EMPTY_STATE
|
||||||
|
}
|
||||||
|
layout="screen-simple"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Combobox.Options>
|
</Combobox.Options>
|
||||||
|
@ -3,14 +3,16 @@ import { useRouter } from "next/router";
|
|||||||
// headless ui
|
// headless ui
|
||||||
import { Combobox, Dialog, Transition } from "@headlessui/react";
|
import { Combobox, Dialog, Transition } from "@headlessui/react";
|
||||||
// services
|
// services
|
||||||
import { Rocket, Search } from "lucide-react";
|
|
||||||
import { LayersIcon, Loader, ToggleSwitch, Tooltip } from "@plane/ui";
|
|
||||||
import useDebounce from "hooks/use-debounce";
|
|
||||||
import { ProjectService } from "services/project";
|
import { ProjectService } from "services/project";
|
||||||
// hooks
|
// hooks
|
||||||
|
import useDebounce from "hooks/use-debounce";
|
||||||
import { usePlatformOS } from "hooks/use-platform-os";
|
import { usePlatformOS } from "hooks/use-platform-os";
|
||||||
|
// components
|
||||||
|
import { IssueSearchModalEmptyState } from "components/core";
|
||||||
// ui
|
// ui
|
||||||
|
import { Loader, ToggleSwitch, Tooltip } from "@plane/ui";
|
||||||
// icons
|
// icons
|
||||||
|
import { Rocket, Search } from "lucide-react";
|
||||||
// types
|
// types
|
||||||
import { ISearchIssueResponse } from "@plane/types";
|
import { ISearchIssueResponse } from "@plane/types";
|
||||||
|
|
||||||
@ -151,15 +153,12 @@ export const ParentIssuesListModal: React.FC<Props> = ({
|
|||||||
</h5>
|
</h5>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!isSearching && issues.length === 0 && searchTerm !== "" && debouncedSearchTerm !== "" && (
|
<IssueSearchModalEmptyState
|
||||||
<div className="flex flex-col items-center justify-center gap-4 px-3 py-8 text-center">
|
debouncedSearchTerm={debouncedSearchTerm}
|
||||||
<LayersIcon height="52" width="52" />
|
isSearching={isSearching}
|
||||||
<h3 className="text-custom-text-200">
|
issues={issues}
|
||||||
No issues found. Create a new issue with{" "}
|
searchTerm={searchTerm}
|
||||||
<pre className="inline rounded bg-custom-background-80 px-2 py-1 text-sm">C</pre>.
|
/>
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isSearching ? (
|
{isSearching ? (
|
||||||
<Loader className="space-y-3 p-3">
|
<Loader className="space-y-3 p-3">
|
||||||
|
@ -1,21 +1,22 @@
|
|||||||
import React, { Fragment } from "react";
|
import React, { Fragment } from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Popover, Transition } from "@headlessui/react";
|
import { Popover, Transition } from "@headlessui/react";
|
||||||
import { Bell } from "lucide-react";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { Tooltip } from "@plane/ui";
|
|
||||||
import { EmptyState } from "components/common";
|
|
||||||
import { SnoozeNotificationModal, NotificationCard, NotificationHeader } from "components/notifications";
|
|
||||||
import { NotificationsLoader } from "components/ui";
|
|
||||||
import { getNumberCount } from "helpers/string.helper";
|
|
||||||
import { useApplication } from "hooks/store";
|
import { useApplication } from "hooks/store";
|
||||||
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
||||||
import useUserNotification from "hooks/use-user-notifications";
|
import useUserNotification from "hooks/use-user-notifications";
|
||||||
import { usePlatformOS } from "hooks/use-platform-os";
|
import { usePlatformOS } from "hooks/use-platform-os";
|
||||||
|
// icons
|
||||||
|
import { Bell } from "lucide-react";
|
||||||
// components
|
// components
|
||||||
// images
|
import { Tooltip } from "@plane/ui";
|
||||||
import emptyNotification from "public/empty-state/notification.svg";
|
import { EmptyState } from "components/empty-state";
|
||||||
|
import { NotificationsLoader } from "components/ui";
|
||||||
|
import { SnoozeNotificationModal, NotificationCard, NotificationHeader } from "components/notifications";
|
||||||
|
// constants
|
||||||
|
import { EmptyStateType } from "constants/empty-state";
|
||||||
// helpers
|
// helpers
|
||||||
|
import { getNumberCount } from "helpers/string.helper";
|
||||||
|
|
||||||
export const NotificationPopover = observer(() => {
|
export const NotificationPopover = observer(() => {
|
||||||
// states
|
// states
|
||||||
@ -59,6 +60,16 @@ export const NotificationPopover = observer(() => {
|
|||||||
if (selectedNotificationForSnooze === null) setIsActive(false);
|
if (selectedNotificationForSnooze === null) setIsActive(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const currentTabEmptyState = snoozed
|
||||||
|
? EmptyStateType.NOTIFICATION_SNOOZED_EMPTY_STATE
|
||||||
|
: archived
|
||||||
|
? EmptyStateType.NOTIFICATION_ARCHIVED_EMPTY_STATE
|
||||||
|
: selectedTab === "created"
|
||||||
|
? EmptyStateType.NOTIFICATION_CREATED_EMPTY_STATE
|
||||||
|
: selectedTab === "watching"
|
||||||
|
? EmptyStateType.NOTIFICATION_SUBSCRIBED_EMPTY_STATE
|
||||||
|
: EmptyStateType.NOTIFICATION_MY_ISSUE_EMPTY_STATE;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SnoozeNotificationModal
|
<SnoozeNotificationModal
|
||||||
@ -70,7 +81,13 @@ export const NotificationPopover = observer(() => {
|
|||||||
/>
|
/>
|
||||||
<Popover ref={notificationPopoverRef} className="md:relative w-full">
|
<Popover ref={notificationPopoverRef} className="md:relative w-full">
|
||||||
<>
|
<>
|
||||||
<Tooltip tooltipContent="Notifications" position="right" className="ml-2" disabled={!isSidebarCollapsed} isMobile={isMobile}>
|
<Tooltip
|
||||||
|
tooltipContent="Notifications"
|
||||||
|
position="right"
|
||||||
|
className="ml-2"
|
||||||
|
disabled={!isSidebarCollapsed}
|
||||||
|
isMobile={isMobile}
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
className={`group relative flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-sm font-medium outline-none ${
|
className={`group relative flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-sm font-medium outline-none ${
|
||||||
isActive
|
isActive
|
||||||
@ -184,11 +201,7 @@ export const NotificationPopover = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="grid h-full w-full scale-75 place-items-center overflow-hidden">
|
<div className="grid h-full w-full scale-75 place-items-center overflow-hidden">
|
||||||
<EmptyState
|
<EmptyState type={currentTabEmptyState} layout="screen-simple" />
|
||||||
title="You're updated with all the notifications"
|
|
||||||
description="You have read all the notifications."
|
|
||||||
image={emptyNotification}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
|
@ -59,7 +59,6 @@ export enum EmptyStateType {
|
|||||||
PROJECT_DRAFT_NO_ISSUES = "project-draft-no-issues",
|
PROJECT_DRAFT_NO_ISSUES = "project-draft-no-issues",
|
||||||
VIEWS_EMPTY_SEARCH = "views-empty-search",
|
VIEWS_EMPTY_SEARCH = "views-empty-search",
|
||||||
PROJECTS_EMPTY_SEARCH = "projects-empty-search",
|
PROJECTS_EMPTY_SEARCH = "projects-empty-search",
|
||||||
COMMANDK_EMPTY_SEARCH = "commandK-empty-search",
|
|
||||||
MEMBERS_EMPTY_SEARCH = "members-empty-search",
|
MEMBERS_EMPTY_SEARCH = "members-empty-search",
|
||||||
PROJECT_MODULE_ISSUES = "project-module-issues",
|
PROJECT_MODULE_ISSUES = "project-module-issues",
|
||||||
PROJECT_MODULE = "project-module",
|
PROJECT_MODULE = "project-module",
|
||||||
@ -71,6 +70,18 @@ export enum EmptyStateType {
|
|||||||
PROJECT_PAGE_SHARED = "project-page-shared",
|
PROJECT_PAGE_SHARED = "project-page-shared",
|
||||||
PROJECT_PAGE_ARCHIVED = "project-page-archived",
|
PROJECT_PAGE_ARCHIVED = "project-page-archived",
|
||||||
PROJECT_PAGE_RECENT = "project-page-recent",
|
PROJECT_PAGE_RECENT = "project-page-recent",
|
||||||
|
|
||||||
|
COMMAND_K_SEARCH_EMPTY_STATE = "command-k-search-empty-state",
|
||||||
|
ISSUE_RELATION_SEARCH_EMPTY_STATE = "issue-relation-search-empty-state",
|
||||||
|
ISSUE_RELATION_EMPTY_STATE = "issue-relation-empty-state",
|
||||||
|
ISSUE_COMMENT_EMPTY_STATE = "issue-comment-empty-state",
|
||||||
|
|
||||||
|
NOTIFICATION_MY_ISSUE_EMPTY_STATE = "notification-my-issues-empty-state",
|
||||||
|
NOTIFICATION_CREATED_EMPTY_STATE = "notification-created-empty-state",
|
||||||
|
NOTIFICATION_SUBSCRIBED_EMPTY_STATE = "notification-subscribed-empty-state",
|
||||||
|
NOTIFICATION_ARCHIVED_EMPTY_STATE = "notification-archived-empty-state",
|
||||||
|
NOTIFICATION_SNOOZED_EMPTY_STATE = "notification-snoozed-empty-state",
|
||||||
|
NOTIFICATION_UNREAD_EMPTY_STATE = "notification-unread-empty-state",
|
||||||
}
|
}
|
||||||
|
|
||||||
const emptyStateDetails = {
|
const emptyStateDetails = {
|
||||||
@ -384,11 +395,6 @@ const emptyStateDetails = {
|
|||||||
description: "No projects detected with the matching criteria. Create a new project instead.",
|
description: "No projects detected with the matching criteria. Create a new project instead.",
|
||||||
path: "/empty-state/search/project",
|
path: "/empty-state/search/project",
|
||||||
},
|
},
|
||||||
[EmptyStateType.COMMANDK_EMPTY_SEARCH]: {
|
|
||||||
key: EmptyStateType.COMMANDK_EMPTY_SEARCH,
|
|
||||||
title: "No results found. ",
|
|
||||||
path: "/empty-state/search/search",
|
|
||||||
},
|
|
||||||
[EmptyStateType.MEMBERS_EMPTY_SEARCH]: {
|
[EmptyStateType.MEMBERS_EMPTY_SEARCH]: {
|
||||||
key: EmptyStateType.MEMBERS_EMPTY_SEARCH,
|
key: EmptyStateType.MEMBERS_EMPTY_SEARCH,
|
||||||
title: "No matching members",
|
title: "No matching members",
|
||||||
@ -504,6 +510,66 @@ const emptyStateDetails = {
|
|||||||
accessType: "project",
|
accessType: "project",
|
||||||
access: EUserProjectRoles.MEMBER,
|
access: EUserProjectRoles.MEMBER,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[EmptyStateType.COMMAND_K_SEARCH_EMPTY_STATE]: {
|
||||||
|
key: EmptyStateType.COMMAND_K_SEARCH_EMPTY_STATE,
|
||||||
|
title: "No results found",
|
||||||
|
path: "/empty-state/search/search",
|
||||||
|
},
|
||||||
|
[EmptyStateType.ISSUE_RELATION_SEARCH_EMPTY_STATE]: {
|
||||||
|
key: EmptyStateType.ISSUE_RELATION_SEARCH_EMPTY_STATE,
|
||||||
|
title: "No maching issues found",
|
||||||
|
path: "/empty-state/search/search",
|
||||||
|
},
|
||||||
|
[EmptyStateType.ISSUE_RELATION_EMPTY_STATE]: {
|
||||||
|
key: EmptyStateType.ISSUE_RELATION_EMPTY_STATE,
|
||||||
|
title: "No issues found",
|
||||||
|
path: "/empty-state/search/issues",
|
||||||
|
},
|
||||||
|
[EmptyStateType.ISSUE_COMMENT_EMPTY_STATE]: {
|
||||||
|
key: EmptyStateType.ISSUE_COMMENT_EMPTY_STATE,
|
||||||
|
title: "No comments yet",
|
||||||
|
description: "Comments can be used as a discussion and follow-up space for the issues",
|
||||||
|
path: "/empty-state/search/comments",
|
||||||
|
},
|
||||||
|
|
||||||
|
[EmptyStateType.NOTIFICATION_MY_ISSUE_EMPTY_STATE]: {
|
||||||
|
key: EmptyStateType.NOTIFICATION_MY_ISSUE_EMPTY_STATE,
|
||||||
|
title: "No issues assigned",
|
||||||
|
description: "Updates for issues assigned to you can be \n seen here",
|
||||||
|
path: "/empty-state/search/notification",
|
||||||
|
},
|
||||||
|
|
||||||
|
[EmptyStateType.NOTIFICATION_CREATED_EMPTY_STATE]: {
|
||||||
|
key: EmptyStateType.NOTIFICATION_CREATED_EMPTY_STATE,
|
||||||
|
title: "No updates to issues",
|
||||||
|
description: "Updates to issues created by you can be \n seen here",
|
||||||
|
path: "/empty-state/search/notification",
|
||||||
|
},
|
||||||
|
[EmptyStateType.NOTIFICATION_SUBSCRIBED_EMPTY_STATE]: {
|
||||||
|
key: EmptyStateType.NOTIFICATION_SUBSCRIBED_EMPTY_STATE,
|
||||||
|
title: "No updates to issues",
|
||||||
|
description: "Updates to any issue you are \n subscribed to can be seen here",
|
||||||
|
path: "/empty-state/search/notification",
|
||||||
|
},
|
||||||
|
[EmptyStateType.NOTIFICATION_UNREAD_EMPTY_STATE]: {
|
||||||
|
key: EmptyStateType.NOTIFICATION_UNREAD_EMPTY_STATE,
|
||||||
|
title: "No unread notifications",
|
||||||
|
description: "Congratulations, you are up-to-date \n with everything happening in the issues \n you care about",
|
||||||
|
path: "/empty-state/search/notification",
|
||||||
|
},
|
||||||
|
[EmptyStateType.NOTIFICATION_SNOOZED_EMPTY_STATE]: {
|
||||||
|
key: EmptyStateType.NOTIFICATION_SNOOZED_EMPTY_STATE,
|
||||||
|
title: "No snoozed notifications yet",
|
||||||
|
description: "Any notification you snooze for later will \n be available here to act upon",
|
||||||
|
path: "/empty-state/search/snooze",
|
||||||
|
},
|
||||||
|
[EmptyStateType.NOTIFICATION_ARCHIVED_EMPTY_STATE]: {
|
||||||
|
key: EmptyStateType.NOTIFICATION_ARCHIVED_EMPTY_STATE,
|
||||||
|
title: "No archived notifications yet",
|
||||||
|
description: "Any notification you archive will be \n available here to help you focus",
|
||||||
|
path: "/empty-state/search/archive",
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const EMPTY_STATE_DETAILS: Record<EmptyStateType, EmptyStateDetails> = emptyStateDetails;
|
export const EMPTY_STATE_DETAILS: Record<EmptyStateType, EmptyStateDetails> = emptyStateDetails;
|
||||||
|
BIN
web/public/empty-state/search/archive-dark.webp
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
web/public/empty-state/search/archive-light.webp
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
web/public/empty-state/search/comments-dark.webp
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
web/public/empty-state/search/comments-light.webp
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
web/public/empty-state/search/issue-dark.webp
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
web/public/empty-state/search/issues-light.webp
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
web/public/empty-state/search/notification-dark.webp
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
web/public/empty-state/search/notification-light.webp
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
web/public/empty-state/search/search-dark.webp
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
web/public/empty-state/search/search-light.webp
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
web/public/empty-state/search/snooze-dark.webp
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
web/public/empty-state/search/snooze-light.webp
Normal file
After Width: | Height: | Size: 2.7 KiB |