Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: new empty state #3640

Merged
merged 4 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion web/components/cycles/active-cycle-details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ import { ICycle, TCycleGroups } from "@plane/types";
// constants
import { EIssuesStoreType } from "constants/issue";
import { CYCLE_ISSUES_WITH_PARAMS } from "constants/fetch-keys";
import { CYCLE_EMPTY_STATE_DETAILS, CYCLE_STATE_GROUPS_DETAILS } from "constants/cycle";
import { CYCLE_STATE_GROUPS_DETAILS } from "constants/cycle";
import { CYCLE_EMPTY_STATE_DETAILS } from "constants/empty-state";

interface IActiveCycleDetails {
workspaceSlug: string;
Expand Down
2 changes: 1 addition & 1 deletion web/components/cycles/cycles-board.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useUser } from "hooks/store";
import { CyclePeekOverview, CyclesBoardCard } from "components/cycles";
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
// constants
import { CYCLE_EMPTY_STATE_DETAILS } from "constants/cycle";
import { CYCLE_EMPTY_STATE_DETAILS } from "constants/empty-state";

export interface ICyclesBoard {
cycleIds: string[];
Expand Down
2 changes: 1 addition & 1 deletion web/components/cycles/cycles-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
// ui
import { Loader } from "@plane/ui";
// constants
import { CYCLE_EMPTY_STATE_DETAILS } from "constants/cycle";
import { CYCLE_EMPTY_STATE_DETAILS } from "constants/empty-state";

export interface ICyclesList {
cycleIds: string[];
Expand Down
34 changes: 17 additions & 17 deletions web/components/estimates/estimates-list.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import React, { useState } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import { Plus } from "lucide-react";
import { useTheme } from "next-themes";
// store hooks
import { useEstimate, useProject } from "hooks/store";
import { useEstimate, useProject, useUser } from "hooks/store";
import useToast from "hooks/use-toast";
// components
import { CreateUpdateEstimateModal, DeleteEstimateModal, EstimateListItem } from "components/estimates";
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
// ui
import { Button, Loader } from "@plane/ui";
import { EmptyState } from "components/common";
// images
import emptyEstimate from "public/empty-state/estimate.svg";
// types
import { IEstimate } from "@plane/types";
// helpers
import { orderArrayBy } from "helpers/array.helper";
// constants
import { PROJECT_SETTINGS_EMPTY_STATE_DETAILS } from "constants/empty-state";

export const EstimatesList: React.FC = observer(() => {
// states
Expand All @@ -25,9 +25,12 @@ export const EstimatesList: React.FC = observer(() => {
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
// theme
const { resolvedTheme } = useTheme();
// store hooks
const { updateProject, currentProjectDetails } = useProject();
const { projectEstimates, getProjectEstimateById } = useEstimate();
const { currentUser } = useUser();
// toast alert
const { setToastAlert } = useToast();

Expand Down Expand Up @@ -55,6 +58,10 @@ export const EstimatesList: React.FC = observer(() => {
});
};

const emptyStateDetail = PROJECT_SETTINGS_EMPTY_STATE_DETAILS["estimate"];
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
const emptyStateImage = getEmptyStateImagePath("project-settings", "estimates", isLightMode);

return (
<>
<CreateUpdateEstimateModal
Expand Down Expand Up @@ -108,19 +115,12 @@ export const EstimatesList: React.FC = observer(() => {
))}
</section>
) : (
<div className="w-full py-8">
<div className="h-full w-full py-8">
<EmptyState
title="No estimates yet"
description="Estimates help you communicate the complexity of an issue."
image={emptyEstimate}
primaryButton={{
icon: <Plus className="h-4 w-4" />,
text: "Add Estimate",
onClick: () => {
setEstimateFormOpen(true);
setEstimateToUpdate(undefined);
},
}}
title={emptyStateDetail.title}
description={emptyStateDetail.description}
image={emptyStateImage}
size="lg"
/>
</div>
)
Expand Down
25 changes: 20 additions & 5 deletions web/components/exporter/guide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ import { useState } from "react";
import Link from "next/link";
import Image from "next/image";
import { useRouter } from "next/router";

import { useTheme } from "next-themes";
import useSWR, { mutate } from "swr";

import { observer } from "mobx-react-lite";
// hooks
import { useUser } from "hooks/store";
import useUserAuth from "hooks/use-user-auth";
// services
import { IntegrationService } from "services/integrations";
// components
import { Exporter, SingleExport } from "components/exporter";
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
// ui
import { Button, Loader } from "@plane/ui";
// icons
Expand All @@ -20,8 +22,8 @@ import { MoveLeft, MoveRight, RefreshCw } from "lucide-react";
import { EXPORT_SERVICES_LIST } from "constants/fetch-keys";
// constants
import { EXPORTERS_LIST } from "constants/workspace";
import { observer } from "mobx-react-lite";
import { useUser } from "hooks/store";

import { WORKSPACE_SETTINGS_EMPTY_STATE_DETAILS } from "constants/empty-state";

// services
const integrationService = new IntegrationService();
Expand All @@ -34,6 +36,8 @@ const IntegrationGuide = observer(() => {
// router
const router = useRouter();
const { workspaceSlug, provider } = router.query;
// theme
const { resolvedTheme } = useTheme();
// store hooks
const { currentUser, currentUserLoader } = useUser();
// custom hooks
Expand All @@ -46,6 +50,10 @@ const IntegrationGuide = observer(() => {
: null
);

const emptyStateDetail = WORKSPACE_SETTINGS_EMPTY_STATE_DETAILS["export"];
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
const emptyStateImage = getEmptyStateImagePath("workspace-settings", "exports", isLightMode);

const handleCsvClose = () => {
router.replace(`/${workspaceSlug?.toString()}/settings/exports`);
};
Expand Down Expand Up @@ -140,7 +148,14 @@ const IntegrationGuide = observer(() => {
</div>
</div>
) : (
<p className="px-4 py-6 text-sm text-custom-text-200">No previous export available.</p>
<div className="h-full w-full flex items-center justify-center">
<EmptyState
title={emptyStateDetail.title}
description={emptyStateDetail.description}
image={emptyStateImage}
size="sm"
/>
</div>
)
) : (
<Loader className="mt-6 grid grid-cols-1 gap-3">
Expand Down
18 changes: 17 additions & 1 deletion web/components/integration/guide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import Image from "next/image";
import { useRouter } from "next/router";
import useSWR, { mutate } from "swr";
import { observer } from "mobx-react-lite";
import { useTheme } from "next-themes";
// hooks
import { useUser } from "hooks/store";
import useUserAuth from "hooks/use-user-auth";
// services
import { IntegrationService } from "services/integrations";
// components
import { DeleteImportModal, GithubImporterRoot, JiraImporterRoot, SingleImport } from "components/integration";
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
// ui
import { Button, Loader } from "@plane/ui";
// icons
Expand All @@ -21,6 +23,7 @@ import { IImporterService } from "@plane/types";
import { IMPORTER_SERVICES_LIST } from "constants/fetch-keys";
// constants
import { IMPORTERS_LIST } from "constants/workspace";
import { WORKSPACE_SETTINGS_EMPTY_STATE_DETAILS } from "constants/empty-state";

// services
const integrationService = new IntegrationService();
Expand All @@ -33,6 +36,8 @@ const IntegrationGuide = observer(() => {
// router
const router = useRouter();
const { workspaceSlug, provider } = router.query;
// theme
const { resolvedTheme } = useTheme();
// store hooks
const { currentUser, currentUserLoader } = useUser();
// custom hooks
Expand All @@ -43,6 +48,10 @@ const IntegrationGuide = observer(() => {
workspaceSlug ? () => integrationService.getImporterServicesList(workspaceSlug as string) : null
);

const emptyStateDetail = WORKSPACE_SETTINGS_EMPTY_STATE_DETAILS["import"];
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
const emptyStateImage = getEmptyStateImagePath("workspace-settings", "imports", isLightMode);

const handleDeleteImport = (importService: IImporterService) => {
setImportToDelete(importService);
setDeleteImportModal(true);
Expand Down Expand Up @@ -134,7 +143,14 @@ const IntegrationGuide = observer(() => {
</div>
</div>
) : (
<p className="px-4 py-6 text-sm text-custom-text-200">No previous imports available.</p>
<div className="h-full w-full flex items-center justify-center">
<EmptyState
title={emptyStateDetail.title}
description={emptyStateDetail.description}
image={emptyStateImage}
size="sm"
/>
</div>
)
) : (
<Loader className="mt-6 grid grid-cols-1 gap-3">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
// constants
import { EUserProjectRoles } from "constants/project";
import { EIssueFilterType, EIssuesStoreType } from "constants/issue";
import { EMPTY_FILTER_STATE_DETAILS, EMPTY_ISSUE_STATE_DETAILS } from "constants/empty-state";
// types
import { IIssueFilterOptions } from "@plane/types";

Expand Down Expand Up @@ -65,20 +66,19 @@ export const ProjectArchivedEmptyState: React.FC = observer(() => {
const emptyStateProps: EmptyStateProps =
issueFilterCount > 0
? {
title: "No issues found matching the filters applied",
title: EMPTY_FILTER_STATE_DETAILS["archived"].title,
image: currentLayoutEmptyStateImagePath,
secondaryButton: {
text: "Clear all filters",
text: EMPTY_FILTER_STATE_DETAILS["archived"].secondaryButton?.text,
onClick: handleClearAllFilters,
},
}
: {
title: "No archived issues yet",
description:
"Archived issues help you remove issues you completed or cancelled from focus. You can set automation to auto archive issues and find them here.",
title: EMPTY_ISSUE_STATE_DETAILS["archived"].title,
description: EMPTY_ISSUE_STATE_DETAILS["archived"].description,
image: EmptyStateImagePath,
primaryButton: {
text: "Set Automation",
text: EMPTY_ISSUE_STATE_DETAILS["archived"].primaryButton.text,
onClick: () => router.push(`/${workspaceSlug}/projects/${projectId}/settings/automations`),
},
size: "sm",
Expand Down
45 changes: 24 additions & 21 deletions web/components/issues/issue-layouts/empty-states/cycle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,29 @@ import { PlusIcon } from "lucide-react";
import { useApplication, useEventTracker, useIssueDetail, useIssues, useUser } from "hooks/store";
import useToast from "hooks/use-toast";
// components
import { EmptyState } from "components/common";
import { ExistingIssuesListModal } from "components/core";
// ui
import { Button } from "@plane/ui";
// assets
import emptyIssue from "public/empty-state/issue.svg";
// types
import { ISearchIssueResponse } from "@plane/types";
import { ISearchIssueResponse, TIssueLayouts } from "@plane/types";
// constants
import { EUserProjectRoles } from "constants/project";
import { EIssuesStoreType } from "constants/issue";
import { CYCLE_EMPTY_STATE_DETAILS } from "constants/empty-state";
import { useTheme } from "next-themes";
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";

type Props = {
workspaceSlug: string | undefined;
projectId: string | undefined;
cycleId: string | undefined;
activeLayout: TIssueLayouts | undefined;
};

export const CycleEmptyState: React.FC<Props> = observer((props) => {
const { workspaceSlug, projectId, cycleId } = props;
const { workspaceSlug, projectId, cycleId, activeLayout } = props;
// states
const [cycleIssuesListModal, setCycleIssuesListModal] = useState(false);
// theme
const { resolvedTheme } = useTheme();
// store hooks
const { issues } = useIssues(EIssuesStoreType.CYCLE);
const { updateIssue, fetchIssue } = useIssueDetail();
Expand All @@ -36,6 +37,7 @@ export const CycleEmptyState: React.FC<Props> = observer((props) => {
const { setTrackElement } = useEventTracker();
const {
membership: { currentProjectRole: userRole },
currentUser,
} = useUser();

const { setToastAlert } = useToast();
Expand All @@ -60,6 +62,11 @@ export const CycleEmptyState: React.FC<Props> = observer((props) => {
});
};

const emptyStateDetail = CYCLE_EMPTY_STATE_DETAILS["no-issues"];

const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
const emptyStateImage = getEmptyStateImagePath("cycle-issues", activeLayout ?? "list", isLightMode);

const isEditingAllowed = !!userRole && userRole >= EUserProjectRoles.MEMBER;

return (
Expand All @@ -74,27 +81,23 @@ export const CycleEmptyState: React.FC<Props> = observer((props) => {
/>
<div className="grid h-full w-full place-items-center">
<EmptyState
title="Cycle issues will appear here"
description="Issues help you track individual pieces of work. With Issues, keep track of what's going on, who is working on it, and what's done."
image={emptyIssue}
title={emptyStateDetail.title}
description={emptyStateDetail.description}
image={emptyStateImage}
primaryButton={{
text: "New issue",
text: emptyStateDetail.primaryButton.text,
icon: <PlusIcon className="h-3 w-3" strokeWidth={2} />,
onClick: () => {
setTrackElement("Cycle issue empty state");
toggleCreateIssueModal(true, EIssuesStoreType.CYCLE);
},
}}
secondaryButton={
<Button
variant="neutral-primary"
prependIcon={<PlusIcon className="h-3 w-3" strokeWidth={2} />}
onClick={() => setCycleIssuesListModal(true)}
disabled={!isEditingAllowed}
>
Add an existing issue
</Button>
}
secondaryButton={{
text: emptyStateDetail.secondaryButton.text,
icon: <PlusIcon className="h-3 w-3" strokeWidth={2} />,
onClick: () => setCycleIssuesListModal(true),
}}
size="sm"
disabled={!isEditingAllowed}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
// constants
import { EUserProjectRoles } from "constants/project";
import { EIssueFilterType, EIssuesStoreType } from "constants/issue";
import { EMPTY_FILTER_STATE_DETAILS, EMPTY_ISSUE_STATE_DETAILS } from "constants/empty-state";
// types
import { IIssueFilterOptions } from "@plane/types";

Expand Down Expand Up @@ -65,17 +66,16 @@ export const ProjectDraftEmptyState: React.FC = observer(() => {
const emptyStateProps: EmptyStateProps =
issueFilterCount > 0
? {
title: "No issues found matching the filters applied",
title: EMPTY_FILTER_STATE_DETAILS["draft"].title,
image: currentLayoutEmptyStateImagePath,
secondaryButton: {
text: "Clear all filters",
text: EMPTY_FILTER_STATE_DETAILS["draft"].secondaryButton.text,
onClick: handleClearAllFilters,
},
}
: {
title: "No draft issues yet",
description:
"Quickly stepping away but want to keep your place? No worries – save a draft now. Your issues will be right here waiting for you.",
title: EMPTY_ISSUE_STATE_DETAILS["draft"].title,
description: EMPTY_ISSUE_STATE_DETAILS["draft"].description,
image: EmptyStateImagePath,
size: "sm",
disabled: !isEditingAllowed,
Expand Down
Loading
Loading