diff --git a/docker-compose-local.yml b/docker-compose-local.yml index a2e518708d..5f49e48976 100644 --- a/docker-compose-local.yml +++ b/docker-compose-local.yml @@ -137,7 +137,7 @@ services: dockerfile: Dockerfile.dev args: DOCKER_BUILDKIT: 1 - restart: no + restart: "no" networks: - dev_env volumes: diff --git a/packages/types/src/projects.d.ts b/packages/types/src/projects.d.ts index 42db704c67..eefbdd14e7 100644 --- a/packages/types/src/projects.d.ts +++ b/packages/types/src/projects.d.ts @@ -128,7 +128,7 @@ export type TProjectIssuesSearchParams = { parent?: boolean; issue_relation?: boolean; cycle?: boolean; - module?: string[]; + module?: string; sub_issue?: boolean; issue_id?: string; workspace_search: boolean; diff --git a/web/components/core/modals/existing-issues-list-modal.tsx b/web/components/core/modals/existing-issues-list-modal.tsx index f136b099fa..c4fa25c6d6 100644 --- a/web/components/core/modals/existing-issues-list-modal.tsx +++ b/web/components/core/modals/existing-issues-list-modal.tsx @@ -78,7 +78,6 @@ export const ExistingIssuesListModal: React.FC = (props) => { useEffect(() => { if (!isOpen || !workspaceSlug || !projectId) return; - if (issues.length <= 0) setIsSearching(true); projectService .projectIssuesSearch(workspaceSlug as string, projectId as string, { @@ -88,16 +87,7 @@ export const ExistingIssuesListModal: React.FC = (props) => { }) .then((res) => setIssues(res)) .finally(() => setIsSearching(false)); - }, [issues, debouncedSearchTerm, isOpen, isWorkspaceLevel, projectId, searchParams, workspaceSlug]); - - useEffect(() => { - setSearchTerm(""); - setIssues([]); - setSelectedIssues([]); - setIsSearching(false); - setIsSubmitting(false); - setIsWorkspaceLevel(false); - }, [isOpen]); + }, [debouncedSearchTerm, isOpen, isWorkspaceLevel, projectId, searchParams, workspaceSlug]); return ( <> diff --git a/web/components/core/sidebar/sidebar-menu-hamburger-toggle.tsx b/web/components/core/sidebar/sidebar-menu-hamburger-toggle.tsx index 30d764ab18..b732d8f952 100644 --- a/web/components/core/sidebar/sidebar-menu-hamburger-toggle.tsx +++ b/web/components/core/sidebar/sidebar-menu-hamburger-toggle.tsx @@ -8,7 +8,7 @@ export const SidebarHamburgerToggle: FC = observer(() => { return (
themStore.toggleSidebar()} + onClick={() => themStore.toggleMobileSidebar()} >
diff --git a/web/components/cycles/active-cycle-details.tsx b/web/components/cycles/active-cycle-details.tsx index 317465d297..73a547364b 100644 --- a/web/components/cycles/active-cycle-details.tsx +++ b/web/components/cycles/active-cycle-details.tsx @@ -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; diff --git a/web/components/cycles/cycles-board.tsx b/web/components/cycles/cycles-board.tsx index 19e7f22252..34e973614c 100644 --- a/web/components/cycles/cycles-board.tsx +++ b/web/components/cycles/cycles-board.tsx @@ -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[]; diff --git a/web/components/cycles/cycles-list.tsx b/web/components/cycles/cycles-list.tsx index 90fcdd8f9f..838d88a305 100644 --- a/web/components/cycles/cycles-list.tsx +++ b/web/components/cycles/cycles-list.tsx @@ -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[]; diff --git a/web/components/cycles/cycles-view.tsx b/web/components/cycles/cycles-view.tsx index 7b58bde453..a321be0b59 100644 --- a/web/components/cycles/cycles-view.tsx +++ b/web/components/cycles/cycles-view.tsx @@ -5,7 +5,7 @@ import { useCycle } from "hooks/store"; // components import { CyclesBoard, CyclesList, CyclesListGanttChartView } from "components/cycles"; // ui components -import { Loader } from "@plane/ui"; +import { CycleModuleBoardLayout, CycleModuleListLayout, GanttLayoutLoader } from "components/ui"; // types import { TCycleLayout, TCycleView } from "@plane/types"; @@ -25,6 +25,7 @@ export const CyclesView: FC = observer((props) => { currentProjectDraftCycleIds, currentProjectUpcomingCycleIds, currentProjectCycleIds, + loader, } = useCycle(); const cyclesList = @@ -36,55 +37,32 @@ export const CyclesView: FC = observer((props) => { ? currentProjectUpcomingCycleIds : currentProjectCycleIds; + if (loader || !cyclesList) + return ( + <> + {layout === "list" && } + {layout === "board" && } + {layout === "gantt" && } + + ); + return ( <> {layout === "list" && ( - <> - {cyclesList ? ( - - ) : ( - - - - - - )} - + )} {layout === "board" && ( - <> - {cyclesList ? ( - - ) : ( - - - - - - )} - + )} - {layout === "gantt" && ( - <> - {cyclesList ? ( - - ) : ( - - - - - - )} - - )} + {layout === "gantt" && } ); }); diff --git a/web/components/estimates/estimates-list.tsx b/web/components/estimates/estimates-list.tsx index e226e17e02..1dabc61814 100644 --- a/web/components/estimates/estimates-list.tsx +++ b/web/components/estimates/estimates-list.tsx @@ -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 @@ -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(); @@ -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 ( <> { ))} ) : ( -
+
, - text: "Add Estimate", - onClick: () => { - setEstimateFormOpen(true); - setEstimateToUpdate(undefined); - }, - }} + title={emptyStateDetail.title} + description={emptyStateDetail.description} + image={emptyStateImage} + size="lg" />
) diff --git a/web/components/exporter/guide.tsx b/web/components/exporter/guide.tsx index 87bf0604a9..ed6a392207 100644 --- a/web/components/exporter/guide.tsx +++ b/web/components/exporter/guide.tsx @@ -3,25 +3,28 @@ 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"; +import { Button } from "@plane/ui"; +import { ImportExportSettingsLoader } from "components/ui"; // icons import { MoveLeft, MoveRight, RefreshCw } from "lucide-react"; // fetch-keys 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(); @@ -34,6 +37,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 @@ -46,6 +51,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`); }; @@ -140,15 +149,17 @@ const IntegrationGuide = observer(() => {
) : ( -

No previous export available.

+
+ +
) ) : ( - - - - - - + )} diff --git a/web/components/gantt-chart/blocks/blocks-list.tsx b/web/components/gantt-chart/blocks/blocks-list.tsx index 1bc75ea8f8..15a3e52950 100644 --- a/web/components/gantt-chart/blocks/blocks-list.tsx +++ b/web/components/gantt-chart/blocks/blocks-list.tsx @@ -86,7 +86,7 @@ export const GanttChartBlocksList: FC = observer((props) className="h-full" style={{ width: `${itemsContainerWidth}px`, - marginTop: `${HEADER_HEIGHT}px`, + transform: `translateY(${HEADER_HEIGHT}px)`, }} > {blocks?.map((block) => { diff --git a/web/components/integration/guide.tsx b/web/components/integration/guide.tsx index 9499988fac..29dfa5b30f 100644 --- a/web/components/integration/guide.tsx +++ b/web/components/integration/guide.tsx @@ -4,6 +4,7 @@ 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"; @@ -11,8 +12,10 @@ import useUserAuth from "hooks/use-user-auth"; 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"; +import { Button } from "@plane/ui"; +import { ImportExportSettingsLoader } from "components/ui"; // icons import { RefreshCw } from "lucide-react"; // types @@ -21,6 +24,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(); @@ -33,6 +37,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 @@ -43,6 +49,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); @@ -134,15 +144,17 @@ const IntegrationGuide = observer(() => { ) : ( -

No previous imports available.

+
+ +
) ) : ( - - - - - - + )} diff --git a/web/components/integration/single-integration-card.tsx b/web/components/integration/single-integration-card.tsx index 70bbb5fa45..3026d69811 100644 --- a/web/components/integration/single-integration-card.tsx +++ b/web/components/integration/single-integration-card.tsx @@ -168,7 +168,7 @@ export const SingleIntegrationCard: React.FC = observer(({ integration }) ) ) : ( - + )} diff --git a/web/components/issues/issue-layouts/empty-states/archived-issues.tsx b/web/components/issues/issue-layouts/empty-states/archived-issues.tsx index 89eb581104..0c8fb377a9 100644 --- a/web/components/issues/issue-layouts/empty-states/archived-issues.tsx +++ b/web/components/issues/issue-layouts/empty-states/archived-issues.tsx @@ -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"; @@ -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", diff --git a/web/components/issues/issue-layouts/empty-states/cycle.tsx b/web/components/issues/issue-layouts/empty-states/cycle.tsx index 02bdf6820c..baf2abb70f 100644 --- a/web/components/issues/issue-layouts/empty-states/cycle.tsx +++ b/web/components/issues/issue-layouts/empty-states/cycle.tsx @@ -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 = 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(); @@ -36,6 +37,7 @@ export const CycleEmptyState: React.FC = observer((props) => { const { setTrackElement } = useEventTracker(); const { membership: { currentProjectRole: userRole }, + currentUser, } = useUser(); const { setToastAlert } = useToast(); @@ -60,6 +62,11 @@ export const CycleEmptyState: React.FC = 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 ( @@ -74,27 +81,23 @@ export const CycleEmptyState: React.FC = observer((props) => { />
, onClick: () => { setTrackElement("Cycle issue empty state"); toggleCreateIssueModal(true, EIssuesStoreType.CYCLE); }, }} - secondaryButton={ - - } + secondaryButton={{ + text: emptyStateDetail.secondaryButton.text, + icon: , + onClick: () => setCycleIssuesListModal(true), + }} + size="sm" disabled={!isEditingAllowed} />
diff --git a/web/components/issues/issue-layouts/empty-states/draft-issues.tsx b/web/components/issues/issue-layouts/empty-states/draft-issues.tsx index 1d2695ff99..347778d8f2 100644 --- a/web/components/issues/issue-layouts/empty-states/draft-issues.tsx +++ b/web/components/issues/issue-layouts/empty-states/draft-issues.tsx @@ -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"; @@ -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, diff --git a/web/components/issues/issue-layouts/empty-states/module.tsx b/web/components/issues/issue-layouts/empty-states/module.tsx index c656bd6227..4285368e99 100644 --- a/web/components/issues/issue-layouts/empty-states/module.tsx +++ b/web/components/issues/issue-layouts/empty-states/module.tsx @@ -5,37 +5,38 @@ import { PlusIcon } from "lucide-react"; import { useApplication, useEventTracker, 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 { MODULE_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; moduleId: string | undefined; + activeLayout: TIssueLayouts | undefined; }; export const ModuleEmptyState: React.FC = observer((props) => { - const { workspaceSlug, projectId, moduleId } = props; + const { workspaceSlug, projectId, moduleId, activeLayout } = props; // states const [moduleIssuesListModal, setModuleIssuesListModal] = useState(false); + // theme + const { resolvedTheme } = useTheme(); // store hooks const { issues } = useIssues(EIssuesStoreType.MODULE); - const { commandPalette: { toggleCreateIssueModal }, } = useApplication(); const { setTrackElement } = useEventTracker(); const { membership: { currentProjectRole: userRole }, + currentUser, } = useUser(); // toast alert const { setToastAlert } = useToast(); @@ -55,6 +56,11 @@ export const ModuleEmptyState: React.FC = observer((props) => { ); }; + const emptyStateDetail = MODULE_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 ( @@ -64,32 +70,27 @@ export const ModuleEmptyState: React.FC = observer((props) => { projectId={projectId} isOpen={moduleIssuesListModal} handleClose={() => setModuleIssuesListModal(false)} - searchParams={{ module: moduleId != undefined ? [moduleId.toString()] : [] }} + searchParams={{ module: moduleId != undefined ? moduleId.toString() : "" }} handleOnSubmit={handleAddIssuesToModule} />
, onClick: () => { setTrackElement("Module issue empty state"); toggleCreateIssueModal(true, EIssuesStoreType.MODULE); }, }} - secondaryButton={ - - } + secondaryButton={{ + text: emptyStateDetail.secondaryButton.text, + icon: , + onClick: () => setModuleIssuesListModal(true), + }} disabled={!isEditingAllowed} />
diff --git a/web/components/issues/issue-layouts/empty-states/project-issues.tsx b/web/components/issues/issue-layouts/empty-states/project-issues.tsx index 32a60e996b..c7185934c2 100644 --- a/web/components/issues/issue-layouts/empty-states/project-issues.tsx +++ b/web/components/issues/issue-layouts/empty-states/project-issues.tsx @@ -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"; @@ -67,26 +68,23 @@ export const ProjectEmptyState: React.FC = observer(() => { const emptyStateProps: EmptyStateProps = issueFilterCount > 0 ? { - title: "No issues found matching the filters applied", + title: EMPTY_FILTER_STATE_DETAILS["project"].title, image: currentLayoutEmptyStateImagePath, secondaryButton: { - text: "Clear all filters", + text: EMPTY_FILTER_STATE_DETAILS["project"].secondaryButton.text, onClick: handleClearAllFilters, }, } : { - title: "Create an issue and assign it to someone, even yourself", - description: - "Think of issues as jobs, tasks, work, or JTBD. Which we like. An issue and its sub-issues are usually time-based actionables assigned to members of your team. Your team creates, assigns, and completes issues to move your project towards its goal.", + title: EMPTY_ISSUE_STATE_DETAILS["project"].title, + description: EMPTY_ISSUE_STATE_DETAILS["project"].description, image: EmptyStateImagePath, comicBox: { - title: "Issues are building blocks in Plane.", - description: - "Redesign the Plane UI, Rebrand the company, or Launch the new fuel injection system are examples of issues that likely have sub-issues.", + title: EMPTY_ISSUE_STATE_DETAILS["project"].comicBox.title, + description: EMPTY_ISSUE_STATE_DETAILS["project"].comicBox.description, }, primaryButton: { - text: "Create your first issue", - + text: EMPTY_ISSUE_STATE_DETAILS["project"].primaryButton.text, onClick: () => { setTrackElement("Project issue empty state"); commandPaletteStore.toggleCreateIssueModal(true, EIssuesStoreType.PROJECT); diff --git a/web/components/issues/issue-layouts/kanban/block.tsx b/web/components/issues/issue-layouts/kanban/block.tsx index 24cbe99084..6102ce0dd5 100644 --- a/web/components/issues/issue-layouts/kanban/block.tsx +++ b/web/components/issues/issue-layouts/kanban/block.tsx @@ -138,14 +138,14 @@ export const KanbanIssueBlock: React.FC = memo((props) => { >
= observer((props) => { const { setToastAlert } = useToast(); const renderExistingIssueModal = moduleId || cycleId; - const ExistingIssuesListModalPayload = moduleId ? { module: [moduleId.toString()] } : { cycle: true }; + const ExistingIssuesListModalPayload = moduleId ? { module: moduleId.toString() } : { cycle: true }; const handleAddIssuesToView = async (data: ISearchIssueResponse[]) => { if (!workspaceSlug || !projectId) return; diff --git a/web/components/issues/issue-layouts/kanban/utils.ts b/web/components/issues/issue-layouts/kanban/utils.ts index 5c5de8c452..6175985243 100644 --- a/web/components/issues/issue-layouts/kanban/utils.ts +++ b/web/components/issues/issue-layouts/kanban/utils.ts @@ -67,10 +67,13 @@ export const handleDragDrop = async ( let updateIssue: any = {}; - const sourceColumnId = (source?.droppableId && source?.droppableId.split("__")) || null; - const destinationColumnId = (destination?.droppableId && destination?.droppableId.split("__")) || null; + const sourceDroppableId = source?.droppableId; + const destinationDroppableId = destination?.droppableId; - if (!sourceColumnId || !destinationColumnId) return; + const sourceColumnId = (sourceDroppableId && sourceDroppableId.split("__")) || null; + const destinationColumnId = (destinationDroppableId && destinationDroppableId.split("__")) || null; + + if (!sourceColumnId || !destinationColumnId || !sourceDroppableId || !destinationDroppableId) return; const sourceGroupByColumnId = sourceColumnId[0] || null; const destinationGroupByColumnId = destinationColumnId[0] || null; @@ -101,9 +104,13 @@ export const handleDragDrop = async ( else return await store?.removeIssue(workspaceSlug, projectId, removed); } } else { - const sourceIssues = subGroupBy - ? (issueWithIds as TSubGroupedIssues)[sourceSubGroupByColumnId][sourceGroupByColumnId] - : (issueWithIds as TGroupedIssues)[sourceGroupByColumnId]; + //spreading the array to stop changing the original reference + //since we are removing an id from array further down + const sourceIssues = [ + ...(subGroupBy + ? (issueWithIds as TSubGroupedIssues)[sourceSubGroupByColumnId][sourceGroupByColumnId] + : (issueWithIds as TGroupedIssues)[sourceGroupByColumnId]), + ]; const destinationIssues = subGroupBy ? (issueWithIds as TSubGroupedIssues)[sourceSubGroupByColumnId][destinationGroupByColumnId] : (issueWithIds as TGroupedIssues)[destinationGroupByColumnId]; @@ -119,7 +126,11 @@ export const handleDragDrop = async ( // for both horizontal and vertical dnd updateIssue = { ...updateIssue, - ...handleSortOrder(destinationIssues, destination.index, issueMap), + ...handleSortOrder( + sourceDroppableId === destinationDroppableId ? sourceIssues : destinationIssues, + destination.index, + issueMap + ), }; if (subGroupBy && sourceSubGroupByColumnId && destinationSubGroupByColumnId) { diff --git a/web/components/issues/issue-layouts/list/headers/group-by-card.tsx b/web/components/issues/issue-layouts/list/headers/group-by-card.tsx index 90270e1a15..5a6b3c462d 100644 --- a/web/components/issues/issue-layouts/list/headers/group-by-card.tsx +++ b/web/components/issues/issue-layouts/list/headers/group-by-card.tsx @@ -41,7 +41,7 @@ export const HeaderGroupByCard = observer( const { setToastAlert } = useToast(); const renderExistingIssueModal = moduleId || cycleId; - const ExistingIssuesListModalPayload = moduleId ? { module: [moduleId.toString()] } : { cycle: true }; + const ExistingIssuesListModalPayload = moduleId ? { module: moduleId.toString() } : { cycle: true }; const handleAddIssuesToView = async (data: ISearchIssueResponse[]) => { if (!workspaceSlug || !projectId) return; diff --git a/web/components/issues/issue-layouts/roots/all-issue-layout-root.tsx b/web/components/issues/issue-layouts/roots/all-issue-layout-root.tsx index 6bbbfebb4e..dd8a7181ab 100644 --- a/web/components/issues/issue-layouts/roots/all-issue-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/all-issue-layout-root.tsx @@ -12,15 +12,15 @@ import { GlobalViewsAppliedFiltersRoot, IssuePeekOverview } from "components/iss import { SpreadsheetView } from "components/issues/issue-layouts"; import { AllIssueQuickActions } from "components/issues/issue-layouts/quick-action-dropdowns"; import { EmptyState, getEmptyStateImagePath } from "components/empty-state"; -// ui -import { Spinner } from "@plane/ui"; +import { SpreadsheetLayoutLoader } from "components/ui"; // types import { TIssue, IIssueDisplayFilterOptions } from "@plane/types"; import { EIssueActions } from "../types"; // constants import { EUserProjectRoles } from "constants/project"; import { EIssueFilterType, EIssuesStoreType, ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "constants/issue"; -import { ALL_ISSUES_EMPTY_STATE_DETAILS, EUserWorkspaceRoles } from "constants/workspace"; +import { EUserWorkspaceRoles } from "constants/workspace"; +import { ALL_ISSUES_EMPTY_STATE_DETAILS } from "constants/empty-state"; export const AllIssueLayoutRoot: React.FC = observer(() => { // router @@ -177,64 +177,60 @@ export const AllIssueLayoutRoot: React.FC = observer(() => { const isEditingAllowed = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER; + if (loader === "init-loader" || !globalViewId || globalViewId !== dataViewId || !issueIds) { + return ; + } + + if (issueIds.length === 0) { + return ( + 0 ? currentViewDetails.title : "No project"} + description={ + (workspaceProjectIds ?? []).length > 0 + ? currentViewDetails.description + : "To create issues or manage your work, you need to create a project or be a part of one." + } + size="sm" + primaryButton={ + (workspaceProjectIds ?? []).length > 0 + ? currentView !== "custom-view" && currentView !== "subscribed" + ? { + text: "Create new issue", + onClick: () => { + setTrackElement("All issues empty state"); + commandPaletteStore.toggleCreateIssueModal(true, EIssuesStoreType.PROJECT); + }, + } + : undefined + : { + text: "Start your first project", + onClick: () => { + setTrackElement("All issues empty state"); + commandPaletteStore.toggleCreateProjectModal(true); + }, + } + } + disabled={!isEditingAllowed} + /> + ); + } + return (
- {!globalViewId || globalViewId !== dataViewId || loader === "init-loader" || !issueIds ? ( -
- -
- ) : ( - <> - - - {(issueIds ?? {}).length == 0 ? ( - 0 ? currentViewDetails.title : "No project"} - description={ - (workspaceProjectIds ?? []).length > 0 - ? currentViewDetails.description - : "To create issues or manage your work, you need to create a project or be a part of one." - } - size="sm" - primaryButton={ - (workspaceProjectIds ?? []).length > 0 - ? currentView !== "custom-view" && currentView !== "subscribed" - ? { - text: "Create new issue", - onClick: () => { - setTrackElement("All issues empty state"); - commandPaletteStore.toggleCreateIssueModal(true, EIssuesStoreType.PROJECT); - }, - } - : undefined - : { - text: "Start your first project", - onClick: () => { - setTrackElement("All issues empty state"); - commandPaletteStore.toggleCreateProjectModal(true); - }, - } - } - disabled={!isEditingAllowed} - /> - ) : ( -
- -
- )} - - )} - + +
+ +
{/* peek overview */}
diff --git a/web/components/issues/issue-layouts/roots/archived-issue-layout-root.tsx b/web/components/issues/issue-layouts/roots/archived-issue-layout-root.tsx index 430383a9f2..1a78a87295 100644 --- a/web/components/issues/issue-layouts/roots/archived-issue-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/archived-issue-layout-root.tsx @@ -13,7 +13,7 @@ import { } from "components/issues"; import { EIssuesStoreType } from "constants/issue"; // ui -import { Spinner } from "@plane/ui"; +import { ListLayoutLoader } from "components/ui"; export const ArchivedIssueLayoutRoot: React.FC = observer(() => { // router @@ -36,31 +36,26 @@ export const ArchivedIssueLayoutRoot: React.FC = observer(() => { } ); + if (issues?.loader === "init-loader" || !issues?.groupedIssueIds) { + return ; + } + + if (issues?.groupedIssueIds?.length === 0) { + return ( +
+ +
+ ); + } + if (!workspaceSlug || !projectId) return <>; return (
- - {issues?.loader === "init-loader" ? ( -
- -
- ) : ( - <> - {!issues?.groupedIssueIds ? ( - - ) : ( - <> -
- -
- - {/* peek overview */} - - - )} - - )} +
+ +
+
); }); diff --git a/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx b/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx index 3e07d16fc4..72f496aaa5 100644 --- a/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx @@ -16,6 +16,7 @@ import { IssuePeekOverview, } from "components/issues"; import { TransferIssues, TransferIssuesModal } from "components/cycles"; +import { ActiveLoader } from "components/ui"; // ui import { Spinner } from "@plane/ui"; // constants @@ -53,6 +54,34 @@ export const CycleLayoutRoot: React.FC = observer(() => { const cycleStatus = cycleDetails?.status?.toLocaleLowerCase() ?? "draft"; if (!workspaceSlug || !projectId || !cycleId) return <>; + + if (issues?.loader === "init-loader" || !issues?.groupedIssueIds) { + return ( + <> + {activeLayout ? ( + + ) : ( +
+ +
+ )} + + ); + } + + if (issues?.groupedIssueIds?.length === 0) { + return ( +
+ +
+ ); + } + return ( <> setTransferIssuesModal(false)} isOpen={transferIssuesModal} /> @@ -61,39 +90,21 @@ export const CycleLayoutRoot: React.FC = observer(() => { {cycleStatus === "completed" && setTransferIssuesModal(true)} />} - {issues?.loader === "init-loader" || !issues?.groupedIssueIds ? ( -
- -
- ) : ( - <> - {issues?.groupedIssueIds?.length === 0 ? ( - - ) : ( - <> -
- {activeLayout === "list" ? ( - - ) : activeLayout === "kanban" ? ( - - ) : activeLayout === "calendar" ? ( - - ) : activeLayout === "gantt_chart" ? ( - - ) : activeLayout === "spreadsheet" ? ( - - ) : null} -
- {/* peek overview */} - - - )} - - )} +
+ {activeLayout === "list" ? ( + + ) : activeLayout === "kanban" ? ( + + ) : activeLayout === "calendar" ? ( + + ) : activeLayout === "gantt_chart" ? ( + + ) : activeLayout === "spreadsheet" ? ( + + ) : null} +
+ {/* peek overview */} +
); diff --git a/web/components/issues/issue-layouts/roots/draft-issue-layout-root.tsx b/web/components/issues/issue-layouts/roots/draft-issue-layout-root.tsx index 8071b40e59..6a22b884fd 100644 --- a/web/components/issues/issue-layouts/roots/draft-issue-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/draft-issue-layout-root.tsx @@ -9,6 +9,7 @@ import { DraftIssueAppliedFiltersRoot } from "../filters/applied-filters/roots/d import { DraftIssueListLayout } from "../list/roots/draft-issue-root"; import { ProjectDraftEmptyState } from "../empty-states"; import { IssuePeekOverview } from "components/issues/peek-overview"; +import { ActiveLoader } from "components/ui"; // ui import { Spinner } from "@plane/ui"; import { DraftKanBanLayout } from "../kanban/roots/draft-issue-root"; @@ -39,31 +40,37 @@ export const DraftIssueLayoutRoot: React.FC = observer(() => { const activeLayout = issuesFilter?.issueFilters?.displayFilters?.layout || undefined; if (!workspaceSlug || !projectId) return <>; + + if (issues?.loader === "init-loader" || !issues?.groupedIssueIds) { + return ( + <> + {activeLayout ? ( + + ) : ( +
+ +
+ )} + + ); + } + + if (issues?.groupedIssueIds?.length === 0) { + return ( +
+ +
+ ); + } return (
- {issues?.loader === "init-loader" ? ( -
- -
- ) : ( - <> - {!issues?.groupedIssueIds ? ( - - ) : ( -
- {activeLayout === "list" ? ( - - ) : activeLayout === "kanban" ? ( - - ) : null} - {/* issue peek overview */} - -
- )} - - )} +
+ {activeLayout === "list" ? : activeLayout === "kanban" ? : null} + {/* issue peek overview */} + +
); }); diff --git a/web/components/issues/issue-layouts/roots/module-layout-root.tsx b/web/components/issues/issue-layouts/roots/module-layout-root.tsx index 10db13e49c..12b91a3757 100644 --- a/web/components/issues/issue-layouts/roots/module-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/module-layout-root.tsx @@ -15,6 +15,7 @@ import { ModuleListLayout, ModuleSpreadsheetLayout, } from "components/issues"; +import { ActiveLoader } from "components/ui"; // ui import { Spinner } from "@plane/ui"; // constants @@ -44,46 +45,56 @@ export const ModuleLayoutRoot: React.FC = observer(() => { } ); + if (!workspaceSlug || !projectId || !moduleId) return <>; + const activeLayout = issuesFilter?.issueFilters?.displayFilters?.layout || undefined; - if (!workspaceSlug || !projectId || !moduleId) return <>; + if (issues?.loader === "init-loader" || !issues?.groupedIssueIds) { + return ( + <> + {activeLayout ? ( + + ) : ( +
+ +
+ )} + + ); + } + + if (issues?.groupedIssueIds?.length === 0) { + return ( +
+ +
+ ); + } + return (
- {issues?.loader === "init-loader" || !issues?.groupedIssueIds ? ( -
- -
- ) : ( - <> - {issues?.groupedIssueIds?.length === 0 ? ( - - ) : ( - <> -
- {activeLayout === "list" ? ( - - ) : activeLayout === "kanban" ? ( - - ) : activeLayout === "calendar" ? ( - - ) : activeLayout === "gantt_chart" ? ( - - ) : activeLayout === "spreadsheet" ? ( - - ) : null} -
- {/* peek overview */} - - - )} - - )} +
+ {activeLayout === "list" ? ( + + ) : activeLayout === "kanban" ? ( + + ) : activeLayout === "calendar" ? ( + + ) : activeLayout === "gantt_chart" ? ( + + ) : activeLayout === "spreadsheet" ? ( + + ) : null} +
+ {/* peek overview */} +
); }); diff --git a/web/components/issues/issue-layouts/roots/project-layout-root.tsx b/web/components/issues/issue-layouts/roots/project-layout-root.tsx index ddc1e99179..5f644ce453 100644 --- a/web/components/issues/issue-layouts/roots/project-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/project-layout-root.tsx @@ -17,6 +17,8 @@ import { import { Spinner } from "@plane/ui"; // hooks import { useIssues } from "hooks/store"; +// helpers +import { ActiveLoader } from "components/ui"; // constants import { EIssuesStoreType } from "constants/issue"; @@ -41,49 +43,45 @@ export const ProjectLayoutRoot: FC = observer(() => { const activeLayout = issuesFilter?.issueFilters?.displayFilters?.layout; if (!workspaceSlug || !projectId) return <>; + + if (issues?.loader === "init-loader" || !issues?.groupedIssueIds) { + return <>{activeLayout && }; + } + + if (issues?.groupedIssueIds?.length === 0) { + return ( +
+ +
+ ); + } + return (
- {issues?.loader === "init-loader" || !issues?.groupedIssueIds ? ( -
- -
- ) : ( - <> - {issues?.groupedIssueIds?.length === 0 ? ( -
- -
- ) : ( - <> -
- {/* mutation loader */} - {issues?.loader === "mutation" && ( -
- -
- )} +
+ {/* mutation loader */} + {issues?.loader === "mutation" && ( +
+ +
+ )} + {activeLayout === "list" ? ( + + ) : activeLayout === "kanban" ? ( + + ) : activeLayout === "calendar" ? ( + + ) : activeLayout === "gantt_chart" ? ( + + ) : activeLayout === "spreadsheet" ? ( + + ) : null} +
- {activeLayout === "list" ? ( - - ) : activeLayout === "kanban" ? ( - - ) : activeLayout === "calendar" ? ( - - ) : activeLayout === "gantt_chart" ? ( - - ) : activeLayout === "spreadsheet" ? ( - - ) : null} -
- - {/* peek overview */} - - - )} - - )} + {/* peek overview */} +
); }); diff --git a/web/components/issues/issue-layouts/roots/project-view-layout-root.tsx b/web/components/issues/issue-layouts/roots/project-view-layout-root.tsx index 75ac2bd9ec..b6e2e36a7c 100644 --- a/web/components/issues/issue-layouts/roots/project-view-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/project-view-layout-root.tsx @@ -16,6 +16,7 @@ import { ProjectViewSpreadsheetLayout, } from "components/issues"; import { Spinner } from "@plane/ui"; +import { ActiveLoader } from "components/ui"; // constants import { EIssuesStoreType } from "constants/issue"; // types @@ -63,40 +64,49 @@ export const ProjectViewLayoutRoot: React.FC = observer(() => { const activeLayout = issuesFilter?.issueFilters?.displayFilters?.layout; if (!workspaceSlug || !projectId || !viewId) return <>; + + if (issues?.loader === "init-loader" || !issues?.groupedIssueIds) { + return ( + <> + {activeLayout ? ( + + ) : ( +
+ +
+ )} + + ); + } + + if (issues?.groupedIssueIds?.length === 0) { + return ( +
+ +
+ ); + } + return (
- {issues?.loader === "init-loader" || !issues?.groupedIssueIds ? ( -
- -
- ) : ( - <> - {issues?.groupedIssueIds?.length === 0 ? ( - - ) : ( - <> -
- {activeLayout === "list" ? ( - - ) : activeLayout === "kanban" ? ( - - ) : activeLayout === "calendar" ? ( - - ) : activeLayout === "gantt_chart" ? ( - - ) : activeLayout === "spreadsheet" ? ( - - ) : null} -
+
+ {activeLayout === "list" ? ( + + ) : activeLayout === "kanban" ? ( + + ) : activeLayout === "calendar" ? ( + + ) : activeLayout === "gantt_chart" ? ( + + ) : activeLayout === "spreadsheet" ? ( + + ) : null} +
- {/* peek overview */} - - - )} - - )} + {/* peek overview */} +
); }); diff --git a/web/components/labels/project-setting-label-list.tsx b/web/components/labels/project-setting-label-list.tsx index 57c48cd018..fcd84d70ab 100644 --- a/web/components/labels/project-setting-label-list.tsx +++ b/web/components/labels/project-setting-label-list.tsx @@ -9,8 +9,9 @@ import { DropResult, Droppable, } from "@hello-pangea/dnd"; +import { useTheme } from "next-themes"; // hooks -import { useLabel } from "hooks/store"; +import { useLabel, useUser } from "hooks/store"; import useDraggableInPortal from "hooks/use-draggable-portal"; // components import { @@ -19,13 +20,13 @@ import { ProjectSettingLabelGroup, ProjectSettingLabelItem, } from "components/labels"; +import { EmptyState, getEmptyStateImagePath } from "components/empty-state"; // ui import { Button, Loader } from "@plane/ui"; -import { EmptyState } from "components/common"; -// images -import emptyLabel from "public/empty-state/label.svg"; // types import { IIssueLabel } from "@plane/types"; +// constants +import { PROJECT_SETTINGS_EMPTY_STATE_DETAILS } from "constants/empty-state"; const LABELS_ROOT = "labels.root"; @@ -40,7 +41,10 @@ export const ProjectSettingsLabelList: React.FC = observer(() => { // router const router = useRouter(); const { workspaceSlug, projectId } = router.query; + // theme + const { resolvedTheme } = useTheme(); // store hooks + const { currentUser } = useUser(); const { projectLabels, updateLabelPosition, projectLabelsTree } = useLabel(); // portal const renderDraggable = useDraggableInPortal(); @@ -50,6 +54,10 @@ export const ProjectSettingsLabelList: React.FC = observer(() => { setLabelForm(true); }; + const emptyStateDetail = PROJECT_SETTINGS_EMPTY_STATE_DETAILS["labels"]; + const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light"; + const emptyStateImage = getEmptyStateImagePath("project-settings", "labels", isLightMode); + const onDragEnd = (result: DropResult) => { const { combine, draggableId, destination, source } = result; @@ -94,7 +102,7 @@ export const ProjectSettingsLabelList: React.FC = observer(() => { Add label -
+
{showLabelForm && (
{ )} {projectLabels ? ( projectLabels.length === 0 && !showLabelForm ? ( - newLabel(), - }} - /> +
+ +
) : ( projectLabelsTree && ( { // router @@ -34,23 +35,13 @@ export const ModulesListView: React.FC = observer(() => { const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER; - if (loader) + if (loader || !projectModuleIds) return ( -
- -
- ); - - if (!projectModuleIds) - return ( - - - - - - - - + <> + {modulesView === "list" && } + {modulesView === "grid" && } + {modulesView === "gantt_chart" && } + ); return ( @@ -97,16 +88,15 @@ export const ModulesListView: React.FC = observer(() => { ) : ( { setTrackElement("Module empty state"); commandPaletteStore.toggleCreateModuleModal(true); diff --git a/web/components/notifications/notification-popover.tsx b/web/components/notifications/notification-popover.tsx index a8cdd4264a..8496d1a35b 100644 --- a/web/components/notifications/notification-popover.tsx +++ b/web/components/notifications/notification-popover.tsx @@ -9,7 +9,8 @@ import useOutsideClickDetector from "hooks/use-outside-click-detector"; // components import { EmptyState } from "components/common"; import { SnoozeNotificationModal, NotificationCard, NotificationHeader } from "components/notifications"; -import { Loader, Tooltip } from "@plane/ui"; +import { Tooltip } from "@plane/ui"; +import { NotificationsLoader } from "components/ui"; // images import emptyNotification from "public/empty-state/notification.svg"; // helpers @@ -188,13 +189,7 @@ export const NotificationPopover = observer(() => {
) ) : ( - - - - - - - + )} diff --git a/web/components/page-views/workspace-dashboard.tsx b/web/components/page-views/workspace-dashboard.tsx index 890cce27f9..98e37ceca0 100644 --- a/web/components/page-views/workspace-dashboard.tsx +++ b/web/components/page-views/workspace-dashboard.tsx @@ -14,6 +14,7 @@ import { Spinner } from "@plane/ui"; // constants import { EUserWorkspaceRoles } from "constants/workspace"; import { PRODUCT_TOUR_COMPLETED } from "constants/event-tracker"; +import { WORKSPACE_EMPTY_STATE_DETAILS } from "constants/empty-state"; export const WorkspaceDashboardView = observer(() => { // theme @@ -78,20 +79,18 @@ export const WorkspaceDashboardView = observer(() => { ) : ( { setTrackElement("Dashboard empty state"); toggleCreateProjectModal(true); }, }} comicBox={{ - title: "Everything starts with a project in Plane", - description: "A project could be a product’s roadmap, a marketing campaign, or launching a new car.", + title: WORKSPACE_EMPTY_STATE_DETAILS["dashboard"].comicBox.title, + description: WORKSPACE_EMPTY_STATE_DETAILS["dashboard"].comicBox.description, }} size="lg" disabled={!isEditingAllowed} diff --git a/web/components/pages/pages-list/list-view.tsx b/web/components/pages/pages-list/list-view.tsx index 4b56347369..2a5a1c677b 100644 --- a/web/components/pages/pages-list/list-view.tsx +++ b/web/components/pages/pages-list/list-view.tsx @@ -11,7 +11,7 @@ import { PagesListItem } from "./list-item"; import { Loader } from "@plane/ui"; // constants import { EUserProjectRoles } from "constants/project"; -import { PAGE_EMPTY_STATE_DETAILS } from "constants/page"; +import { PAGE_EMPTY_STATE_DETAILS } from "constants/empty-state"; type IPagesListView = { pageIds: string[]; diff --git a/web/components/pages/pages-list/recent-pages-list.tsx b/web/components/pages/pages-list/recent-pages-list.tsx index e998629c66..71bbf12ace 100644 --- a/web/components/pages/pages-list/recent-pages-list.tsx +++ b/web/components/pages/pages-list/recent-pages-list.tsx @@ -13,6 +13,7 @@ import { Loader } from "@plane/ui"; import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper"; // constants import { EUserProjectRoles } from "constants/project"; +import { PAGE_EMPTY_STATE_DETAILS } from "constants/empty-state"; export const RecentPagesList: FC = observer(() => { // theme @@ -63,11 +64,11 @@ export const RecentPagesList: FC = observer(() => { ) : ( <> commandPaletteStore.toggleCreatePageModal(true), }} size="sm" diff --git a/web/components/profile/profile-issues.tsx b/web/components/profile/profile-issues.tsx index 81c9b141c9..8b11ca5184 100644 --- a/web/components/profile/profile-issues.tsx +++ b/web/components/profile/profile-issues.tsx @@ -7,14 +7,14 @@ import { useTheme } from "next-themes"; import { ProfileIssuesListLayout } from "components/issues/issue-layouts/list/roots/profile-issues-root"; import { ProfileIssuesKanBanLayout } from "components/issues/issue-layouts/kanban/roots/profile-issues-root"; import { IssuePeekOverview, ProfileIssuesAppliedFiltersRoot } from "components/issues"; -import { Spinner } from "@plane/ui"; +import { KanbanLayoutLoader, ListLayoutLoader } from "components/ui"; import { EmptyState, getEmptyStateImagePath } from "components/empty-state"; // hooks import { useIssues, useUser } from "hooks/store"; // constants import { EUserWorkspaceRoles } from "constants/workspace"; -import { PROFILE_EMPTY_STATE_DETAILS } from "constants/profile"; import { EIssuesStoreType } from "constants/issue"; +import { PROFILE_EMPTY_STATE_DETAILS } from "constants/empty-state"; interface IProfileIssuesPage { type: "assigned" | "subscribed" | "created"; @@ -57,38 +57,33 @@ export const ProfileIssuesPage = observer((props: IProfileIssuesPage) => { const isEditingAllowed = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER; + if (!groupedIssueIds || loader === "init-loader") + return <>{activeLayout === "list" ? : }; + + if (groupedIssueIds.length === 0) { + return ( + + ); + } + return ( <> - {loader === "init-loader" ? ( -
- -
- ) : ( - <> - {groupedIssueIds ? ( - <> - -
- {activeLayout === "list" ? ( - - ) : activeLayout === "kanban" ? ( - - ) : null} -
- {/* peek overview */} - - - ) : ( - - )} - - )} + +
+ {activeLayout === "list" ? ( + + ) : activeLayout === "kanban" ? ( + + ) : null} +
+ {/* peek overview */} + ); }); diff --git a/web/components/project/card-list.tsx b/web/components/project/card-list.tsx index e7601ce350..7a5430554a 100644 --- a/web/components/project/card-list.tsx +++ b/web/components/project/card-list.tsx @@ -4,10 +4,11 @@ import { useTheme } from "next-themes"; import { useApplication, useEventTracker, useProject, useUser } from "hooks/store"; // components import { ProjectCard } from "components/project"; -import { Loader } from "@plane/ui"; import { EmptyState, getEmptyStateImagePath } from "components/empty-state"; +import { ProjectsLoader } from "components/ui"; // constants import { EUserWorkspaceRoles } from "constants/workspace"; +import { WORKSPACE_EMPTY_STATE_DETAILS } from "constants/empty-state"; export const ProjectCardList = observer(() => { // theme @@ -26,17 +27,7 @@ export const ProjectCardList = observer(() => { const isEditingAllowed = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER; - if (!workspaceProjectIds) - return ( - - - - - - - - - ); + if (!workspaceProjectIds) return ; return ( <> @@ -59,18 +50,18 @@ export const ProjectCardList = observer(() => { ) : ( { setTrackElement("Project empty state"); commandPaletteStore.toggleCreateProjectModal(true); }, }} comicBox={{ - title: "Everything starts with a project in Plane", - description: "A project could be a product’s roadmap, a marketing campaign, or launching a new car.", + title: WORKSPACE_EMPTY_STATE_DETAILS["projects"].comicBox.title, + description: WORKSPACE_EMPTY_STATE_DETAILS["projects"].comicBox.description, }} size="lg" disabled={!isEditingAllowed} diff --git a/web/components/project/member-list.tsx b/web/components/project/member-list.tsx index fa8008c8f2..d7b4324453 100644 --- a/web/components/project/member-list.tsx +++ b/web/components/project/member-list.tsx @@ -6,7 +6,8 @@ import { useEventTracker, useMember } from "hooks/store"; // components import { ProjectMemberListItem, SendProjectInvitationModal } from "components/project"; // ui -import { Button, Loader } from "@plane/ui"; +import { Button } from "@plane/ui"; +import { MembersSettingsLoader } from "components/ui"; export const ProjectMemberList: React.FC = observer(() => { // states @@ -56,12 +57,7 @@ export const ProjectMemberList: React.FC = observer(() => {
{!projectMemberIds ? ( - - - - - - + ) : (
{projectMemberIds.length > 0 diff --git a/web/components/ui/index.ts b/web/components/ui/index.ts index 6f1bba3ed7..3a867b18c3 100644 --- a/web/components/ui/index.ts +++ b/web/components/ui/index.ts @@ -7,3 +7,4 @@ export * from "./markdown-to-component"; export * from "./integration-and-import-export-banner"; export * from "./range-datepicker"; export * from "./profile-empty-state"; +export * from "./loader"; diff --git a/web/components/ui/loader/cycle-module-board-loader.tsx b/web/components/ui/loader/cycle-module-board-loader.tsx new file mode 100644 index 0000000000..09c885fb92 --- /dev/null +++ b/web/components/ui/loader/cycle-module-board-loader.tsx @@ -0,0 +1,38 @@ +export const CycleModuleBoardLayout = () => ( +
+
+
+ {[...Array(5)].map(() => ( +
+
+ +
+ + +
+
+
+
+
+ + +
+ +
+ +
+
+ +
+
+ + +
+
+
+
+ ))} +
+
+
+); diff --git a/web/components/ui/loader/cycle-module-list-loader.tsx b/web/components/ui/loader/cycle-module-list-loader.tsx new file mode 100644 index 0000000000..8787a14254 --- /dev/null +++ b/web/components/ui/loader/cycle-module-list-loader.tsx @@ -0,0 +1,28 @@ +export const CycleModuleListLayout = () => ( +
+
+
+ {[...Array(5)].map(() => ( +
+
+
+
+ + +
+
+ +
+
+
+ + + +
+
+
+ ))} +
+
+
+); diff --git a/web/components/ui/loader/index.ts b/web/components/ui/loader/index.ts new file mode 100644 index 0000000000..c36666583f --- /dev/null +++ b/web/components/ui/loader/index.ts @@ -0,0 +1,9 @@ +export * from "./layouts"; +export * from "./settings"; +export * from "./pages-loader"; +export * from "./notification-loader"; +export * from "./cycle-module-board-loader"; +export * from "./cycle-module-list-loader"; +export * from "./view-list-loader"; +export * from "./projects-loader"; +export * from "./utils"; diff --git a/web/components/ui/loader/layouts/calendar-layout-loader.tsx b/web/components/ui/loader/layouts/calendar-layout-loader.tsx new file mode 100644 index 0000000000..db7bc40665 --- /dev/null +++ b/web/components/ui/loader/layouts/calendar-layout-loader.tsx @@ -0,0 +1,48 @@ +import { getRandomInt } from "../utils"; + +const CalendarDay = () => { + const dataCount = getRandomInt(0, 1); + const dataBlocks = Array.from({ length: dataCount }, (_, index) => ( + + )); + + return ( +
+
+ +
+
{dataBlocks}
+
+ ); +}; + +export const CalendarLayoutLoader = () => ( +
+
+
+ + +
+
+ + +
+
+ + {[...Array(5)].map((_, index) => ( + + ))} + +
+
+ {[...Array(6)].map((_, index) => ( +
+ {[...Array(5)].map((_, index) => ( + + ))} +
+ ))} +
+
+
+); diff --git a/web/components/ui/loader/layouts/gantt-layout-loader.tsx b/web/components/ui/loader/layouts/gantt-layout-loader.tsx new file mode 100644 index 0000000000..fd50638f67 --- /dev/null +++ b/web/components/ui/loader/layouts/gantt-layout-loader.tsx @@ -0,0 +1,50 @@ +import { getRandomLength } from "../utils"; + +export const GanttLayoutLoader = () => ( +
+
+ +
+
+
+
+
+ + +
+
+
+ {[...Array(6)].map((_, index) => ( +
+ + +
+ ))} +
+
+
+
+
+ +
+
+ {[...Array(15)].map((_, index) => ( + + ))} +
+
+
+ {[...Array(6)].map((_, index) => ( +
+ +
+ ))} +
+
+
+
+); diff --git a/web/components/ui/loader/layouts/index.ts b/web/components/ui/loader/layouts/index.ts new file mode 100644 index 0000000000..72d2f9e44a --- /dev/null +++ b/web/components/ui/loader/layouts/index.ts @@ -0,0 +1,5 @@ +export * from "./list-layout-loader"; +export * from "./kanban-layout-loader"; +export * from "./calendar-layout-loader"; +export * from "./spreadsheet-layout-loader"; +export * from "./gantt-layout-loader"; diff --git a/web/components/ui/loader/layouts/kanban-layout-loader.tsx b/web/components/ui/loader/layouts/kanban-layout-loader.tsx new file mode 100644 index 0000000000..b949c1b73c --- /dev/null +++ b/web/components/ui/loader/layouts/kanban-layout-loader.tsx @@ -0,0 +1,18 @@ +export const KanbanLayoutLoader = ({ cardsInEachColumn = [2, 3, 2, 4, 3] }: { cardsInEachColumn?: number[] }) => ( +
+ {cardsInEachColumn.map((cardsInColumn, columnIndex) => ( +
+
+
+ + +
+ +
+ {Array.from({ length: cardsInColumn }, (_, cardIndex) => ( + + ))} +
+ ))} +
+); diff --git a/web/components/ui/loader/layouts/list-layout-loader.tsx b/web/components/ui/loader/layouts/list-layout-loader.tsx new file mode 100644 index 0000000000..9b861cc971 --- /dev/null +++ b/web/components/ui/loader/layouts/list-layout-loader.tsx @@ -0,0 +1,45 @@ +import { getRandomInt, getRandomLength } from "../utils"; + +const ListItemRow = () => ( +
+
+ + +
+
+ {[...Array(6)].map((_, index) => ( + <> + {getRandomInt(1, 2) % 2 === 0 ? ( + + ) : ( + + )} + + ))} +
+
+); + +const ListSection = ({ itemCount }: { itemCount: number }) => ( +
+
+
+ + +
+
+
+ {[...Array(itemCount)].map((_, index) => ( + + ))} +
+
+); + +export const ListLayoutLoader = () => ( +
+ {[6, 5, 2].map((itemCount, index) => ( + + ))} +
+); diff --git a/web/components/ui/loader/layouts/spreadsheet-layout-loader.tsx b/web/components/ui/loader/layouts/spreadsheet-layout-loader.tsx new file mode 100644 index 0000000000..3b9e9bc653 --- /dev/null +++ b/web/components/ui/loader/layouts/spreadsheet-layout-loader.tsx @@ -0,0 +1,38 @@ +import { getRandomLength } from "../utils"; + +export const SpreadsheetLayoutLoader = () => ( +
+ + + + + + + {[...Array(16)].map((_, rowIndex) => ( + + + {[...Array(10)].map((_, colIndex) => ( + + ))} + + ))} + +
+ {[...Array(10)].map((_, index) => ( + + ))} +
+
+ + +
+
+
+ +
+
+
+); diff --git a/web/components/ui/loader/notification-loader.tsx b/web/components/ui/loader/notification-loader.tsx new file mode 100644 index 0000000000..143f1a9b64 --- /dev/null +++ b/web/components/ui/loader/notification-loader.tsx @@ -0,0 +1,16 @@ +export const NotificationsLoader = () => ( +
+ {[...Array(3)].map(() => ( +
+ +
+ +
+ + +
+
+
+ ))} +
+); diff --git a/web/components/ui/loader/pages-loader.tsx b/web/components/ui/loader/pages-loader.tsx new file mode 100644 index 0000000000..e31e829421 --- /dev/null +++ b/web/components/ui/loader/pages-loader.tsx @@ -0,0 +1,28 @@ +export const PagesLoader = () => ( +
+
+

Pages

+
+
+ {[...Array(5)].map(() => ( + + ))} +
+
+ {[...Array(5)].map(() => ( +
+
+ + +
+
+ + + + +
+
+ ))} +
+
+); diff --git a/web/components/ui/loader/projects-loader.tsx b/web/components/ui/loader/projects-loader.tsx new file mode 100644 index 0000000000..9548a1f483 --- /dev/null +++ b/web/components/ui/loader/projects-loader.tsx @@ -0,0 +1,34 @@ +export const ProjectsLoader = () => ( +
+
+ {[...Array(3)].map(() => ( +
+
+
+
+
+ +
+ + +
+
+
+ + +
+
+
+
+
+ +
+ + +
+
+
+ ))} +
+
+); diff --git a/web/components/ui/loader/settings/activity.tsx b/web/components/ui/loader/settings/activity.tsx new file mode 100644 index 0000000000..7bc5c392f4 --- /dev/null +++ b/web/components/ui/loader/settings/activity.tsx @@ -0,0 +1,12 @@ +import { getRandomLength } from "../utils"; + +export const ActivitySettingsLoader = () => ( +
+ {[...Array(10)].map(() => ( +
+ + +
+ ))} +
+); diff --git a/web/components/ui/loader/settings/api-token.tsx b/web/components/ui/loader/settings/api-token.tsx new file mode 100644 index 0000000000..fc5b4c41d6 --- /dev/null +++ b/web/components/ui/loader/settings/api-token.tsx @@ -0,0 +1,19 @@ +export const APITokenSettingsLoader = () => ( +
+
+

API tokens

+ +
+
+ {[...Array(2)].map(() => ( +
+
+ + +
+ +
+ ))} +
+
+); diff --git a/web/components/ui/loader/settings/email.tsx b/web/components/ui/loader/settings/email.tsx new file mode 100644 index 0000000000..fa68b972f5 --- /dev/null +++ b/web/components/ui/loader/settings/email.tsx @@ -0,0 +1,27 @@ +export const EmailSettingsLoader = () => ( +
+
+ + +
+
+
+ +
+ {[...Array(4)].map(() => ( +
+
+ + +
+
+ +
+
+ ))} +
+ +
+
+
+); diff --git a/web/components/ui/loader/settings/import-and-export.tsx b/web/components/ui/loader/settings/import-and-export.tsx new file mode 100644 index 0000000000..70496d1c19 --- /dev/null +++ b/web/components/ui/loader/settings/import-and-export.tsx @@ -0,0 +1,18 @@ +export const ImportExportSettingsLoader = () => ( +
+ {[...Array(2)].map(() => ( +
+
+
+ + +
+
+ + +
+
+
+ ))} +
+); diff --git a/web/components/ui/loader/settings/index.ts b/web/components/ui/loader/settings/index.ts new file mode 100644 index 0000000000..8b73cd98dd --- /dev/null +++ b/web/components/ui/loader/settings/index.ts @@ -0,0 +1,7 @@ +export * from "./activity"; +export * from "./api-token"; +export * from "./email"; +export * from "./integration"; +export * from "./members"; +export * from "./web-hook"; +export * from "./import-and-export"; diff --git a/web/components/ui/loader/settings/integration.tsx b/web/components/ui/loader/settings/integration.tsx new file mode 100644 index 0000000000..871b570b19 --- /dev/null +++ b/web/components/ui/loader/settings/integration.tsx @@ -0,0 +1,16 @@ +export const IntegrationsSettingsLoader = () => ( +
+ {[...Array(2)].map(() => ( +
+
+ +
+ + +
+
+ +
+ ))} +
+); diff --git a/web/components/ui/loader/settings/members.tsx b/web/components/ui/loader/settings/members.tsx new file mode 100644 index 0000000000..3ed2c41efc --- /dev/null +++ b/web/components/ui/loader/settings/members.tsx @@ -0,0 +1,16 @@ +export const MembersSettingsLoader = () => ( +
+ {[...Array(4)].map(() => ( +
+
+ +
+ + +
+
+ +
+ ))} +
+); diff --git a/web/components/ui/loader/settings/web-hook.tsx b/web/components/ui/loader/settings/web-hook.tsx new file mode 100644 index 0000000000..87ddbd19a0 --- /dev/null +++ b/web/components/ui/loader/settings/web-hook.tsx @@ -0,0 +1,20 @@ +export const WebhookSettingsLoader = () => ( +
+
+
+
Webhooks
+ +
+
+
+
+ + + + +
+
+
+
+
+); diff --git a/web/components/ui/loader/utils.tsx b/web/components/ui/loader/utils.tsx new file mode 100644 index 0000000000..312df038e4 --- /dev/null +++ b/web/components/ui/loader/utils.tsx @@ -0,0 +1,35 @@ +import { + CalendarLayoutLoader, + GanttLayoutLoader, + KanbanLayoutLoader, + ListLayoutLoader, + SpreadsheetLayoutLoader, +} from "./layouts"; + +export const getRandomInt = (min: number, max: number) => Math.floor(Math.random() * (max - min + 1)) + min; + +export const getRandomLength = (lengthArray: string[]) => { + const randomIndex = Math.floor(Math.random() * lengthArray.length); + return `${lengthArray[randomIndex]}`; +}; + +interface Props { + layout: string; +} +export const ActiveLoader: React.FC = (props) => { + const { layout } = props; + switch (layout) { + case "list": + return ; + case "kanban": + return ; + case "spreadsheet": + return ; + case "calendar": + return ; + case "gantt_chart": + return ; + default: + return ; + } +}; diff --git a/web/components/ui/loader/view-list-loader.tsx b/web/components/ui/loader/view-list-loader.tsx new file mode 100644 index 0000000000..97899a6576 --- /dev/null +++ b/web/components/ui/loader/view-list-loader.tsx @@ -0,0 +1,18 @@ +export const ViewListLoader = () => ( +
+ {[...Array(8)].map(() => ( +
+
+
+ + +
+
+ + +
+
+
+ ))} +
+); diff --git a/web/components/views/views-list.tsx b/web/components/views/views-list.tsx index 13ad104419..902193dba6 100644 --- a/web/components/views/views-list.tsx +++ b/web/components/views/views-list.tsx @@ -8,9 +8,11 @@ import { useApplication, useProjectView, useUser } from "hooks/store"; import { ProjectViewListItem } from "components/views"; import { EmptyState, getEmptyStateImagePath } from "components/empty-state"; // ui -import { Input, Loader, Spinner } from "@plane/ui"; +import { Input } from "@plane/ui"; +import { ViewListLoader } from "components/ui"; // constants import { EUserProjectRoles } from "constants/project"; +import { VIEW_EMPTY_STATE_DETAILS } from "constants/empty-state"; export const ProjectViewsList = observer(() => { // states @@ -27,22 +29,7 @@ export const ProjectViewsList = observer(() => { } = useUser(); const { projectViewIds, getViewById, loader } = useProjectView(); - if (loader) - return ( -
- -
- ); - - if (!projectViewIds) - return ( - - - - - - - ); + if (loader || !projectViewIds) return ; const viewsList = projectViewIds.map((viewId) => getViewById(viewId)); @@ -77,15 +64,15 @@ export const ProjectViewsList = observer(() => {
) : ( toggleCreateViewModal(true), }} size="lg" diff --git a/web/components/workspace/settings/members-list.tsx b/web/components/workspace/settings/members-list.tsx index 97f253afc6..1dc02d508e 100644 --- a/web/components/workspace/settings/members-list.tsx +++ b/web/components/workspace/settings/members-list.tsx @@ -7,7 +7,7 @@ import { useMember } from "hooks/store"; // components import { WorkspaceInvitationsListItem, WorkspaceMembersListItem } from "components/workspace"; // ui -import { Loader } from "@plane/ui"; +import { MembersSettingsLoader } from "components/ui"; export const WorkspaceMembersList: FC<{ searchQuery: string }> = observer((props) => { const { searchQuery } = props; @@ -30,15 +30,7 @@ export const WorkspaceMembersList: FC<{ searchQuery: string }> = observer((props workspaceSlug ? () => fetchWorkspaceMemberInvitations(workspaceSlug.toString()) : null ); - if (!workspaceMemberIds && !workspaceMemberInvitationIds) - return ( - - - - - - - ); + if (!workspaceMemberIds && !workspaceMemberInvitationIds) return ; // derived values const searchedMemberIds = getSearchedWorkspaceMemberIds(searchQuery); diff --git a/web/components/workspace/views/views-list.tsx b/web/components/workspace/views/views-list.tsx index 6a04374b22..9a8758d2dc 100644 --- a/web/components/workspace/views/views-list.tsx +++ b/web/components/workspace/views/views-list.tsx @@ -6,7 +6,7 @@ import { useGlobalView } from "hooks/store"; // components import { GlobalViewListItem } from "components/workspace"; // ui -import { Loader } from "@plane/ui"; +import { ViewListLoader } from "components/ui"; type Props = { searchQuery: string; @@ -25,15 +25,7 @@ export const GlobalViewsList: React.FC = observer((props) => { workspaceSlug ? () => fetchAllGlobalViews(workspaceSlug.toString()) : null ); - if (!currentWorkspaceViews) - return ( - - - - - - - ); + if (!currentWorkspaceViews) return ; const filteredViewsList = getSearchedViews(searchQuery); diff --git a/web/constants/cycle.ts b/web/constants/cycle.ts index 79b2fc272e..ba7c86c910 100644 --- a/web/constants/cycle.ts +++ b/web/constants/cycle.ts @@ -163,30 +163,6 @@ export const WORKSPACE_ACTIVE_CYCLES_DETAILS = [ }, ]; -export const CYCLE_EMPTY_STATE_DETAILS = { - active: { - key: "active", - title: "No active cycles", - description: - "An active cycle includes any period that encompasses today's date within its range. Find the progress and details of the active cycle here.", - }, - upcoming: { - key: "upcoming", - title: "No upcoming cycles", - description: "Upcoming cycles on deck! Just add dates to cycles in draft, and they'll show up right here.", - }, - completed: { - key: "completed", - title: "No completed cycles", - description: "Any cycle with a past due date is considered completed. Explore all completed cycles here.", - }, - draft: { - key: "draft", - title: "No draft cycles", - description: "No dates added in cycles? Find them here as drafts.", - }, -}; - // ee export const WORKSPACE_ACTIVE_CYCLE_STATE_GROUPS_DETAILS = [ { diff --git a/web/constants/empty-state.ts b/web/constants/empty-state.ts new file mode 100644 index 0000000000..eaf7f4b05f --- /dev/null +++ b/web/constants/empty-state.ts @@ -0,0 +1,366 @@ +// workspace empty state +export const WORKSPACE_EMPTY_STATE_DETAILS = { + dashboard: { + title: "Overview of your projects, activity, and metrics", + description: + " Welcome to Plane, we are excited to have you here. Create your first project and track your issues, and this page will transform into a space that helps you progress. Admins will also see items which help their team progress.", + primaryButton: { + text: "Build your first project", + }, + comicBox: { + title: "Everything starts with a project in Plane", + description: "A project could be a product’s roadmap, a marketing campaign, or launching a new car.", + }, + }, + analytics: { + title: "Track progress, workloads, and allocations. Spot trends, remove blockers, and move work faster", + description: + "See scope versus demand, estimates, and scope creep. Get performance by team members and teams, and make sure your project runs on time.", + primaryButton: { + text: "Create Cycles and Modules first", + }, + comicBox: { + title: "Analytics works best with Cycles + Modules", + description: + "First, timebox your issues into Cycles and, if you can, group issues that span more than a cycle into Modules. Check out both on the left nav.", + }, + }, + projects: { + title: "Start a Project", + description: + "Think of each project as the parent for goal-oriented work. Projects are where Jobs, Cycles, and Modules live and, along with your colleagues, help you achieve that goal.", + primaryButton: { + text: "Start your first project", + }, + comicBox: { + title: "Everything starts with a project in Plane", + description: "A project could be a product’s roadmap, a marketing campaign, or launching a new car.", + }, + }, + "assigned-notification": { + key: "assigned-notification", + title: "No issues assigned", + description: "Updates for issues assigned to you can be seen here", + }, + "created-notification": { + key: "created-notification", + title: "No updates to issues", + description: "Updates to issues created by you can be seen here", + }, + "subscribed-notification": { + key: "subscribed-notification", + title: "No updates to issues", + description: "Updates to any issue you are subscribed to can be seen here", + }, +}; + +export const ALL_ISSUES_EMPTY_STATE_DETAILS = { + "all-issues": { + key: "all-issues", + title: "No issues in the project", + description: "First project done! Now, slice your work into trackable pieces with issues. Let's go!", + }, + assigned: { + key: "assigned", + title: "No issues yet", + description: "Issues assigned to you can be tracked from here.", + }, + created: { + key: "created", + title: "No issues yet", + description: "All issues created by you come here, track them here directly.", + }, + subscribed: { + key: "subscribed", + title: "No issues yet", + description: "Subscribe to issues you are interested in, track all of them here.", + }, + "custom-view": { + key: "custom-view", + title: "No issues yet", + description: "Issues that applies to the filters, track all of them here.", + }, +}; + +export const SEARCH_EMPTY_STATE_DETAILS = { + views: { + key: "views", + title: "No matching views", + description: "No views match the search criteria. Create a new view instead.", + }, + projects: { + key: "projects", + title: "No matching projects", + description: "No projects detected with the matching criteria. Create a new project instead.", + }, + commandK: { + key: "commandK", + title: "No results found. ", + }, + members: { + key: "members", + title: "No matching members", + description: "Add them to the project if they are already a part of the workspace", + }, +}; + +export const WORKSPACE_SETTINGS_EMPTY_STATE_DETAILS = { + "api-tokens": { + key: "api-tokens", + title: "No API tokens created", + description: + "Plane APIs can be used to integrate your data in Plane with any external system. Create a token to get started.", + }, + webhooks: { + key: "webhooks", + title: "No webhooks added", + description: "Create webhooks to receive real-time updates and automate actions.", + }, + export: { + key: "export", + title: "No previous exports yet", + description: "Anytime you export, you will also have a copy here for reference.", + }, + import: { + key: "export", + title: "No previous imports yet", + description: "Find all your previous imports here and download them.", + }, +}; + +// profile empty state +export const PROFILE_EMPTY_STATE_DETAILS = { + assigned: { + key: "assigned", + title: "No issues are assigned to you", + description: "Issues assigned to you can be tracked from here.", + }, + subscribed: { + key: "created", + title: "No issues yet", + description: "All issues created by you come here, track them here directly.", + }, + created: { + key: "subscribed", + title: "No issues yet", + description: "Subscribe to issues you are interested in, track all of them here.", + }, +}; + +// project empty state + +export const PROJECT_SETTINGS_EMPTY_STATE_DETAILS = { + labels: { + key: "labels", + title: "No labels yet", + description: "Create labels to help organize and filter issues in you project.", + }, + integrations: { + key: "integrations", + title: "No integrations configured", + description: "Configure GitHub and other integrations to sync your project issues.", + }, + estimate: { + key: "estimate", + title: "No estimates added", + description: "Create a set of estimates to communicate the amount of work per issue.", + }, +}; + +export const CYCLE_EMPTY_STATE_DETAILS = { + cycles: { + title: "Group and timebox your work in Cycles.", + description: + "Break work down by timeboxed chunks, work backwards from your project deadline to set dates, and make tangible progress as a team.", + comicBox: { + title: "Cycles are repetitive time-boxes.", + description: + "A sprint, an iteration, and or any other term you use for weekly or fortnightly tracking of work is a cycle.", + }, + primaryButton: { + text: "Set your first cycle", + }, + }, + "no-issues": { + key: "no-issues", + title: "No issues added to the cycle", + description: "Add or create issues you wish to timebox and deliver within this cycle", + primaryButton: { + text: "Create new issue ", + }, + secondaryButton: { + text: "Add an existing issue", + }, + }, + active: { + key: "active", + title: "No active cycles", + description: + "An active cycle includes any period that encompasses today's date within its range. Find the progress and details of the active cycle here.", + }, + upcoming: { + key: "upcoming", + title: "No upcoming cycles", + description: "Upcoming cycles on deck! Just add dates to cycles in draft, and they'll show up right here.", + }, + completed: { + key: "completed", + title: "No completed cycles", + description: "Any cycle with a past due date is considered completed. Explore all completed cycles here.", + }, + draft: { + key: "draft", + title: "No draft cycles", + description: "No dates added in cycles? Find them here as drafts.", + }, +}; + +export const EMPTY_FILTER_STATE_DETAILS = { + archived: { + key: "archived", + title: "No issues found matching the filters applied", + secondaryButton: { + text: "Clear all filters", + }, + }, + draft: { + key: "draft", + title: "No issues found matching the filters applied", + secondaryButton: { + text: "Clear all filters", + }, + }, + project: { + key: "project", + title: "No issues found matching the filters applied", + secondaryButton: { + text: "Clear all filters", + }, + }, +}; + +export const EMPTY_ISSUE_STATE_DETAILS = { + archived: { + key: "archived", + 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.", + primaryButton: { + text: "Set Automation", + }, + }, + draft: { + key: "draft", + 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.", + }, + project: { + key: "project", + title: "Create an issue and assign it to someone, even yourself", + description: + "Think of issues as jobs, tasks, work, or JTBD. Which we like. An issue and its sub-issues are usually time-based actionables assigned to members of your team. Your team creates, assigns, and completes issues to move your project towards its goal.", + comicBox: { + title: "Issues are building blocks in Plane.", + description: + "Redesign the Plane UI, Rebrand the company, or Launch the new fuel injection system are examples of issues that likely have sub-issues.", + }, + primaryButton: { + text: "Create your first issue", + }, + }, +}; + +export const MODULE_EMPTY_STATE_DETAILS = { + "no-issues": { + key: "no-issues", + title: "No issues in the module", + description: "Create or add issues which you want to accomplish as part of this module", + primaryButton: { + text: "Create new issue ", + }, + secondaryButton: { + text: "Add an existing issue", + }, + }, + modules: { + title: "Map your project milestones to Modules and track aggregated work easily.", + description: + "A group of issues that belong to a logical, hierarchical parent form a module. Think of them as a way to track work by project milestones. They have their own periods and deadlines as well as analytics to help you see how close or far you are from a milestone.", + + comicBox: { + title: "Modules help group work by hierarchy.", + description: "A cart module, a chassis module, and a warehouse module are all good example of this grouping.", + }, + primaryButton: { + text: "Build your first module", + }, + }, +}; + +export const VIEW_EMPTY_STATE_DETAILS = { + "project-views": { + title: "Save filtered views for your project. Create as many as you need", + description: + "Views are a set of saved filters that you use frequently or want easy access to. All your colleagues in a project can see everyone’s views and choose whichever suits their needs best.", + comicBox: { + title: "Views work atop Issue properties.", + description: "You can create a view from here with as many properties as filters as you see fit.", + }, + primaryButton: { + text: "Create your first view", + }, + }, +}; + +export const PAGE_EMPTY_STATE_DETAILS = { + pages: { + key: "pages", + title: "Write a note, a doc, or a full knowledge base. Get Galileo, Plane’s AI assistant, to help you get started", + description: + "Pages are thoughts potting space in Plane. Take down meeting notes, format them easily, embed issues, lay them out using a library of components, and keep them all in your project’s context. To make short work of any doc, invoke Galileo, Plane’s AI, with a shortcut or the click of a button.", + primaryButton: { + text: "Create your first page", + }, + comicBox: { + title: "A page can be a doc or a doc of docs.", + description: + "We wrote Nikhil and Meera’s love story. You could write your project’s mission, goals, and eventual vision.", + }, + }, + All: { + key: "all", + title: "Write a note, a doc, or a full knowledge base", + description: + "Pages help you organise your thoughts to create wikis, discussions or even document heated takes for your project. Use it wisely!", + }, + Favorites: { + key: "favorites", + title: "No favorite pages yet", + description: "Favorites for quick access? mark them and find them right here.", + }, + Private: { + key: "private", + title: "No private pages yet", + description: "Keep your private thoughts here. When you're ready to share, the team's just a click away.", + }, + Shared: { + key: "shared", + title: "No shared pages yet", + description: "See pages shared with everyone in your project right here.", + }, + Archived: { + key: "archived", + title: "No archived pages yet", + description: "Archive pages not on your radar. Access them here when needed.", + }, + Recent: { + key: "recent", + title: "Write a note, a doc, or a full knowledge base", + description: + "Pages help you organise your thoughts to create wikis, discussions or even document heated takes for your project. Use it wisely! Pages will be sorted and grouped by last updated", + primaryButton: { + text: "Create new page", + }, + }, +}; diff --git a/web/constants/page.ts b/web/constants/page.ts index 4183d46d18..4b303ae73b 100644 --- a/web/constants/page.ts +++ b/web/constants/page.ts @@ -52,38 +52,3 @@ export const PAGE_ACCESS_SPECIFIERS: { key: number; label: string; icon: any }[] icon: Lock, }, ]; - -export const PAGE_EMPTY_STATE_DETAILS = { - All: { - key: "all", - title: "Write a note, a doc, or a full knowledge base", - description: - "Pages help you organise your thoughts to create wikis, discussions or even document heated takes for your project. Use it wisely!", - }, - Favorites: { - key: "favorites", - title: "No favorite pages yet", - description: "Favorites for quick access? mark them and find them right here.", - }, - Private: { - key: "private", - title: "No private pages yet", - description: "Keep your private thoughts here. When you're ready to share, the team's just a click away.", - }, - Shared: { - key: "shared", - title: "No shared pages yet", - description: "See pages shared with everyone in your project right here.", - }, - Archived: { - key: "archived", - title: "No archived pages yet", - description: "Archive pages not on your radar. Access them here when needed.", - }, - Recent: { - key: "recent", - title: "Write a note, a doc, or a full knowledge base", - description: - "Pages help you organise your thoughts to create wikis, discussions or even document heated takes for your project. Use it wisely! Pages will be sorted and grouped by last updated", - }, -}; diff --git a/web/constants/profile.ts b/web/constants/profile.ts index 0fffdbc9b2..463fd27eed 100644 --- a/web/constants/profile.ts +++ b/web/constants/profile.ts @@ -64,21 +64,3 @@ export const PROFILE_ADMINS_TAB = [ selected: "/[workspaceSlug]/profile/[userId]/subscribed", }, ]; - -export const PROFILE_EMPTY_STATE_DETAILS = { - assigned: { - key: "assigned", - title: "No issues are assigned to you", - description: "Issues assigned to you can be tracked from here.", - }, - subscribed: { - key: "created", - title: "No issues yet", - description: "All issues created by you come here, track them here directly.", - }, - created: { - key: "subscribed", - title: "No issues yet", - description: "Subscribe to issues you are interested in, track all of them here.", - }, -}; diff --git a/web/constants/workspace.ts b/web/constants/workspace.ts index 4e7a0d6ae0..1471de3958 100644 --- a/web/constants/workspace.ts +++ b/web/constants/workspace.ts @@ -190,31 +190,3 @@ export const WORKSPACE_SETTINGS_LINKS: { Icon: SettingIcon, }, ]; - -export const ALL_ISSUES_EMPTY_STATE_DETAILS = { - "all-issues": { - key: "all-issues", - title: "No issues in the project", - description: "First project done! Now, slice your work into trackable pieces with issues. Let's go!", - }, - assigned: { - key: "assigned", - title: "No issues yet", - description: "Issues assigned to you can be tracked from here.", - }, - created: { - key: "created", - title: "No issues yet", - description: "All issues created by you come here, track them here directly.", - }, - subscribed: { - key: "subscribed", - title: "No issues yet", - description: "Subscribe to issues you are interested in, track all of them here.", - }, - "custom-view": { - key: "custom-view", - title: "No issues yet", - description: "Issues that applies to the filters, track all of them here.", - }, -}; diff --git a/web/layouts/app-layout/sidebar.tsx b/web/layouts/app-layout/sidebar.tsx index 0ccbf26065..8477525d4a 100644 --- a/web/layouts/app-layout/sidebar.tsx +++ b/web/layouts/app-layout/sidebar.tsx @@ -1,4 +1,4 @@ -import { FC, useEffect, useRef } from "react"; +import { FC, useRef } from "react"; import { observer } from "mobx-react-lite"; // components import { @@ -12,7 +12,7 @@ import { ProjectSidebarList } from "components/project"; import { useApplication } from "hooks/store"; import useOutsideClickDetector from "hooks/use-outside-click-detector"; -export interface IAppSidebar { } +export interface IAppSidebar {} export const AppSidebar: FC = observer(() => { // store hooks @@ -20,35 +20,19 @@ export const AppSidebar: FC = observer(() => { const ref = useRef(null); useOutsideClickDetector(ref, () => { - if (themStore.sidebarCollapsed === false) { + if (themStore.mobileSidebarCollapsed === false) { if (window.innerWidth < 768) { - themStore.toggleSidebar(); + themStore.toggleMobileSidebar(); } } }); - useEffect(() => { - const handleResize = () => { - if (window.innerWidth <= 768) { - themStore.toggleSidebar(true); - } - if (window.innerWidth > 768) { - themStore.toggleSidebar(false); - } - }; - handleResize(); - window.addEventListener("resize", handleResize); - return () => { - window.removeEventListener("resize", handleResize); - }; - }, [themStore]); - return (
{ - const router = useRouter() - const { analytics_tab } = router.query + const router = useRouter(); + const { analytics_tab } = router.query; // theme const { resolvedTheme } = useTheme(); // store hooks @@ -41,18 +42,21 @@ const AnalyticsPage: NextPageWithLayout = observer(() => { <> {workspaceProjectIds && workspaceProjectIds.length > 0 ? (
- + {ANALYTICS_TABS.map((tab) => ( - `rounded-0 w-full md:w-max md:rounded-3xl border-b md:border border-custom-border-200 focus:outline-none px-0 md:px-4 py-2 text-xs hover:bg-custom-background-80 ${selected ? "border-custom-primary-100 text-custom-primary-100 md:bg-custom-background-80 md:text-custom-text-200 md:border-custom-border-200" : "border-transparent" + `rounded-0 w-full md:w-max md:rounded-3xl border-b md:border border-custom-border-200 focus:outline-none px-0 md:px-4 py-2 text-xs hover:bg-custom-background-80 ${ + selected + ? "border-custom-primary-100 text-custom-primary-100 md:bg-custom-background-80 md:text-custom-text-200 md:border-custom-border-200" + : "border-transparent" }` } onClick={() => { - router.query.analytics_tab = tab.key - router.push(router) + router.query.analytics_tab = tab.key; + router.push(router); }} > {tab.title} @@ -72,19 +76,18 @@ const AnalyticsPage: NextPageWithLayout = observer(() => { ) : ( { setTrackElement("Analytics empty state"); toggleCreateProjectModal(true); }, }} comicBox={{ - title: "Analytics works best with Cycles + Modules", - description: - "First, timebox your issues into Cycles and, if you can, group issues that span more than a cycle into Modules. Check out both on the left nav.", + title: WORKSPACE_EMPTY_STATE_DETAILS["analytics"].comicBox.title, + description: WORKSPACE_EMPTY_STATE_DETAILS["analytics"].comicBox.description, }} size="lg" disabled={!isEditingAllowed} diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx index 0541dfce41..0b9af62fd2 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx @@ -13,13 +13,15 @@ import { CyclesHeader } from "components/headers"; import { CyclesView, ActiveCycleDetails, CycleCreateUpdateModal } from "components/cycles"; import { EmptyState, getEmptyStateImagePath } from "components/empty-state"; // ui -import { Spinner, Tooltip } from "@plane/ui"; +import { Tooltip } from "@plane/ui"; +import { CycleModuleBoardLayout, CycleModuleListLayout, GanttLayoutLoader } from "components/ui"; // types import { TCycleView, TCycleLayout } from "@plane/types"; import { NextPageWithLayout } from "lib/types"; // constants import { CYCLE_TAB_LIST, CYCLE_VIEW_LAYOUTS } from "constants/cycle"; import { EUserWorkspaceRoles } from "constants/workspace"; +import { CYCLE_EMPTY_STATE_DETAILS } from "constants/empty-state"; const ProjectCyclesPage: NextPageWithLayout = observer(() => { const [createModal, setCreateModal] = useState(false); @@ -65,9 +67,11 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => { if (loader) return ( -
- -
+ <> + {cycleLayout === "list" && } + {cycleLayout === "board" && } + {cycleLayout === "gantt" && } + ); return ( @@ -81,16 +85,15 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => { {totalCycles === 0 ? (
{ setTrackElement("Cycle empty state"); setCreateModal(true); @@ -114,7 +117,8 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => { - `border-b-2 p-4 text-sm font-medium outline-none ${selected ? "border-custom-primary-100 text-custom-primary-100" : "border-transparent" + `border-b-2 p-4 text-sm font-medium outline-none ${ + selected ? "border-custom-primary-100 text-custom-primary-100" : "border-transparent" }` } > @@ -132,14 +136,16 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => { diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/pages/index.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/pages/index.tsx index 1c5a08f8dc..5dad4ede01 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/pages/index.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/pages/index.tsx @@ -16,13 +16,14 @@ import { AppLayout } from "layouts/app-layout"; import { RecentPagesList, CreateUpdatePageModal } from "components/pages"; import { EmptyState, getEmptyStateImagePath } from "components/empty-state"; import { PagesHeader } from "components/headers"; -import { Spinner } from "@plane/ui"; +import { PagesLoader } from "components/ui"; // types import { NextPageWithLayout } from "lib/types"; // constants import { PAGE_TABS_LIST } from "constants/page"; import { useProjectPages } from "hooks/store/use-project-page"; import { EUserWorkspaceRoles } from "constants/workspace"; +import { PAGE_EMPTY_STATE_DETAILS } from "constants/empty-state"; const AllPagesList = dynamic(() => import("components/pages").then((a) => a.AllPagesList), { ssr: false, @@ -124,12 +125,7 @@ const ProjectPagesPage: NextPageWithLayout = observer(() => { ); - if (loader || archivedPageLoader) - return ( -
- -
- ); + if (loader || archivedPageLoader) return ; return ( <> @@ -217,19 +213,18 @@ const ProjectPagesPage: NextPageWithLayout = observer(() => { ) : ( { setTrackElement("Pages empty state"); toggleCreatePageModal(true); }, }} comicBox={{ - title: "A page can be a doc or a doc of docs.", - description: - "We wrote Nikhil and Meera’s love story. You could write your project’s mission, goals, and eventual vision.", + title: PAGE_EMPTY_STATE_DETAILS["pages"].comicBox.title, + description: PAGE_EMPTY_STATE_DETAILS["pages"].comicBox.description, }} size="lg" disabled={!isEditingAllowed} diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/settings/estimates.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/settings/estimates.tsx index 2a2322e314..bc90c110fb 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/settings/estimates.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/settings/estimates.tsx @@ -21,7 +21,7 @@ const EstimatesSettingsPage: NextPageWithLayout = observer(() => { const isAdmin = currentProjectRole === EUserProjectRoles.ADMIN; return ( -
+
); diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/settings/integrations.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/settings/integrations.tsx index bc0163464d..a44fbeaa49 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/settings/integrations.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/settings/integrations.tsx @@ -1,6 +1,9 @@ import { ReactElement } from "react"; import { useRouter } from "next/router"; import useSWR from "swr"; +import { useTheme } from "next-themes"; +// hooks +import { useUser } from "hooks/store"; // layouts import { AppLayout } from "layouts/app-layout"; import { ProjectSettingLayout } from "layouts/settings-layout"; @@ -10,16 +13,15 @@ import { ProjectService } from "services/project"; // components import { IntegrationCard } from "components/project"; import { ProjectSettingHeader } from "components/headers"; +import { EmptyState, getEmptyStateImagePath } from "components/empty-state"; // ui -import { EmptyState } from "components/common"; -import { Loader } from "@plane/ui"; -// images -import emptyIntegration from "public/empty-state/integration.svg"; +import { IntegrationsSettingsLoader } from "components/ui"; // types import { IProject } from "@plane/types"; import { NextPageWithLayout } from "lib/types"; // fetch-keys import { PROJECT_DETAILS, WORKSPACE_INTEGRATIONS } from "constants/fetch-keys"; +import { PROJECT_SETTINGS_EMPTY_STATE_DETAILS } from "constants/empty-state"; // services const integrationService = new IntegrationService(); @@ -28,6 +30,10 @@ const projectService = new ProjectService(); const ProjectIntegrationsPage: NextPageWithLayout = () => { const router = useRouter(); const { workspaceSlug, projectId } = router.query; + // theme + const { resolvedTheme } = useTheme(); + // store hooks + const { currentUser } = useUser(); const { data: projectDetails } = useSWR( workspaceSlug && projectId ? PROJECT_DETAILS(projectId as string) : null, @@ -39,10 +45,14 @@ const ProjectIntegrationsPage: NextPageWithLayout = () => { () => (workspaceSlug ? integrationService.getWorkspaceIntegrationsList(workspaceSlug as string) : null) ); + const emptyStateDetail = PROJECT_SETTINGS_EMPTY_STATE_DETAILS["integrations"]; + const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light"; + const emptyStateImage = getEmptyStateImagePath("project-settings", "integrations", isLightMode); + const isAdmin = projectDetails?.member_role === 20; return ( -
+

Integrations

@@ -54,26 +64,22 @@ const ProjectIntegrationsPage: NextPageWithLayout = () => { ))}
) : ( -
+
router.push(`/${workspaceSlug}/settings/integrations`), }} + size="lg" disabled={!isAdmin} />
) ) : ( - - - - - - + )}
); diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/settings/labels.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/settings/labels.tsx index edede146f7..02a700bbee 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/settings/labels.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/settings/labels.tsx @@ -9,7 +9,7 @@ import { ProjectSettingHeader } from "components/headers"; import { NextPageWithLayout } from "lib/types"; const LabelsSettingsPage: NextPageWithLayout = () => ( -
+
); diff --git a/web/pages/[workspaceSlug]/settings/api-tokens.tsx b/web/pages/[workspaceSlug]/settings/api-tokens.tsx index e7307c97da..3d65c2d7b5 100644 --- a/web/pages/[workspaceSlug]/settings/api-tokens.tsx +++ b/web/pages/[workspaceSlug]/settings/api-tokens.tsx @@ -2,6 +2,7 @@ import React, { useState } from "react"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import useSWR from "swr"; +import { useTheme } from "next-themes"; // store hooks import { useUser } from "hooks/store"; // layouts @@ -9,9 +10,11 @@ import { AppLayout } from "layouts/app-layout"; import { WorkspaceSettingLayout } from "layouts/settings-layout"; // component import { WorkspaceSettingHeader } from "components/headers"; -import { ApiTokenEmptyState, ApiTokenListItem, CreateApiTokenModal } from "components/api-token"; +import { ApiTokenListItem, CreateApiTokenModal } from "components/api-token"; +import { EmptyState, getEmptyStateImagePath } from "components/empty-state"; // ui -import { Button, Spinner } from "@plane/ui"; +import { Button } from "@plane/ui"; +import { APITokenSettingsLoader } from "components/ui"; // services import { APITokenService } from "services/api_token.service"; // types @@ -19,6 +22,7 @@ import { NextPageWithLayout } from "lib/types"; // constants import { API_TOKENS_LIST } from "constants/fetch-keys"; import { EUserWorkspaceRoles } from "constants/workspace"; +import { WORKSPACE_SETTINGS_EMPTY_STATE_DETAILS } from "constants/empty-state"; const apiTokenService = new APITokenService(); @@ -28,9 +32,12 @@ const ApiTokensPage: NextPageWithLayout = observer(() => { // router const router = useRouter(); const { workspaceSlug } = router.query; + // theme + const { resolvedTheme } = useTheme(); // store hooks const { membership: { currentWorkspaceRole }, + currentUser, } = useUser(); const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN; @@ -39,6 +46,10 @@ const ApiTokensPage: NextPageWithLayout = observer(() => { workspaceSlug && isAdmin ? apiTokenService.getApiTokens(workspaceSlug.toString()) : null ); + const emptyStateDetail = WORKSPACE_SETTINGS_EMPTY_STATE_DETAILS["api-tokens"]; + const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light"; + const emptyStateImage = getEmptyStateImagePath("workspace-settings", "api-tokens", isLightMode); + if (!isAdmin) return (
@@ -46,36 +57,47 @@ const ApiTokensPage: NextPageWithLayout = observer(() => {
); + if (!tokens) { + return ; + } + return ( <> setIsCreateTokenModalOpen(false)} /> - {tokens ? ( -
- {tokens.length > 0 ? ( - <> -
-

API tokens

- -
-
- {tokens.map((token) => ( - - ))} -
- - ) : ( -
- setIsCreateTokenModalOpen(true)} /> +
+ {tokens.length > 0 ? ( + <> +
+

API tokens

+
- )} -
- ) : ( -
- -
- )} +
+ {tokens.map((token) => ( + + ))} +
+ + ) : ( +
+
+

API tokens

+ +
+
+ +
+
+ )} +
); }); diff --git a/web/pages/[workspaceSlug]/settings/integrations.tsx b/web/pages/[workspaceSlug]/settings/integrations.tsx index 70147c312c..940c90f3a6 100644 --- a/web/pages/[workspaceSlug]/settings/integrations.tsx +++ b/web/pages/[workspaceSlug]/settings/integrations.tsx @@ -13,8 +13,7 @@ import { WorkspaceSettingLayout } from "layouts/settings-layout"; import { SingleIntegrationCard } from "components/integration"; import { WorkspaceSettingHeader } from "components/headers"; // ui -import { IntegrationAndImportExportBanner } from "components/ui"; -import { Loader } from "@plane/ui"; +import { IntegrationAndImportExportBanner, IntegrationsSettingsLoader } from "components/ui"; // types import { NextPageWithLayout } from "lib/types"; // fetch-keys @@ -53,10 +52,7 @@ const WorkspaceIntegrationsPage: NextPageWithLayout = observer(() => { {appIntegrations ? ( appIntegrations.map((integration) => ) ) : ( - - - - + )}
diff --git a/web/pages/[workspaceSlug]/settings/webhooks/index.tsx b/web/pages/[workspaceSlug]/settings/webhooks/index.tsx index d8ba13f933..cafac485e6 100644 --- a/web/pages/[workspaceSlug]/settings/webhooks/index.tsx +++ b/web/pages/[workspaceSlug]/settings/webhooks/index.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react"; import { useRouter } from "next/router"; import useSWR from "swr"; import { observer } from "mobx-react-lite"; +import { useTheme } from "next-themes"; // hooks import { useUser, useWebhook, useWorkspace } from "hooks/store"; // layouts @@ -9,11 +10,15 @@ import { AppLayout } from "layouts/app-layout"; import { WorkspaceSettingLayout } from "layouts/settings-layout"; // components import { WorkspaceSettingHeader } from "components/headers"; -import { WebhooksList, WebhooksEmptyState, CreateWebhookModal } from "components/web-hooks"; +import { WebhooksList, CreateWebhookModal } from "components/web-hooks"; +import { EmptyState, getEmptyStateImagePath } from "components/empty-state"; // ui -import { Button, Spinner } from "@plane/ui"; +import { Button } from "@plane/ui"; +import { WebhookSettingsLoader } from "components/ui"; // types import { NextPageWithLayout } from "lib/types"; +// constants +import { WORKSPACE_SETTINGS_EMPTY_STATE_DETAILS } from "constants/empty-state"; const WebhooksListPage: NextPageWithLayout = observer(() => { // states @@ -21,9 +26,12 @@ const WebhooksListPage: NextPageWithLayout = observer(() => { // router const router = useRouter(); const { workspaceSlug } = router.query; + // theme + const { resolvedTheme } = useTheme(); // mobx store const { membership: { currentWorkspaceRole }, + currentUser, } = useUser(); const { fetchWebhooks, webhooks, clearSecretKey, webhookSecretKey, createWebhook } = useWebhook(); const { currentWorkspace } = useWorkspace(); @@ -35,6 +43,11 @@ const WebhooksListPage: NextPageWithLayout = observer(() => { workspaceSlug && isAdmin ? () => fetchWebhooks(workspaceSlug.toString()) : null ); + const emptyStateDetail = WORKSPACE_SETTINGS_EMPTY_STATE_DETAILS["webhooks"]; + + const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light"; + const emptyStateImage = getEmptyStateImagePath("workspace-settings", "webhooks", isLightMode); + // clear secret key when modal is closed. useEffect(() => { if (!showCreateWebhookModal && webhookSecretKey) clearSecretKey(); @@ -47,12 +60,7 @@ const WebhooksListPage: NextPageWithLayout = observer(() => {
); - if (!webhooks) - return ( -
- -
- ); + if (!webhooks) return ; return (
@@ -76,8 +84,21 @@ const WebhooksListPage: NextPageWithLayout = observer(() => {
) : ( -
- setShowCreateWebhookModal(true)} /> +
+
+
Webhooks
+ +
+
+ +
)}
diff --git a/web/pages/profile/activity.tsx b/web/pages/profile/activity.tsx index d0c83ffdbd..031e68f352 100644 --- a/web/pages/profile/activity.tsx +++ b/web/pages/profile/activity.tsx @@ -14,7 +14,7 @@ import { RichReadOnlyEditor } from "@plane/rich-text-editor"; // icons import { History, MessageSquare } from "lucide-react"; // ui -import { Loader } from "@plane/ui"; +import { ActivitySettingsLoader } from "components/ui"; // fetch-keys import { USER_ACTIVITY } from "constants/fetch-keys"; // helper @@ -31,7 +31,6 @@ const ProfileActivityPage: NextPageWithLayout = observer(() => { const { currentUser } = useUser(); return ( -
@@ -97,12 +96,12 @@ const ProfileActivityPage: NextPageWithLayout = observer(() => { const message = activityItem.verb === "created" && - activityItem.field !== "cycles" && - activityItem.field !== "modules" && - activityItem.field !== "attachment" && - activityItem.field !== "link" && - activityItem.field !== "estimate" && - !activityItem.field ? ( + activityItem.field !== "cycles" && + activityItem.field !== "modules" && + activityItem.field !== "attachment" && + activityItem.field !== "link" && + activityItem.field !== "estimate" && + !activityItem.field ? ( created @@ -182,15 +181,9 @@ const ProfileActivityPage: NextPageWithLayout = observer(() => {
) : ( - - - - - - + )}
- ); }); diff --git a/web/pages/profile/preferences/email.tsx b/web/pages/profile/preferences/email.tsx index 7db6df1135..8b896bbcb0 100644 --- a/web/pages/profile/preferences/email.tsx +++ b/web/pages/profile/preferences/email.tsx @@ -3,7 +3,7 @@ import useSWR from "swr"; // layouts import { ProfilePreferenceSettingsLayout } from "layouts/settings-layout/profile/preferences"; // ui -import { Loader } from "@plane/ui"; +import { EmailSettingsLoader } from "components/ui"; // components import { EmailNotificationForm } from "components/profile/preferences"; // services @@ -20,18 +20,8 @@ const ProfilePreferencesThemePage: NextPageWithLayout = () => { userService.currentUserEmailNotificationSettings() ); - if (isLoading) { - return ( - - - - - - ); - } - - if (!data) { - return null; + if (!data || isLoading) { + return ; } return ( diff --git a/web/public/empty-state/all-issues/all-issues-dark.webp b/web/public/empty-state/all-issues/all-issues-dark.webp index 2e7da76b33..6221d397fe 100644 Binary files a/web/public/empty-state/all-issues/all-issues-dark.webp and b/web/public/empty-state/all-issues/all-issues-dark.webp differ diff --git a/web/public/empty-state/all-issues/all-issues-light.webp b/web/public/empty-state/all-issues/all-issues-light.webp index 6b5897bf97..bb7e862942 100644 Binary files a/web/public/empty-state/all-issues/all-issues-light.webp and b/web/public/empty-state/all-issues/all-issues-light.webp differ diff --git a/web/public/empty-state/all-issues/assigned-dark.webp b/web/public/empty-state/all-issues/assigned-dark.webp index 5e8e3916f5..21b9162a58 100644 Binary files a/web/public/empty-state/all-issues/assigned-dark.webp and b/web/public/empty-state/all-issues/assigned-dark.webp differ diff --git a/web/public/empty-state/all-issues/assigned-light.webp b/web/public/empty-state/all-issues/assigned-light.webp index 6b04f3b30b..8e9247850e 100644 Binary files a/web/public/empty-state/all-issues/assigned-light.webp and b/web/public/empty-state/all-issues/assigned-light.webp differ diff --git a/web/public/empty-state/all-issues/created-dark.webp b/web/public/empty-state/all-issues/created-dark.webp index 6394e63f70..1e2b9abafe 100644 Binary files a/web/public/empty-state/all-issues/created-dark.webp and b/web/public/empty-state/all-issues/created-dark.webp differ diff --git a/web/public/empty-state/all-issues/created-light.webp b/web/public/empty-state/all-issues/created-light.webp index cf2b55dbb2..17d33cd013 100644 Binary files a/web/public/empty-state/all-issues/created-light.webp and b/web/public/empty-state/all-issues/created-light.webp differ diff --git a/web/public/empty-state/all-issues/custom-view-dark.webp b/web/public/empty-state/all-issues/custom-view-dark.webp index aba847d79c..2127ef965f 100644 Binary files a/web/public/empty-state/all-issues/custom-view-dark.webp and b/web/public/empty-state/all-issues/custom-view-dark.webp differ diff --git a/web/public/empty-state/all-issues/custom-view-light.webp b/web/public/empty-state/all-issues/custom-view-light.webp index a531babb32..110a08c25b 100644 Binary files a/web/public/empty-state/all-issues/custom-view-light.webp and b/web/public/empty-state/all-issues/custom-view-light.webp differ diff --git a/web/public/empty-state/all-issues/no-project-dark.webp b/web/public/empty-state/all-issues/no-project-dark.webp index 50c1ccf217..a6501a1eab 100644 Binary files a/web/public/empty-state/all-issues/no-project-dark.webp and b/web/public/empty-state/all-issues/no-project-dark.webp differ diff --git a/web/public/empty-state/all-issues/no-project-light.webp b/web/public/empty-state/all-issues/no-project-light.webp index 564c74ee5f..d1f5532489 100644 Binary files a/web/public/empty-state/all-issues/no-project-light.webp and b/web/public/empty-state/all-issues/no-project-light.webp differ diff --git a/web/public/empty-state/all-issues/subscribed-dark.webp b/web/public/empty-state/all-issues/subscribed-dark.webp index 6923b65e10..fbc5270e84 100644 Binary files a/web/public/empty-state/all-issues/subscribed-dark.webp and b/web/public/empty-state/all-issues/subscribed-dark.webp differ diff --git a/web/public/empty-state/all-issues/subscribed-light.webp b/web/public/empty-state/all-issues/subscribed-light.webp index d0411895b2..216c423939 100644 Binary files a/web/public/empty-state/all-issues/subscribed-light.webp and b/web/public/empty-state/all-issues/subscribed-light.webp differ diff --git a/web/public/empty-state/archived/empty-issues-dark.webp b/web/public/empty-state/archived/empty-issues-dark.webp index 09d522d286..264488cbf6 100644 Binary files a/web/public/empty-state/archived/empty-issues-dark.webp and b/web/public/empty-state/archived/empty-issues-dark.webp differ diff --git a/web/public/empty-state/archived/empty-issues-light.webp b/web/public/empty-state/archived/empty-issues-light.webp index 7aa422a4f8..602f0b14f0 100644 Binary files a/web/public/empty-state/archived/empty-issues-light.webp and b/web/public/empty-state/archived/empty-issues-light.webp differ diff --git a/web/public/empty-state/cycle-issues/calendar-dark-resp.webp b/web/public/empty-state/cycle-issues/calendar-dark-resp.webp new file mode 100644 index 0000000000..8ef6659090 Binary files /dev/null and b/web/public/empty-state/cycle-issues/calendar-dark-resp.webp differ diff --git a/web/public/empty-state/cycle-issues/calendar-dark.webp b/web/public/empty-state/cycle-issues/calendar-dark.webp new file mode 100644 index 0000000000..1ad89607dd Binary files /dev/null and b/web/public/empty-state/cycle-issues/calendar-dark.webp differ diff --git a/web/public/empty-state/cycle-issues/calendar-light-resp.webp b/web/public/empty-state/cycle-issues/calendar-light-resp.webp new file mode 100644 index 0000000000..0706693b88 Binary files /dev/null and b/web/public/empty-state/cycle-issues/calendar-light-resp.webp differ diff --git a/web/public/empty-state/cycle-issues/calendar-light.webp b/web/public/empty-state/cycle-issues/calendar-light.webp new file mode 100644 index 0000000000..c844daf5a9 Binary files /dev/null and b/web/public/empty-state/cycle-issues/calendar-light.webp differ diff --git a/web/public/empty-state/cycle-issues/gantt_chart-dark-resp.webp b/web/public/empty-state/cycle-issues/gantt_chart-dark-resp.webp new file mode 100644 index 0000000000..d824ba289e Binary files /dev/null and b/web/public/empty-state/cycle-issues/gantt_chart-dark-resp.webp differ diff --git a/web/public/empty-state/cycle-issues/gantt_chart-dark.webp b/web/public/empty-state/cycle-issues/gantt_chart-dark.webp new file mode 100644 index 0000000000..d03aac9e0e Binary files /dev/null and b/web/public/empty-state/cycle-issues/gantt_chart-dark.webp differ diff --git a/web/public/empty-state/cycle-issues/gantt_chart-light-resp.webp b/web/public/empty-state/cycle-issues/gantt_chart-light-resp.webp new file mode 100644 index 0000000000..6309d98541 Binary files /dev/null and b/web/public/empty-state/cycle-issues/gantt_chart-light-resp.webp differ diff --git a/web/public/empty-state/cycle-issues/gantt_chart-light.webp b/web/public/empty-state/cycle-issues/gantt_chart-light.webp new file mode 100644 index 0000000000..b77192863b Binary files /dev/null and b/web/public/empty-state/cycle-issues/gantt_chart-light.webp differ diff --git a/web/public/empty-state/cycle-issues/kanban-dark-resp.webp b/web/public/empty-state/cycle-issues/kanban-dark-resp.webp new file mode 100644 index 0000000000..f2ce02bfa8 Binary files /dev/null and b/web/public/empty-state/cycle-issues/kanban-dark-resp.webp differ diff --git a/web/public/empty-state/cycle-issues/kanban-dark.webp b/web/public/empty-state/cycle-issues/kanban-dark.webp new file mode 100644 index 0000000000..19785a8ba8 Binary files /dev/null and b/web/public/empty-state/cycle-issues/kanban-dark.webp differ diff --git a/web/public/empty-state/cycle-issues/kanban-light-resp.webp b/web/public/empty-state/cycle-issues/kanban-light-resp.webp new file mode 100644 index 0000000000..355b007ebb Binary files /dev/null and b/web/public/empty-state/cycle-issues/kanban-light-resp.webp differ diff --git a/web/public/empty-state/cycle-issues/kanban-light.webp b/web/public/empty-state/cycle-issues/kanban-light.webp new file mode 100644 index 0000000000..c22f8308cd Binary files /dev/null and b/web/public/empty-state/cycle-issues/kanban-light.webp differ diff --git a/web/public/empty-state/cycle-issues/list-dark-resp.webp b/web/public/empty-state/cycle-issues/list-dark-resp.webp new file mode 100644 index 0000000000..c631e36bf9 Binary files /dev/null and b/web/public/empty-state/cycle-issues/list-dark-resp.webp differ diff --git a/web/public/empty-state/cycle-issues/list-dark.webp b/web/public/empty-state/cycle-issues/list-dark.webp new file mode 100644 index 0000000000..89a83d9f81 Binary files /dev/null and b/web/public/empty-state/cycle-issues/list-dark.webp differ diff --git a/web/public/empty-state/cycle-issues/list-light-resp.webp b/web/public/empty-state/cycle-issues/list-light-resp.webp new file mode 100644 index 0000000000..5d54bc6b3d Binary files /dev/null and b/web/public/empty-state/cycle-issues/list-light-resp.webp differ diff --git a/web/public/empty-state/cycle-issues/list-light.webp b/web/public/empty-state/cycle-issues/list-light.webp new file mode 100644 index 0000000000..2769501a99 Binary files /dev/null and b/web/public/empty-state/cycle-issues/list-light.webp differ diff --git a/web/public/empty-state/cycle-issues/spreadsheet-dark-resp.webp b/web/public/empty-state/cycle-issues/spreadsheet-dark-resp.webp new file mode 100644 index 0000000000..9c4ecbd877 Binary files /dev/null and b/web/public/empty-state/cycle-issues/spreadsheet-dark-resp.webp differ diff --git a/web/public/empty-state/cycle-issues/spreadsheet-dark.webp b/web/public/empty-state/cycle-issues/spreadsheet-dark.webp new file mode 100644 index 0000000000..f4015a0262 Binary files /dev/null and b/web/public/empty-state/cycle-issues/spreadsheet-dark.webp differ diff --git a/web/public/empty-state/cycle-issues/spreadsheet-light-resp.webp b/web/public/empty-state/cycle-issues/spreadsheet-light-resp.webp new file mode 100644 index 0000000000..89c90d4e86 Binary files /dev/null and b/web/public/empty-state/cycle-issues/spreadsheet-light-resp.webp differ diff --git a/web/public/empty-state/cycle-issues/spreadsheet-light.webp b/web/public/empty-state/cycle-issues/spreadsheet-light.webp new file mode 100644 index 0000000000..055638b428 Binary files /dev/null and b/web/public/empty-state/cycle-issues/spreadsheet-light.webp differ diff --git a/web/public/empty-state/cycle/active-dark.webp b/web/public/empty-state/cycle/active-dark.webp index 76de471941..336fd8173c 100644 Binary files a/web/public/empty-state/cycle/active-dark.webp and b/web/public/empty-state/cycle/active-dark.webp differ diff --git a/web/public/empty-state/cycle/active-light.webp b/web/public/empty-state/cycle/active-light.webp index d5508c069c..2ea9a8a88c 100644 Binary files a/web/public/empty-state/cycle/active-light.webp and b/web/public/empty-state/cycle/active-light.webp differ diff --git a/web/public/empty-state/cycle/completed-dark.webp b/web/public/empty-state/cycle/completed-dark.webp index 9121d1f4d2..eebe656d6f 100644 Binary files a/web/public/empty-state/cycle/completed-dark.webp and b/web/public/empty-state/cycle/completed-dark.webp differ diff --git a/web/public/empty-state/cycle/completed-light.webp b/web/public/empty-state/cycle/completed-light.webp index c1799c34a3..6c192fd356 100644 Binary files a/web/public/empty-state/cycle/completed-light.webp and b/web/public/empty-state/cycle/completed-light.webp differ diff --git a/web/public/empty-state/cycle/draft-dark.webp b/web/public/empty-state/cycle/draft-dark.webp index 251016532a..887e2e916a 100644 Binary files a/web/public/empty-state/cycle/draft-dark.webp and b/web/public/empty-state/cycle/draft-dark.webp differ diff --git a/web/public/empty-state/cycle/draft-light.webp b/web/public/empty-state/cycle/draft-light.webp index 7e809f3625..d1c88bc370 100644 Binary files a/web/public/empty-state/cycle/draft-light.webp and b/web/public/empty-state/cycle/draft-light.webp differ diff --git a/web/public/empty-state/cycle/upcoming-dark.webp b/web/public/empty-state/cycle/upcoming-dark.webp index d412702c0a..205164f486 100644 Binary files a/web/public/empty-state/cycle/upcoming-dark.webp and b/web/public/empty-state/cycle/upcoming-dark.webp differ diff --git a/web/public/empty-state/cycle/upcoming-light.webp b/web/public/empty-state/cycle/upcoming-light.webp index febf6ec897..522122f4a2 100644 Binary files a/web/public/empty-state/cycle/upcoming-light.webp and b/web/public/empty-state/cycle/upcoming-light.webp differ diff --git a/web/public/empty-state/draft/draft-issues-empty-dark.webp b/web/public/empty-state/draft/draft-issues-empty-dark.webp new file mode 100644 index 0000000000..0a3689022f Binary files /dev/null and b/web/public/empty-state/draft/draft-issues-empty-dark.webp differ diff --git a/web/public/empty-state/draft/draft-issues-empty-light.webp b/web/public/empty-state/draft/draft-issues-empty-light.webp new file mode 100644 index 0000000000..c4e84a9d3f Binary files /dev/null and b/web/public/empty-state/draft/draft-issues-empty-light.webp differ diff --git a/web/public/empty-state/draft/empty-issues-dark.webp b/web/public/empty-state/draft/empty-issues-dark.webp deleted file mode 100644 index 0973e52901..0000000000 Binary files a/web/public/empty-state/draft/empty-issues-dark.webp and /dev/null differ diff --git a/web/public/empty-state/draft/empty-issues-light.webp b/web/public/empty-state/draft/empty-issues-light.webp deleted file mode 100644 index 0ce0bb9f35..0000000000 Binary files a/web/public/empty-state/draft/empty-issues-light.webp and /dev/null differ diff --git a/web/public/empty-state/empty-filters/calendar-dark.webp b/web/public/empty-state/empty-filters/calendar-dark.webp index 160e1d533e..18fa5dfdd5 100644 Binary files a/web/public/empty-state/empty-filters/calendar-dark.webp and b/web/public/empty-state/empty-filters/calendar-dark.webp differ diff --git a/web/public/empty-state/empty-filters/calendar-light.webp b/web/public/empty-state/empty-filters/calendar-light.webp index 0ab8f52c61..3c443cef84 100644 Binary files a/web/public/empty-state/empty-filters/calendar-light.webp and b/web/public/empty-state/empty-filters/calendar-light.webp differ diff --git a/web/public/empty-state/empty-filters/gantt_chart-dark.webp b/web/public/empty-state/empty-filters/gantt_chart-dark.webp index 861439ba4e..0dd590ef74 100644 Binary files a/web/public/empty-state/empty-filters/gantt_chart-dark.webp and b/web/public/empty-state/empty-filters/gantt_chart-dark.webp differ diff --git a/web/public/empty-state/empty-filters/gantt_chart-light.webp b/web/public/empty-state/empty-filters/gantt_chart-light.webp index 22537f8c57..348d8a72c5 100644 Binary files a/web/public/empty-state/empty-filters/gantt_chart-light.webp and b/web/public/empty-state/empty-filters/gantt_chart-light.webp differ diff --git a/web/public/empty-state/empty-filters/kanban-dark.webp b/web/public/empty-state/empty-filters/kanban-dark.webp index 7845a126fb..8a742ab197 100644 Binary files a/web/public/empty-state/empty-filters/kanban-dark.webp and b/web/public/empty-state/empty-filters/kanban-dark.webp differ diff --git a/web/public/empty-state/empty-filters/kanban-light.webp b/web/public/empty-state/empty-filters/kanban-light.webp index e071a09a48..d0c8acf65a 100644 Binary files a/web/public/empty-state/empty-filters/kanban-light.webp and b/web/public/empty-state/empty-filters/kanban-light.webp differ diff --git a/web/public/empty-state/empty-filters/list-dark.webp b/web/public/empty-state/empty-filters/list-dark.webp index 8d9f160b8b..2edf6eecca 100644 Binary files a/web/public/empty-state/empty-filters/list-dark.webp and b/web/public/empty-state/empty-filters/list-dark.webp differ diff --git a/web/public/empty-state/empty-filters/list-light.webp b/web/public/empty-state/empty-filters/list-light.webp index 00bacf12e8..e034115e7e 100644 Binary files a/web/public/empty-state/empty-filters/list-light.webp and b/web/public/empty-state/empty-filters/list-light.webp differ diff --git a/web/public/empty-state/empty-filters/spreadsheet-dark.webp b/web/public/empty-state/empty-filters/spreadsheet-dark.webp index 3e86982e21..1d8a3d6bee 100644 Binary files a/web/public/empty-state/empty-filters/spreadsheet-dark.webp and b/web/public/empty-state/empty-filters/spreadsheet-dark.webp differ diff --git a/web/public/empty-state/empty-filters/spreadsheet-light.webp b/web/public/empty-state/empty-filters/spreadsheet-light.webp index e9c6e3a02c..b4ee9ee4c5 100644 Binary files a/web/public/empty-state/empty-filters/spreadsheet-light.webp and b/web/public/empty-state/empty-filters/spreadsheet-light.webp differ diff --git a/web/public/empty-state/inbox/inbox-dark-resp.webp b/web/public/empty-state/inbox/inbox-dark-resp.webp new file mode 100644 index 0000000000..690d3377cb Binary files /dev/null and b/web/public/empty-state/inbox/inbox-dark-resp.webp differ diff --git a/web/public/empty-state/inbox/inbox-dark.webp b/web/public/empty-state/inbox/inbox-dark.webp new file mode 100644 index 0000000000..fe3d486371 Binary files /dev/null and b/web/public/empty-state/inbox/inbox-dark.webp differ diff --git a/web/public/empty-state/inbox/inbox-light-resp.webp b/web/public/empty-state/inbox/inbox-light-resp.webp new file mode 100644 index 0000000000..7bb5f04e67 Binary files /dev/null and b/web/public/empty-state/inbox/inbox-light-resp.webp differ diff --git a/web/public/empty-state/inbox/inbox-light.webp b/web/public/empty-state/inbox/inbox-light.webp new file mode 100644 index 0000000000..83be70e0de Binary files /dev/null and b/web/public/empty-state/inbox/inbox-light.webp differ diff --git a/web/public/empty-state/module-issues/calendar-dark-resp.webp b/web/public/empty-state/module-issues/calendar-dark-resp.webp new file mode 100644 index 0000000000..80d7637c5d Binary files /dev/null and b/web/public/empty-state/module-issues/calendar-dark-resp.webp differ diff --git a/web/public/empty-state/module-issues/calendar-dark.webp b/web/public/empty-state/module-issues/calendar-dark.webp new file mode 100644 index 0000000000..18adf277cf Binary files /dev/null and b/web/public/empty-state/module-issues/calendar-dark.webp differ diff --git a/web/public/empty-state/module-issues/calendar-light-resp.webp b/web/public/empty-state/module-issues/calendar-light-resp.webp new file mode 100644 index 0000000000..acc31bc190 Binary files /dev/null and b/web/public/empty-state/module-issues/calendar-light-resp.webp differ diff --git a/web/public/empty-state/module-issues/calendar-light.webp b/web/public/empty-state/module-issues/calendar-light.webp new file mode 100644 index 0000000000..f542f91e14 Binary files /dev/null and b/web/public/empty-state/module-issues/calendar-light.webp differ diff --git a/web/public/empty-state/module-issues/gantt-dark-resp.webp b/web/public/empty-state/module-issues/gantt-dark-resp.webp new file mode 100644 index 0000000000..90bf1dd8f7 Binary files /dev/null and b/web/public/empty-state/module-issues/gantt-dark-resp.webp differ diff --git a/web/public/empty-state/module-issues/gantt-dark.webp b/web/public/empty-state/module-issues/gantt-dark.webp new file mode 100644 index 0000000000..365ab04c72 Binary files /dev/null and b/web/public/empty-state/module-issues/gantt-dark.webp differ diff --git a/web/public/empty-state/module-issues/gantt-light-resp.webp b/web/public/empty-state/module-issues/gantt-light-resp.webp new file mode 100644 index 0000000000..c160bf4a33 Binary files /dev/null and b/web/public/empty-state/module-issues/gantt-light-resp.webp differ diff --git a/web/public/empty-state/module-issues/gantt-light.webp b/web/public/empty-state/module-issues/gantt-light.webp new file mode 100644 index 0000000000..19531a8eb5 Binary files /dev/null and b/web/public/empty-state/module-issues/gantt-light.webp differ diff --git a/web/public/empty-state/module-issues/kanban-dark-resp.webp b/web/public/empty-state/module-issues/kanban-dark-resp.webp new file mode 100644 index 0000000000..d6fa66d3ef Binary files /dev/null and b/web/public/empty-state/module-issues/kanban-dark-resp.webp differ diff --git a/web/public/empty-state/module-issues/kanban-dark.webp b/web/public/empty-state/module-issues/kanban-dark.webp new file mode 100644 index 0000000000..ad1b9dcadc Binary files /dev/null and b/web/public/empty-state/module-issues/kanban-dark.webp differ diff --git a/web/public/empty-state/module-issues/kanban-light-resp.webp b/web/public/empty-state/module-issues/kanban-light-resp.webp new file mode 100644 index 0000000000..97dd429ed3 Binary files /dev/null and b/web/public/empty-state/module-issues/kanban-light-resp.webp differ diff --git a/web/public/empty-state/module-issues/kanban-light.webp b/web/public/empty-state/module-issues/kanban-light.webp new file mode 100644 index 0000000000..cad700e034 Binary files /dev/null and b/web/public/empty-state/module-issues/kanban-light.webp differ diff --git a/web/public/empty-state/module-issues/list-dark-resp.webp b/web/public/empty-state/module-issues/list-dark-resp.webp new file mode 100644 index 0000000000..f60248bfc1 Binary files /dev/null and b/web/public/empty-state/module-issues/list-dark-resp.webp differ diff --git a/web/public/empty-state/module-issues/list-dark.webp b/web/public/empty-state/module-issues/list-dark.webp new file mode 100644 index 0000000000..3b00a2c2d0 Binary files /dev/null and b/web/public/empty-state/module-issues/list-dark.webp differ diff --git a/web/public/empty-state/module-issues/list-light-resp.webp b/web/public/empty-state/module-issues/list-light-resp.webp new file mode 100644 index 0000000000..2226988ae7 Binary files /dev/null and b/web/public/empty-state/module-issues/list-light-resp.webp differ diff --git a/web/public/empty-state/module-issues/list-light.webp b/web/public/empty-state/module-issues/list-light.webp new file mode 100644 index 0000000000..5d9735ee1e Binary files /dev/null and b/web/public/empty-state/module-issues/list-light.webp differ diff --git a/web/public/empty-state/module-issues/spreadsheet-dark-resp.webp b/web/public/empty-state/module-issues/spreadsheet-dark-resp.webp new file mode 100644 index 0000000000..1beb57b090 Binary files /dev/null and b/web/public/empty-state/module-issues/spreadsheet-dark-resp.webp differ diff --git a/web/public/empty-state/module-issues/spreadsheet-dark.webp b/web/public/empty-state/module-issues/spreadsheet-dark.webp new file mode 100644 index 0000000000..58698ed74b Binary files /dev/null and b/web/public/empty-state/module-issues/spreadsheet-dark.webp differ diff --git a/web/public/empty-state/module-issues/spreadsheet-light-resp.webp b/web/public/empty-state/module-issues/spreadsheet-light-resp.webp new file mode 100644 index 0000000000..8f3a0679b4 Binary files /dev/null and b/web/public/empty-state/module-issues/spreadsheet-light-resp.webp differ diff --git a/web/public/empty-state/module-issues/spreadsheet-light.webp b/web/public/empty-state/module-issues/spreadsheet-light.webp new file mode 100644 index 0000000000..a1735e1d17 Binary files /dev/null and b/web/public/empty-state/module-issues/spreadsheet-light.webp differ diff --git a/web/public/empty-state/onboarding/analytics-dark.webp b/web/public/empty-state/onboarding/analytics-dark.webp index f9ed54ae63..a8a1d95761 100644 Binary files a/web/public/empty-state/onboarding/analytics-dark.webp and b/web/public/empty-state/onboarding/analytics-dark.webp differ diff --git a/web/public/empty-state/onboarding/analytics-light.webp b/web/public/empty-state/onboarding/analytics-light.webp index ca0f5f5516..76d5cfc282 100644 Binary files a/web/public/empty-state/onboarding/analytics-light.webp and b/web/public/empty-state/onboarding/analytics-light.webp differ diff --git a/web/public/empty-state/onboarding/archive-dark.png b/web/public/empty-state/onboarding/archive-dark.png new file mode 100644 index 0000000000..54cca36ec3 Binary files /dev/null and b/web/public/empty-state/onboarding/archive-dark.png differ diff --git a/web/public/empty-state/onboarding/archive-light.png b/web/public/empty-state/onboarding/archive-light.png new file mode 100644 index 0000000000..8530bbac1b Binary files /dev/null and b/web/public/empty-state/onboarding/archive-light.png differ diff --git a/web/public/empty-state/onboarding/cycles-dark.webp b/web/public/empty-state/onboarding/cycles-dark.webp index d655b5226a..716f121d78 100644 Binary files a/web/public/empty-state/onboarding/cycles-dark.webp and b/web/public/empty-state/onboarding/cycles-dark.webp differ diff --git a/web/public/empty-state/onboarding/cycles-light.webp b/web/public/empty-state/onboarding/cycles-light.webp index ca069f50a6..0b2b400883 100644 Binary files a/web/public/empty-state/onboarding/cycles-light.webp and b/web/public/empty-state/onboarding/cycles-light.webp differ diff --git a/web/public/empty-state/onboarding/dashboard-dark.webp b/web/public/empty-state/onboarding/dashboard-dark.webp index 486060c09e..afa10bea77 100644 Binary files a/web/public/empty-state/onboarding/dashboard-dark.webp and b/web/public/empty-state/onboarding/dashboard-dark.webp differ diff --git a/web/public/empty-state/onboarding/dashboard-light.webp b/web/public/empty-state/onboarding/dashboard-light.webp index 89d97bf085..d4bce8bee3 100644 Binary files a/web/public/empty-state/onboarding/dashboard-light.webp and b/web/public/empty-state/onboarding/dashboard-light.webp differ diff --git a/web/public/empty-state/onboarding/graph-dark.png b/web/public/empty-state/onboarding/graph-dark.png new file mode 100644 index 0000000000..e8a938d8f0 Binary files /dev/null and b/web/public/empty-state/onboarding/graph-dark.png differ diff --git a/web/public/empty-state/onboarding/graph-light.png b/web/public/empty-state/onboarding/graph-light.png new file mode 100644 index 0000000000..eb48b45a1c Binary files /dev/null and b/web/public/empty-state/onboarding/graph-light.png differ diff --git a/web/public/empty-state/onboarding/issues-closed-dark.png b/web/public/empty-state/onboarding/issues-closed-dark.png new file mode 100644 index 0000000000..d4bafb28ed Binary files /dev/null and b/web/public/empty-state/onboarding/issues-closed-dark.png differ diff --git a/web/public/empty-state/onboarding/issues-closed-light.png b/web/public/empty-state/onboarding/issues-closed-light.png new file mode 100644 index 0000000000..739acd10d8 Binary files /dev/null and b/web/public/empty-state/onboarding/issues-closed-light.png differ diff --git a/web/public/empty-state/onboarding/issues-dark.webp b/web/public/empty-state/onboarding/issues-dark.webp index d1b1338a17..bbcf81b19a 100644 Binary files a/web/public/empty-state/onboarding/issues-dark.webp and b/web/public/empty-state/onboarding/issues-dark.webp differ diff --git a/web/public/empty-state/onboarding/issues-light.webp b/web/public/empty-state/onboarding/issues-light.webp index b875a5eb0f..0e67d81a33 100644 Binary files a/web/public/empty-state/onboarding/issues-light.webp and b/web/public/empty-state/onboarding/issues-light.webp differ diff --git a/web/public/empty-state/onboarding/members-dark.png b/web/public/empty-state/onboarding/members-dark.png new file mode 100644 index 0000000000..2f8a1c873a Binary files /dev/null and b/web/public/empty-state/onboarding/members-dark.png differ diff --git a/web/public/empty-state/onboarding/members-light.png b/web/public/empty-state/onboarding/members-light.png new file mode 100644 index 0000000000..eaa43ef900 Binary files /dev/null and b/web/public/empty-state/onboarding/members-light.png differ diff --git a/web/public/empty-state/onboarding/modules-dark.webp b/web/public/empty-state/onboarding/modules-dark.webp index ee86e78806..3e16dfc42c 100644 Binary files a/web/public/empty-state/onboarding/modules-dark.webp and b/web/public/empty-state/onboarding/modules-dark.webp differ diff --git a/web/public/empty-state/onboarding/modules-light.webp b/web/public/empty-state/onboarding/modules-light.webp index 1eedadb6ef..47d319feea 100644 Binary files a/web/public/empty-state/onboarding/modules-light.webp and b/web/public/empty-state/onboarding/modules-light.webp differ diff --git a/web/public/empty-state/onboarding/notification-dark.png b/web/public/empty-state/onboarding/notification-dark.png new file mode 100644 index 0000000000..dee284ce67 Binary files /dev/null and b/web/public/empty-state/onboarding/notification-dark.png differ diff --git a/web/public/empty-state/onboarding/notification-light.png b/web/public/empty-state/onboarding/notification-light.png new file mode 100644 index 0000000000..3065787e19 Binary files /dev/null and b/web/public/empty-state/onboarding/notification-light.png differ diff --git a/web/public/empty-state/onboarding/pages-dark.webp b/web/public/empty-state/onboarding/pages-dark.webp index 278d228df9..def65dd515 100644 Binary files a/web/public/empty-state/onboarding/pages-dark.webp and b/web/public/empty-state/onboarding/pages-dark.webp differ diff --git a/web/public/empty-state/onboarding/projects-dark.webp b/web/public/empty-state/onboarding/projects-dark.webp index 2f34a02dee..c5482908a0 100644 Binary files a/web/public/empty-state/onboarding/projects-dark.webp and b/web/public/empty-state/onboarding/projects-dark.webp differ diff --git a/web/public/empty-state/onboarding/projects-light.webp b/web/public/empty-state/onboarding/projects-light.webp index 54894cbde2..20206adfb0 100644 Binary files a/web/public/empty-state/onboarding/projects-light.webp and b/web/public/empty-state/onboarding/projects-light.webp differ diff --git a/web/public/empty-state/onboarding/search-dark.png b/web/public/empty-state/onboarding/search-dark.png new file mode 100644 index 0000000000..531081d433 Binary files /dev/null and b/web/public/empty-state/onboarding/search-dark.png differ diff --git a/web/public/empty-state/onboarding/search-light.png b/web/public/empty-state/onboarding/search-light.png new file mode 100644 index 0000000000..39e2636676 Binary files /dev/null and b/web/public/empty-state/onboarding/search-light.png differ diff --git a/web/public/empty-state/onboarding/snooze-light.png b/web/public/empty-state/onboarding/snooze-light.png new file mode 100644 index 0000000000..6ccd7ab91d Binary files /dev/null and b/web/public/empty-state/onboarding/snooze-light.png differ diff --git a/web/public/empty-state/onboarding/snoozed-dark.png b/web/public/empty-state/onboarding/snoozed-dark.png new file mode 100644 index 0000000000..c8b88fe560 Binary files /dev/null and b/web/public/empty-state/onboarding/snoozed-dark.png differ diff --git a/web/public/empty-state/onboarding/views-dark.webp b/web/public/empty-state/onboarding/views-dark.webp index bca0f383ce..7ad4e6a87b 100644 Binary files a/web/public/empty-state/onboarding/views-dark.webp and b/web/public/empty-state/onboarding/views-dark.webp differ diff --git a/web/public/empty-state/onboarding/views-light.webp b/web/public/empty-state/onboarding/views-light.webp index cd6900834a..88c5967fbe 100644 Binary files a/web/public/empty-state/onboarding/views-light.webp and b/web/public/empty-state/onboarding/views-light.webp differ diff --git a/web/public/empty-state/onboarding/workspace-invites-dark.webp b/web/public/empty-state/onboarding/workspace-invites-dark.webp new file mode 100644 index 0000000000..d6e10046a1 Binary files /dev/null and b/web/public/empty-state/onboarding/workspace-invites-dark.webp differ diff --git a/web/public/empty-state/onboarding/workspace-invites-light.webp b/web/public/empty-state/onboarding/workspace-invites-light.webp new file mode 100644 index 0000000000..123a600efb Binary files /dev/null and b/web/public/empty-state/onboarding/workspace-invites-light.webp differ diff --git a/web/public/empty-state/pages/all-dark.webp b/web/public/empty-state/pages/all-dark.webp index 3c9ea167af..9ce16f6d4a 100644 Binary files a/web/public/empty-state/pages/all-dark.webp and b/web/public/empty-state/pages/all-dark.webp differ diff --git a/web/public/empty-state/pages/all-light.webp b/web/public/empty-state/pages/all-light.webp index 54b1b47d99..ad70d20d8c 100644 Binary files a/web/public/empty-state/pages/all-light.webp and b/web/public/empty-state/pages/all-light.webp differ diff --git a/web/public/empty-state/pages/archived-dark.webp b/web/public/empty-state/pages/archived-dark.webp index 3a9543b54f..38a9481cf9 100644 Binary files a/web/public/empty-state/pages/archived-dark.webp and b/web/public/empty-state/pages/archived-dark.webp differ diff --git a/web/public/empty-state/pages/archived-light.webp b/web/public/empty-state/pages/archived-light.webp index 54cc928c99..0dbb1e8549 100644 Binary files a/web/public/empty-state/pages/archived-light.webp and b/web/public/empty-state/pages/archived-light.webp differ diff --git a/web/public/empty-state/pages/favorites-dark.webp b/web/public/empty-state/pages/favorites-dark.webp index 9ae6788708..c58b0aab36 100644 Binary files a/web/public/empty-state/pages/favorites-dark.webp and b/web/public/empty-state/pages/favorites-dark.webp differ diff --git a/web/public/empty-state/pages/favorites-light.webp b/web/public/empty-state/pages/favorites-light.webp index 88b62af4d7..bd8d061321 100644 Binary files a/web/public/empty-state/pages/favorites-light.webp and b/web/public/empty-state/pages/favorites-light.webp differ diff --git a/web/public/empty-state/pages/private-dark.webp b/web/public/empty-state/pages/private-dark.webp index ac3e836b4d..389849909e 100644 Binary files a/web/public/empty-state/pages/private-dark.webp and b/web/public/empty-state/pages/private-dark.webp differ diff --git a/web/public/empty-state/pages/private-light.webp b/web/public/empty-state/pages/private-light.webp index 760e136572..9acf5dbcf0 100644 Binary files a/web/public/empty-state/pages/private-light.webp and b/web/public/empty-state/pages/private-light.webp differ diff --git a/web/public/empty-state/pages/recent-dark.webp b/web/public/empty-state/pages/recent-dark.webp index 4a103354e7..f96ef1564d 100644 Binary files a/web/public/empty-state/pages/recent-dark.webp and b/web/public/empty-state/pages/recent-dark.webp differ diff --git a/web/public/empty-state/pages/recent-light.webp b/web/public/empty-state/pages/recent-light.webp index 4b908d3981..9c8d64a4f6 100644 Binary files a/web/public/empty-state/pages/recent-light.webp and b/web/public/empty-state/pages/recent-light.webp differ diff --git a/web/public/empty-state/pages/shared-dark.webp b/web/public/empty-state/pages/shared-dark.webp index 941960280d..444886758d 100644 Binary files a/web/public/empty-state/pages/shared-dark.webp and b/web/public/empty-state/pages/shared-dark.webp differ diff --git a/web/public/empty-state/pages/shared-light.webp b/web/public/empty-state/pages/shared-light.webp index a3ead55f30..03b3051b60 100644 Binary files a/web/public/empty-state/pages/shared-light.webp and b/web/public/empty-state/pages/shared-light.webp differ diff --git a/web/public/empty-state/profile/assigned-dark.webp b/web/public/empty-state/profile/assigned-dark.webp index eaec74dde2..39b56fff91 100644 Binary files a/web/public/empty-state/profile/assigned-dark.webp and b/web/public/empty-state/profile/assigned-dark.webp differ diff --git a/web/public/empty-state/profile/assigned-light.webp b/web/public/empty-state/profile/assigned-light.webp index 59a7b06e36..2963fa41b3 100644 Binary files a/web/public/empty-state/profile/assigned-light.webp and b/web/public/empty-state/profile/assigned-light.webp differ diff --git a/web/public/empty-state/profile/created-dark.webp b/web/public/empty-state/profile/created-dark.webp index 12f153519a..f006ddf746 100644 Binary files a/web/public/empty-state/profile/created-dark.webp and b/web/public/empty-state/profile/created-dark.webp differ diff --git a/web/public/empty-state/profile/created-light.webp b/web/public/empty-state/profile/created-light.webp index f95679f112..446b8f4508 100644 Binary files a/web/public/empty-state/profile/created-light.webp and b/web/public/empty-state/profile/created-light.webp differ diff --git a/web/public/empty-state/profile/subscribed-dark.webp b/web/public/empty-state/profile/subscribed-dark.webp index ae30d3d5db..15125f5463 100644 Binary files a/web/public/empty-state/profile/subscribed-dark.webp and b/web/public/empty-state/profile/subscribed-dark.webp differ diff --git a/web/public/empty-state/profile/subscribed-light.webp b/web/public/empty-state/profile/subscribed-light.webp index d24f58f245..9204dca819 100644 Binary files a/web/public/empty-state/profile/subscribed-light.webp and b/web/public/empty-state/profile/subscribed-light.webp differ diff --git a/web/public/empty-state/project-settings/estimates-dark-resp.webp b/web/public/empty-state/project-settings/estimates-dark-resp.webp new file mode 100644 index 0000000000..ff136236cb Binary files /dev/null and b/web/public/empty-state/project-settings/estimates-dark-resp.webp differ diff --git a/web/public/empty-state/project-settings/estimates-dark.webp b/web/public/empty-state/project-settings/estimates-dark.webp new file mode 100644 index 0000000000..35d42e52de Binary files /dev/null and b/web/public/empty-state/project-settings/estimates-dark.webp differ diff --git a/web/public/empty-state/project-settings/estimates-light-resp.webp b/web/public/empty-state/project-settings/estimates-light-resp.webp new file mode 100644 index 0000000000..31daa2c199 Binary files /dev/null and b/web/public/empty-state/project-settings/estimates-light-resp.webp differ diff --git a/web/public/empty-state/project-settings/estimates-light.webp b/web/public/empty-state/project-settings/estimates-light.webp new file mode 100644 index 0000000000..b329ea916f Binary files /dev/null and b/web/public/empty-state/project-settings/estimates-light.webp differ diff --git a/web/public/empty-state/project-settings/integrations-dark-resp.webp b/web/public/empty-state/project-settings/integrations-dark-resp.webp new file mode 100644 index 0000000000..3f9e00956e Binary files /dev/null and b/web/public/empty-state/project-settings/integrations-dark-resp.webp differ diff --git a/web/public/empty-state/project-settings/integrations-dark.webp b/web/public/empty-state/project-settings/integrations-dark.webp new file mode 100644 index 0000000000..603a658ef4 Binary files /dev/null and b/web/public/empty-state/project-settings/integrations-dark.webp differ diff --git a/web/public/empty-state/project-settings/integrations-light-resp.webp b/web/public/empty-state/project-settings/integrations-light-resp.webp new file mode 100644 index 0000000000..13e08be84f Binary files /dev/null and b/web/public/empty-state/project-settings/integrations-light-resp.webp differ diff --git a/web/public/empty-state/project-settings/integrations-light.webp b/web/public/empty-state/project-settings/integrations-light.webp new file mode 100644 index 0000000000..eb62503e0d Binary files /dev/null and b/web/public/empty-state/project-settings/integrations-light.webp differ diff --git a/web/public/empty-state/project-settings/labels-dark-resp.webp b/web/public/empty-state/project-settings/labels-dark-resp.webp new file mode 100644 index 0000000000..fe961c061a Binary files /dev/null and b/web/public/empty-state/project-settings/labels-dark-resp.webp differ diff --git a/web/public/empty-state/project-settings/labels-dark.webp b/web/public/empty-state/project-settings/labels-dark.webp new file mode 100644 index 0000000000..a5489d3aea Binary files /dev/null and b/web/public/empty-state/project-settings/labels-dark.webp differ diff --git a/web/public/empty-state/project-settings/labels-light-resp.webp b/web/public/empty-state/project-settings/labels-light-resp.webp new file mode 100644 index 0000000000..f49009b715 Binary files /dev/null and b/web/public/empty-state/project-settings/labels-light-resp.webp differ diff --git a/web/public/empty-state/project-settings/labels-light.webp b/web/public/empty-state/project-settings/labels-light.webp new file mode 100644 index 0000000000..eddb82f5fb Binary files /dev/null and b/web/public/empty-state/project-settings/labels-light.webp differ diff --git a/web/public/empty-state/search/all-issue-view-dark.webp b/web/public/empty-state/search/all-issue-view-dark.webp new file mode 100644 index 0000000000..5db15e1651 Binary files /dev/null and b/web/public/empty-state/search/all-issue-view-dark.webp differ diff --git a/web/public/empty-state/search/all-issues-view-light.webp b/web/public/empty-state/search/all-issues-view-light.webp new file mode 100644 index 0000000000..af44f0ed75 Binary files /dev/null and b/web/public/empty-state/search/all-issues-view-light.webp differ diff --git a/web/public/empty-state/search/member-dark.webp b/web/public/empty-state/search/member-dark.webp new file mode 100644 index 0000000000..20521a5d55 Binary files /dev/null and b/web/public/empty-state/search/member-dark.webp differ diff --git a/web/public/empty-state/search/member-light.webp b/web/public/empty-state/search/member-light.webp new file mode 100644 index 0000000000..dcd38c9741 Binary files /dev/null and b/web/public/empty-state/search/member-light.webp differ diff --git a/web/public/empty-state/search/project-dark.webp b/web/public/empty-state/search/project-dark.webp new file mode 100644 index 0000000000..c25806b5bc Binary files /dev/null and b/web/public/empty-state/search/project-dark.webp differ diff --git a/web/public/empty-state/search/project-light.webp b/web/public/empty-state/search/project-light.webp new file mode 100644 index 0000000000..2e4bbad4ca Binary files /dev/null and b/web/public/empty-state/search/project-light.webp differ diff --git a/web/public/empty-state/workspace-settings/api-tokens-dark-resp.webp b/web/public/empty-state/workspace-settings/api-tokens-dark-resp.webp new file mode 100644 index 0000000000..1ac7f6dd94 Binary files /dev/null and b/web/public/empty-state/workspace-settings/api-tokens-dark-resp.webp differ diff --git a/web/public/empty-state/workspace-settings/api-tokens-dark.webp b/web/public/empty-state/workspace-settings/api-tokens-dark.webp new file mode 100644 index 0000000000..768e692330 Binary files /dev/null and b/web/public/empty-state/workspace-settings/api-tokens-dark.webp differ diff --git a/web/public/empty-state/workspace-settings/api-tokens-light-resp.webp b/web/public/empty-state/workspace-settings/api-tokens-light-resp.webp new file mode 100644 index 0000000000..1ed0bf3670 Binary files /dev/null and b/web/public/empty-state/workspace-settings/api-tokens-light-resp.webp differ diff --git a/web/public/empty-state/workspace-settings/api-tokens-light.webp b/web/public/empty-state/workspace-settings/api-tokens-light.webp new file mode 100644 index 0000000000..537532c76c Binary files /dev/null and b/web/public/empty-state/workspace-settings/api-tokens-light.webp differ diff --git a/web/public/empty-state/workspace-settings/exports-dark-resp.webp b/web/public/empty-state/workspace-settings/exports-dark-resp.webp new file mode 100644 index 0000000000..b175142b36 Binary files /dev/null and b/web/public/empty-state/workspace-settings/exports-dark-resp.webp differ diff --git a/web/public/empty-state/workspace-settings/exports-dark.webp b/web/public/empty-state/workspace-settings/exports-dark.webp new file mode 100644 index 0000000000..beac5a82e1 Binary files /dev/null and b/web/public/empty-state/workspace-settings/exports-dark.webp differ diff --git a/web/public/empty-state/workspace-settings/exports-light-resp.webp b/web/public/empty-state/workspace-settings/exports-light-resp.webp new file mode 100644 index 0000000000..65c4297b95 Binary files /dev/null and b/web/public/empty-state/workspace-settings/exports-light-resp.webp differ diff --git a/web/public/empty-state/workspace-settings/exports-light.webp b/web/public/empty-state/workspace-settings/exports-light.webp new file mode 100644 index 0000000000..6d482cddb9 Binary files /dev/null and b/web/public/empty-state/workspace-settings/exports-light.webp differ diff --git a/web/public/empty-state/workspace-settings/imports-dark-resp.webp b/web/public/empty-state/workspace-settings/imports-dark-resp.webp new file mode 100644 index 0000000000..5aefc4110d Binary files /dev/null and b/web/public/empty-state/workspace-settings/imports-dark-resp.webp differ diff --git a/web/public/empty-state/workspace-settings/imports-dark.webp b/web/public/empty-state/workspace-settings/imports-dark.webp new file mode 100644 index 0000000000..65642f6129 Binary files /dev/null and b/web/public/empty-state/workspace-settings/imports-dark.webp differ diff --git a/web/public/empty-state/workspace-settings/imports-light-resp.webp b/web/public/empty-state/workspace-settings/imports-light-resp.webp new file mode 100644 index 0000000000..5a24ad681a Binary files /dev/null and b/web/public/empty-state/workspace-settings/imports-light-resp.webp differ diff --git a/web/public/empty-state/workspace-settings/imports-light.webp b/web/public/empty-state/workspace-settings/imports-light.webp new file mode 100644 index 0000000000..fe0307065d Binary files /dev/null and b/web/public/empty-state/workspace-settings/imports-light.webp differ diff --git a/web/public/empty-state/workspace-settings/integrations-dark-resp.webp b/web/public/empty-state/workspace-settings/integrations-dark-resp.webp new file mode 100644 index 0000000000..3f9e00956e Binary files /dev/null and b/web/public/empty-state/workspace-settings/integrations-dark-resp.webp differ diff --git a/web/public/empty-state/workspace-settings/integrations-dark.webp b/web/public/empty-state/workspace-settings/integrations-dark.webp new file mode 100644 index 0000000000..603a658ef4 Binary files /dev/null and b/web/public/empty-state/workspace-settings/integrations-dark.webp differ diff --git a/web/public/empty-state/workspace-settings/integrations-light-resp.webp b/web/public/empty-state/workspace-settings/integrations-light-resp.webp new file mode 100644 index 0000000000..13e08be84f Binary files /dev/null and b/web/public/empty-state/workspace-settings/integrations-light-resp.webp differ diff --git a/web/public/empty-state/workspace-settings/integrations-light.webp b/web/public/empty-state/workspace-settings/integrations-light.webp new file mode 100644 index 0000000000..eb62503e0d Binary files /dev/null and b/web/public/empty-state/workspace-settings/integrations-light.webp differ diff --git a/web/public/empty-state/workspace-settings/webhooks-dark-resp.webp b/web/public/empty-state/workspace-settings/webhooks-dark-resp.webp new file mode 100644 index 0000000000..dde3ca7189 Binary files /dev/null and b/web/public/empty-state/workspace-settings/webhooks-dark-resp.webp differ diff --git a/web/public/empty-state/workspace-settings/webhooks-dark.webp b/web/public/empty-state/workspace-settings/webhooks-dark.webp new file mode 100644 index 0000000000..9196c14afc Binary files /dev/null and b/web/public/empty-state/workspace-settings/webhooks-dark.webp differ diff --git a/web/public/empty-state/workspace-settings/webhooks-light-resp.webp b/web/public/empty-state/workspace-settings/webhooks-light-resp.webp new file mode 100644 index 0000000000..8ee7a80784 Binary files /dev/null and b/web/public/empty-state/workspace-settings/webhooks-light-resp.webp differ diff --git a/web/public/empty-state/workspace-settings/webhooks-light.webp b/web/public/empty-state/workspace-settings/webhooks-light.webp new file mode 100644 index 0000000000..d51d586966 Binary files /dev/null and b/web/public/empty-state/workspace-settings/webhooks-light.webp differ diff --git a/web/store/application/theme.store.ts b/web/store/application/theme.store.ts index 505f3803c0..3873e73868 100644 --- a/web/store/application/theme.store.ts +++ b/web/store/application/theme.store.ts @@ -6,12 +6,14 @@ import { applyTheme, unsetCustomCssVariables } from "helpers/theme.helper"; export interface IThemeStore { // observables theme: string | null; + mobileSidebarCollapsed: boolean | undefined; sidebarCollapsed: boolean | undefined; profileSidebarCollapsed: boolean | undefined; workspaceAnalyticsSidebarCollapsed: boolean | undefined; issueDetailSidebarCollapsed: boolean | undefined; // actions toggleSidebar: (collapsed?: boolean) => void; + toggleMobileSidebar: (collapsed?: boolean) => void; setTheme: (theme: any) => void; toggleProfileSidebar: (collapsed?: boolean) => void; toggleWorkspaceAnalyticsSidebar: (collapsed?: boolean) => void; @@ -20,6 +22,7 @@ export interface IThemeStore { export class ThemeStore implements IThemeStore { // observables + mobileSidebarCollapsed: boolean | undefined = true; sidebarCollapsed: boolean | undefined = undefined; theme: string | null = null; profileSidebarCollapsed: boolean | undefined = undefined; @@ -31,6 +34,7 @@ export class ThemeStore implements IThemeStore { constructor(_rootStore: any | null = null) { makeObservable(this, { // observable + mobileSidebarCollapsed: observable.ref, sidebarCollapsed: observable.ref, theme: observable.ref, profileSidebarCollapsed: observable.ref, @@ -38,6 +42,7 @@ export class ThemeStore implements IThemeStore { issueDetailSidebarCollapsed: observable.ref, // action toggleSidebar: action, + toggleMobileSidebar: action, setTheme: action, toggleProfileSidebar: action, toggleWorkspaceAnalyticsSidebar: action, @@ -61,6 +66,19 @@ export class ThemeStore implements IThemeStore { localStorage.setItem("app_sidebar_collapsed", this.sidebarCollapsed.toString()); }; + /** + * Toggle mobile sidebar collapsed state + * @param collapsed + */ + toggleMobileSidebar = (collapsed?: boolean) => { + if (collapsed === undefined) { + this.mobileSidebarCollapsed = !this.mobileSidebarCollapsed; + } else { + this.mobileSidebarCollapsed = collapsed; + } + localStorage.setItem("mobile_sidebar_collapsed", this.mobileSidebarCollapsed.toString()); + }; + /** * Toggle the profile sidebar collapsed state * @param collapsed @@ -88,13 +106,13 @@ export class ThemeStore implements IThemeStore { }; toggleIssueDetailSidebar = (collapsed?: boolean) => { - if(collapsed === undefined) { + if (collapsed === undefined) { this.issueDetailSidebarCollapsed = !this.issueDetailSidebarCollapsed; } else { this.issueDetailSidebarCollapsed = collapsed; } localStorage.setItem("issue_detail_sidebar_collapsed", this.issueDetailSidebarCollapsed.toString()); - } + }; /** * Sets the user theme and applies it to the platform