mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
Merge branch 'feat-global-views' of github.com:makeplane/plane into feat-global-views
This commit is contained in:
commit
9d39096ed2
@ -137,7 +137,7 @@ services:
|
|||||||
dockerfile: Dockerfile.dev
|
dockerfile: Dockerfile.dev
|
||||||
args:
|
args:
|
||||||
DOCKER_BUILDKIT: 1
|
DOCKER_BUILDKIT: 1
|
||||||
restart: no
|
restart: "no"
|
||||||
networks:
|
networks:
|
||||||
- dev_env
|
- dev_env
|
||||||
volumes:
|
volumes:
|
||||||
|
10
packages/types/src/projects.d.ts
vendored
10
packages/types/src/projects.d.ts
vendored
@ -1,5 +1,11 @@
|
|||||||
import { EUserProjectRoles } from "constants/project";
|
import { EUserProjectRoles } from "constants/project";
|
||||||
import type { IUser, IUserLite, IWorkspace, IWorkspaceLite, TStateGroups } from ".";
|
import type {
|
||||||
|
IUser,
|
||||||
|
IUserLite,
|
||||||
|
IWorkspace,
|
||||||
|
IWorkspaceLite,
|
||||||
|
TStateGroups,
|
||||||
|
} from ".";
|
||||||
|
|
||||||
export interface IProject {
|
export interface IProject {
|
||||||
archive_in: number;
|
archive_in: number;
|
||||||
@ -117,7 +123,7 @@ export type TProjectIssuesSearchParams = {
|
|||||||
parent?: boolean;
|
parent?: boolean;
|
||||||
issue_relation?: boolean;
|
issue_relation?: boolean;
|
||||||
cycle?: boolean;
|
cycle?: boolean;
|
||||||
module?: string[];
|
module?: string;
|
||||||
sub_issue?: boolean;
|
sub_issue?: boolean;
|
||||||
issue_id?: string;
|
issue_id?: string;
|
||||||
workspace_search: boolean;
|
workspace_search: boolean;
|
||||||
|
@ -78,7 +78,6 @@ export const ExistingIssuesListModal: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isOpen || !workspaceSlug || !projectId) return;
|
if (!isOpen || !workspaceSlug || !projectId) return;
|
||||||
if (issues.length <= 0) setIsSearching(true);
|
|
||||||
|
|
||||||
projectService
|
projectService
|
||||||
.projectIssuesSearch(workspaceSlug as string, projectId as string, {
|
.projectIssuesSearch(workspaceSlug as string, projectId as string, {
|
||||||
@ -88,16 +87,7 @@ export const ExistingIssuesListModal: React.FC<Props> = (props) => {
|
|||||||
})
|
})
|
||||||
.then((res) => setIssues(res))
|
.then((res) => setIssues(res))
|
||||||
.finally(() => setIsSearching(false));
|
.finally(() => setIsSearching(false));
|
||||||
}, [issues, debouncedSearchTerm, isOpen, isWorkspaceLevel, projectId, searchParams, workspaceSlug]);
|
}, [debouncedSearchTerm, isOpen, isWorkspaceLevel, projectId, searchParams, workspaceSlug]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setSearchTerm("");
|
|
||||||
setIssues([]);
|
|
||||||
setSelectedIssues([]);
|
|
||||||
setIsSearching(false);
|
|
||||||
setIsSubmitting(false);
|
|
||||||
setIsWorkspaceLevel(false);
|
|
||||||
}, [isOpen]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -5,7 +5,7 @@ import { useCycle } from "hooks/store";
|
|||||||
// components
|
// components
|
||||||
import { CyclesBoard, CyclesList, CyclesListGanttChartView } from "components/cycles";
|
import { CyclesBoard, CyclesList, CyclesListGanttChartView } from "components/cycles";
|
||||||
// ui components
|
// ui components
|
||||||
import { Loader } from "@plane/ui";
|
import { CycleModuleBoardLayout, CycleModuleListLayout, GanttLayoutLoader } from "components/ui";
|
||||||
// types
|
// types
|
||||||
import { TCycleLayout, TCycleView } from "@plane/types";
|
import { TCycleLayout, TCycleView } from "@plane/types";
|
||||||
|
|
||||||
@ -25,6 +25,7 @@ export const CyclesView: FC<ICyclesView> = observer((props) => {
|
|||||||
currentProjectDraftCycleIds,
|
currentProjectDraftCycleIds,
|
||||||
currentProjectUpcomingCycleIds,
|
currentProjectUpcomingCycleIds,
|
||||||
currentProjectCycleIds,
|
currentProjectCycleIds,
|
||||||
|
loader,
|
||||||
} = useCycle();
|
} = useCycle();
|
||||||
|
|
||||||
const cyclesList =
|
const cyclesList =
|
||||||
@ -36,25 +37,22 @@ export const CyclesView: FC<ICyclesView> = observer((props) => {
|
|||||||
? currentProjectUpcomingCycleIds
|
? currentProjectUpcomingCycleIds
|
||||||
: currentProjectCycleIds;
|
: currentProjectCycleIds;
|
||||||
|
|
||||||
|
if (loader || !cyclesList)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{layout === "list" && <CycleModuleListLayout />}
|
||||||
|
{layout === "board" && <CycleModuleBoardLayout />}
|
||||||
|
{layout === "gantt" && <GanttLayoutLoader />}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{layout === "list" && (
|
{layout === "list" && (
|
||||||
<>
|
|
||||||
{cyclesList ? (
|
|
||||||
<CyclesList cycleIds={cyclesList} filter={filter} workspaceSlug={workspaceSlug} projectId={projectId} />
|
<CyclesList cycleIds={cyclesList} filter={filter} workspaceSlug={workspaceSlug} projectId={projectId} />
|
||||||
) : (
|
|
||||||
<Loader className="space-y-4 p-8">
|
|
||||||
<Loader.Item height="50px" />
|
|
||||||
<Loader.Item height="50px" />
|
|
||||||
<Loader.Item height="50px" />
|
|
||||||
</Loader>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{layout === "board" && (
|
{layout === "board" && (
|
||||||
<>
|
|
||||||
{cyclesList ? (
|
|
||||||
<CyclesBoard
|
<CyclesBoard
|
||||||
cycleIds={cyclesList}
|
cycleIds={cyclesList}
|
||||||
filter={filter}
|
filter={filter}
|
||||||
@ -62,29 +60,9 @@ export const CyclesView: FC<ICyclesView> = observer((props) => {
|
|||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
peekCycle={peekCycle}
|
peekCycle={peekCycle}
|
||||||
/>
|
/>
|
||||||
) : (
|
|
||||||
<Loader className="grid grid-cols-1 gap-9 p-8 md:grid-cols-2 lg:grid-cols-3">
|
|
||||||
<Loader.Item height="200px" />
|
|
||||||
<Loader.Item height="200px" />
|
|
||||||
<Loader.Item height="200px" />
|
|
||||||
</Loader>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{layout === "gantt" && (
|
{layout === "gantt" && <CyclesListGanttChartView cycleIds={cyclesList} workspaceSlug={workspaceSlug} />}
|
||||||
<>
|
|
||||||
{cyclesList ? (
|
|
||||||
<CyclesListGanttChartView cycleIds={cyclesList} workspaceSlug={workspaceSlug} />
|
|
||||||
) : (
|
|
||||||
<Loader className="space-y-4">
|
|
||||||
<Loader.Item height="50px" />
|
|
||||||
<Loader.Item height="50px" />
|
|
||||||
<Loader.Item height="50px" />
|
|
||||||
</Loader>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -15,7 +15,8 @@ import { IntegrationService } from "services/integrations";
|
|||||||
import { Exporter, SingleExport } from "components/exporter";
|
import { Exporter, SingleExport } from "components/exporter";
|
||||||
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
||||||
// ui
|
// ui
|
||||||
import { Button, Loader } from "@plane/ui";
|
import { Button } from "@plane/ui";
|
||||||
|
import { ImportExportSettingsLoader } from "components/ui";
|
||||||
// icons
|
// icons
|
||||||
import { MoveLeft, MoveRight, RefreshCw } from "lucide-react";
|
import { MoveLeft, MoveRight, RefreshCw } from "lucide-react";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
@ -158,12 +159,7 @@ const IntegrationGuide = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<Loader className="mt-6 grid grid-cols-1 gap-3">
|
<ImportExportSettingsLoader />
|
||||||
<Loader.Item height="40px" width="100%" />
|
|
||||||
<Loader.Item height="40px" width="100%" />
|
|
||||||
<Loader.Item height="40px" width="100%" />
|
|
||||||
<Loader.Item height="40px" width="100%" />
|
|
||||||
</Loader>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,7 +14,8 @@ import { IntegrationService } from "services/integrations";
|
|||||||
import { DeleteImportModal, GithubImporterRoot, JiraImporterRoot, SingleImport } from "components/integration";
|
import { DeleteImportModal, GithubImporterRoot, JiraImporterRoot, SingleImport } from "components/integration";
|
||||||
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
||||||
// ui
|
// ui
|
||||||
import { Button, Loader } from "@plane/ui";
|
import { Button } from "@plane/ui";
|
||||||
|
import { ImportExportSettingsLoader } from "components/ui";
|
||||||
// icons
|
// icons
|
||||||
import { RefreshCw } from "lucide-react";
|
import { RefreshCw } from "lucide-react";
|
||||||
// types
|
// types
|
||||||
@ -153,12 +154,7 @@ const IntegrationGuide = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<Loader className="mt-6 grid grid-cols-1 gap-3">
|
<ImportExportSettingsLoader />
|
||||||
<Loader.Item height="40px" width="100%" />
|
|
||||||
<Loader.Item height="40px" width="100%" />
|
|
||||||
<Loader.Item height="40px" width="100%" />
|
|
||||||
<Loader.Item height="40px" width="100%" />
|
|
||||||
</Loader>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -168,7 +168,7 @@ export const SingleIntegrationCard: React.FC<Props> = observer(({ integration })
|
|||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<Loader>
|
<Loader>
|
||||||
<Loader.Item height="35px" width="150px" />
|
<Loader.Item height="32px" width="64px" />
|
||||||
</Loader>
|
</Loader>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -70,7 +70,7 @@ export const ModuleEmptyState: React.FC<Props> = observer((props) => {
|
|||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
isOpen={moduleIssuesListModal}
|
isOpen={moduleIssuesListModal}
|
||||||
handleClose={() => setModuleIssuesListModal(false)}
|
handleClose={() => setModuleIssuesListModal(false)}
|
||||||
searchParams={{ module: moduleId != undefined ? [moduleId.toString()] : [] }}
|
searchParams={{ module: moduleId != undefined ? moduleId.toString() : "" }}
|
||||||
handleOnSubmit={handleAddIssuesToModule}
|
handleOnSubmit={handleAddIssuesToModule}
|
||||||
/>
|
/>
|
||||||
<div className="grid h-full w-full place-items-center">
|
<div className="grid h-full w-full place-items-center">
|
||||||
|
@ -36,22 +36,34 @@ export const CycleAppliedFiltersRoot: React.FC = observer(() => {
|
|||||||
const handleRemoveFilter = (key: keyof IIssueFilterOptions, value: string | null) => {
|
const handleRemoveFilter = (key: keyof IIssueFilterOptions, value: string | null) => {
|
||||||
if (!workspaceSlug || !projectId || !cycleId) return;
|
if (!workspaceSlug || !projectId || !cycleId) return;
|
||||||
if (!value) {
|
if (!value) {
|
||||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.FILTERS, {
|
updateFilters(
|
||||||
|
workspaceSlug,
|
||||||
|
projectId,
|
||||||
|
EIssueFilterType.FILTERS,
|
||||||
|
{
|
||||||
[key]: null,
|
[key]: null,
|
||||||
});
|
},
|
||||||
|
cycleId
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let newValues = issueFilters?.filters?.[key] ?? [];
|
let newValues = issueFilters?.filters?.[key] ?? [];
|
||||||
newValues = newValues.filter((val) => val !== value);
|
newValues = newValues.filter((val) => val !== value);
|
||||||
|
|
||||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.FILTERS, {
|
updateFilters(
|
||||||
|
workspaceSlug,
|
||||||
|
projectId,
|
||||||
|
EIssueFilterType.FILTERS,
|
||||||
|
{
|
||||||
[key]: newValues,
|
[key]: newValues,
|
||||||
});
|
},
|
||||||
|
cycleId
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClearAllFilters = () => {
|
const handleClearAllFilters = () => {
|
||||||
if (!workspaceSlug || !projectId) return;
|
if (!workspaceSlug || !projectId || !cycleId) return;
|
||||||
const newFilters: IIssueFilterOptions = {};
|
const newFilters: IIssueFilterOptions = {};
|
||||||
Object.keys(userFilters ?? {}).forEach((key) => {
|
Object.keys(userFilters ?? {}).forEach((key) => {
|
||||||
newFilters[key as keyof IIssueFilterOptions] = null;
|
newFilters[key as keyof IIssueFilterOptions] = null;
|
||||||
|
@ -33,24 +33,36 @@ export const ModuleAppliedFiltersRoot: React.FC = observer(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const handleRemoveFilter = (key: keyof IIssueFilterOptions, value: string | null) => {
|
const handleRemoveFilter = (key: keyof IIssueFilterOptions, value: string | null) => {
|
||||||
if (!workspaceSlug || !projectId) return;
|
if (!workspaceSlug || !projectId || !moduleId) return;
|
||||||
if (!value) {
|
if (!value) {
|
||||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.FILTERS, {
|
updateFilters(
|
||||||
|
workspaceSlug,
|
||||||
|
projectId,
|
||||||
|
EIssueFilterType.FILTERS,
|
||||||
|
{
|
||||||
[key]: null,
|
[key]: null,
|
||||||
});
|
},
|
||||||
|
moduleId
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let newValues = issueFilters?.filters?.[key] ?? [];
|
let newValues = issueFilters?.filters?.[key] ?? [];
|
||||||
newValues = newValues.filter((val) => val !== value);
|
newValues = newValues.filter((val) => val !== value);
|
||||||
|
|
||||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.FILTERS, {
|
updateFilters(
|
||||||
|
workspaceSlug,
|
||||||
|
projectId,
|
||||||
|
EIssueFilterType.FILTERS,
|
||||||
|
{
|
||||||
[key]: newValues,
|
[key]: newValues,
|
||||||
});
|
},
|
||||||
|
moduleId
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClearAllFilters = () => {
|
const handleClearAllFilters = () => {
|
||||||
if (!workspaceSlug || !projectId) return;
|
if (!workspaceSlug || !projectId || !moduleId) return;
|
||||||
const newFilters: IIssueFilterOptions = {};
|
const newFilters: IIssueFilterOptions = {};
|
||||||
Object.keys(userFilters ?? {}).forEach((key) => {
|
Object.keys(userFilters ?? {}).forEach((key) => {
|
||||||
newFilters[key as keyof IIssueFilterOptions] = null;
|
newFilters[key as keyof IIssueFilterOptions] = null;
|
||||||
|
@ -138,14 +138,14 @@ export const KanbanIssueBlock: React.FC<IssueBlockProps> = memo((props) => {
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded border-[0.5px] border-custom-border-200 bg-custom-background-100 px-3 py-2 text-sm transition-all hover:border-custom-border-400",
|
"rounded border-[0.5px] border-custom-border-200 bg-custom-background-100 text-sm transition-all hover:border-custom-border-400",
|
||||||
{ "hover:cursor-grab": !isDragDisabled },
|
{ "hover:cursor-grab": !isDragDisabled },
|
||||||
{ "border-custom-primary-100": snapshot.isDragging },
|
{ "border-custom-primary-100": snapshot.isDragging },
|
||||||
{ "border border-custom-primary-70 hover:border-custom-primary-70": peekIssueId === issue.id }
|
{ "border border-custom-primary-70 hover:border-custom-primary-70": peekIssueId === issue.id }
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<RenderIfVisible
|
<RenderIfVisible
|
||||||
classNames="space-y-2"
|
classNames="space-y-2 px-3 py-2"
|
||||||
root={scrollableContainerRef}
|
root={scrollableContainerRef}
|
||||||
defaultHeight="100px"
|
defaultHeight="100px"
|
||||||
horizonatlOffset={50}
|
horizonatlOffset={50}
|
||||||
|
@ -59,7 +59,7 @@ export const HeaderGroupByCard: FC<IHeaderGroupByCard> = observer((props) => {
|
|||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
|
||||||
const renderExistingIssueModal = moduleId || cycleId;
|
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[]) => {
|
const handleAddIssuesToView = async (data: ISearchIssueResponse[]) => {
|
||||||
if (!workspaceSlug || !projectId) return;
|
if (!workspaceSlug || !projectId) return;
|
||||||
|
@ -67,10 +67,13 @@ export const handleDragDrop = async (
|
|||||||
|
|
||||||
let updateIssue: any = {};
|
let updateIssue: any = {};
|
||||||
|
|
||||||
const sourceColumnId = (source?.droppableId && source?.droppableId.split("__")) || null;
|
const sourceDroppableId = source?.droppableId;
|
||||||
const destinationColumnId = (destination?.droppableId && destination?.droppableId.split("__")) || null;
|
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 sourceGroupByColumnId = sourceColumnId[0] || null;
|
||||||
const destinationGroupByColumnId = destinationColumnId[0] || null;
|
const destinationGroupByColumnId = destinationColumnId[0] || null;
|
||||||
@ -123,7 +126,11 @@ export const handleDragDrop = async (
|
|||||||
// for both horizontal and vertical dnd
|
// for both horizontal and vertical dnd
|
||||||
updateIssue = {
|
updateIssue = {
|
||||||
...updateIssue,
|
...updateIssue,
|
||||||
...handleSortOrder(destinationIssues, destination.index, issueMap),
|
...handleSortOrder(
|
||||||
|
sourceDroppableId === destinationDroppableId ? sourceIssues : destinationIssues,
|
||||||
|
destination.index,
|
||||||
|
issueMap
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (subGroupBy && sourceSubGroupByColumnId && destinationSubGroupByColumnId) {
|
if (subGroupBy && sourceSubGroupByColumnId && destinationSubGroupByColumnId) {
|
||||||
|
@ -41,7 +41,7 @@ export const HeaderGroupByCard = observer(
|
|||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
|
||||||
const renderExistingIssueModal = moduleId || cycleId;
|
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[]) => {
|
const handleAddIssuesToView = async (data: ISearchIssueResponse[]) => {
|
||||||
if (!workspaceSlug || !projectId) return;
|
if (!workspaceSlug || !projectId) return;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, useMemo } from "react";
|
import React, { Fragment, useCallback, useMemo } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
@ -12,8 +12,7 @@ import { GlobalViewsAppliedFiltersRoot, IssuePeekOverview } from "components/iss
|
|||||||
import { SpreadsheetView } from "components/issues/issue-layouts";
|
import { SpreadsheetView } from "components/issues/issue-layouts";
|
||||||
import { AllIssueQuickActions } from "components/issues/issue-layouts/quick-action-dropdowns";
|
import { AllIssueQuickActions } from "components/issues/issue-layouts/quick-action-dropdowns";
|
||||||
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
||||||
// ui
|
import { SpreadsheetLayoutLoader } from "components/ui";
|
||||||
import { Spinner } from "@plane/ui";
|
|
||||||
// types
|
// types
|
||||||
import { TIssue, IIssueDisplayFilterOptions } from "@plane/types";
|
import { TIssue, IIssueDisplayFilterOptions } from "@plane/types";
|
||||||
import { EIssueActions } from "../types";
|
import { EIssueActions } from "../types";
|
||||||
@ -178,17 +177,15 @@ export const AllIssueLayoutRoot: React.FC = observer(() => {
|
|||||||
|
|
||||||
const isEditingAllowed = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
const isEditingAllowed = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
||||||
|
|
||||||
|
if (loader === "init-loader" || !globalViewId || globalViewId !== dataViewId || !issueIds) {
|
||||||
|
return <SpreadsheetLayoutLoader />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex h-full w-full flex-col overflow-hidden">
|
<div className="relative flex h-full w-full flex-col overflow-hidden">
|
||||||
{!globalViewId || globalViewId !== dataViewId || loader === "init-loader" || !issueIds ? (
|
<div className="relative h-full w-full overflow-auto">
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
|
||||||
<Spinner />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<GlobalViewsAppliedFiltersRoot globalViewId={globalViewId} />
|
<GlobalViewsAppliedFiltersRoot globalViewId={globalViewId} />
|
||||||
|
{issueIds.length === 0 ? (
|
||||||
{(issueIds ?? {}).length == 0 ? (
|
|
||||||
<EmptyState
|
<EmptyState
|
||||||
image={emptyStateImage}
|
image={emptyStateImage}
|
||||||
title={(workspaceProjectIds ?? []).length > 0 ? currentViewDetails.title : "No project"}
|
title={(workspaceProjectIds ?? []).length > 0 ? currentViewDetails.title : "No project"}
|
||||||
@ -220,7 +217,7 @@ export const AllIssueLayoutRoot: React.FC = observer(() => {
|
|||||||
disabled={!isEditingAllowed}
|
disabled={!isEditingAllowed}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="relative h-full w-full overflow-auto">
|
<Fragment>
|
||||||
<SpreadsheetView
|
<SpreadsheetView
|
||||||
displayProperties={issueFilters?.displayProperties ?? {}}
|
displayProperties={issueFilters?.displayProperties ?? {}}
|
||||||
displayFilters={issueFilters?.displayFilters ?? {}}
|
displayFilters={issueFilters?.displayFilters ?? {}}
|
||||||
@ -231,13 +228,11 @@ export const AllIssueLayoutRoot: React.FC = observer(() => {
|
|||||||
canEditProperties={canEditProperties}
|
canEditProperties={canEditProperties}
|
||||||
viewId={globalViewId}
|
viewId={globalViewId}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* peek overview */}
|
{/* peek overview */}
|
||||||
<IssuePeekOverview />
|
<IssuePeekOverview />
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React from "react";
|
import React, { Fragment } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
@ -13,7 +13,7 @@ import {
|
|||||||
} from "components/issues";
|
} from "components/issues";
|
||||||
import { EIssuesStoreType } from "constants/issue";
|
import { EIssuesStoreType } from "constants/issue";
|
||||||
// ui
|
// ui
|
||||||
import { Spinner } from "@plane/ui";
|
import { ListLayoutLoader } from "components/ui";
|
||||||
|
|
||||||
export const ArchivedIssueLayoutRoot: React.FC = observer(() => {
|
export const ArchivedIssueLayoutRoot: React.FC = observer(() => {
|
||||||
// router
|
// router
|
||||||
@ -36,30 +36,26 @@ export const ArchivedIssueLayoutRoot: React.FC = observer(() => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (issues?.loader === "init-loader" || !issues?.groupedIssueIds) {
|
||||||
|
return <ListLayoutLoader />;
|
||||||
|
}
|
||||||
|
|
||||||
if (!workspaceSlug || !projectId) return <></>;
|
if (!workspaceSlug || !projectId) return <></>;
|
||||||
return (
|
return (
|
||||||
<div className="relative flex h-full w-full flex-col overflow-hidden">
|
<div className="relative flex h-full w-full flex-col overflow-hidden">
|
||||||
<ArchivedIssueAppliedFiltersRoot />
|
<ArchivedIssueAppliedFiltersRoot />
|
||||||
|
|
||||||
{issues?.loader === "init-loader" ? (
|
{issues?.groupedIssueIds?.length === 0 ? (
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
<div className="relative h-full w-full overflow-y-auto">
|
||||||
<Spinner />
|
<ProjectArchivedEmptyState />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<Fragment>
|
||||||
{!issues?.groupedIssueIds ? (
|
|
||||||
<ProjectArchivedEmptyState />
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<div className="relative h-full w-full overflow-auto">
|
<div className="relative h-full w-full overflow-auto">
|
||||||
<ArchivedIssueListLayout />
|
<ArchivedIssueListLayout />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* peek overview */}
|
|
||||||
<IssuePeekOverview is_archived />
|
<IssuePeekOverview is_archived />
|
||||||
</>
|
</Fragment>
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from "react";
|
import React, { Fragment, useState } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
@ -16,6 +16,7 @@ import {
|
|||||||
IssuePeekOverview,
|
IssuePeekOverview,
|
||||||
} from "components/issues";
|
} from "components/issues";
|
||||||
import { TransferIssues, TransferIssuesModal } from "components/cycles";
|
import { TransferIssues, TransferIssuesModal } from "components/cycles";
|
||||||
|
import { ActiveLoader } from "components/ui";
|
||||||
// ui
|
// ui
|
||||||
import { Spinner } from "@plane/ui";
|
import { Spinner } from "@plane/ui";
|
||||||
// constants
|
// constants
|
||||||
@ -53,29 +54,39 @@ export const CycleLayoutRoot: React.FC = observer(() => {
|
|||||||
const cycleStatus = cycleDetails?.status?.toLocaleLowerCase() ?? "draft";
|
const cycleStatus = cycleDetails?.status?.toLocaleLowerCase() ?? "draft";
|
||||||
|
|
||||||
if (!workspaceSlug || !projectId || !cycleId) return <></>;
|
if (!workspaceSlug || !projectId || !cycleId) return <></>;
|
||||||
|
|
||||||
|
if (issues?.loader === "init-loader" || !issues?.groupedIssueIds) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{activeLayout ? (
|
||||||
|
<ActiveLoader layout={activeLayout} />
|
||||||
|
) : (
|
||||||
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TransferIssuesModal handleClose={() => setTransferIssuesModal(false)} isOpen={transferIssuesModal} />
|
<TransferIssuesModal handleClose={() => setTransferIssuesModal(false)} isOpen={transferIssuesModal} />
|
||||||
|
|
||||||
<div className="relative flex h-full w-full flex-col overflow-hidden">
|
<div className="relative flex h-full w-full flex-col overflow-hidden">
|
||||||
{cycleStatus === "completed" && <TransferIssues handleClick={() => setTransferIssuesModal(true)} />}
|
{cycleStatus === "completed" && <TransferIssues handleClick={() => setTransferIssuesModal(true)} />}
|
||||||
<CycleAppliedFiltersRoot />
|
<CycleAppliedFiltersRoot />
|
||||||
|
|
||||||
{issues?.loader === "init-loader" || !issues?.groupedIssueIds ? (
|
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
|
||||||
<Spinner />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{issues?.groupedIssueIds?.length === 0 ? (
|
{issues?.groupedIssueIds?.length === 0 ? (
|
||||||
|
<div className="relative h-full w-full overflow-y-auto">
|
||||||
<CycleEmptyState
|
<CycleEmptyState
|
||||||
workspaceSlug={workspaceSlug.toString()}
|
workspaceSlug={workspaceSlug.toString()}
|
||||||
projectId={projectId.toString()}
|
projectId={projectId.toString()}
|
||||||
cycleId={cycleId.toString()}
|
cycleId={cycleId.toString()}
|
||||||
activeLayout={activeLayout}
|
activeLayout={activeLayout}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<Fragment>
|
||||||
<div className="h-full w-full overflow-auto">
|
<div className="h-full w-full overflow-auto">
|
||||||
{activeLayout === "list" ? (
|
{activeLayout === "list" ? (
|
||||||
<CycleListLayout />
|
<CycleListLayout />
|
||||||
@ -91,9 +102,7 @@ export const CycleLayoutRoot: React.FC = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
{/* peek overview */}
|
{/* peek overview */}
|
||||||
<IssuePeekOverview />
|
<IssuePeekOverview />
|
||||||
</>
|
</Fragment>
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
@ -9,6 +9,7 @@ import { DraftIssueAppliedFiltersRoot } from "../filters/applied-filters/roots/d
|
|||||||
import { DraftIssueListLayout } from "../list/roots/draft-issue-root";
|
import { DraftIssueListLayout } from "../list/roots/draft-issue-root";
|
||||||
import { ProjectDraftEmptyState } from "../empty-states";
|
import { ProjectDraftEmptyState } from "../empty-states";
|
||||||
import { IssuePeekOverview } from "components/issues/peek-overview";
|
import { IssuePeekOverview } from "components/issues/peek-overview";
|
||||||
|
import { ActiveLoader } from "components/ui";
|
||||||
// ui
|
// ui
|
||||||
import { Spinner } from "@plane/ui";
|
import { Spinner } from "@plane/ui";
|
||||||
import { DraftKanBanLayout } from "../kanban/roots/draft-issue-root";
|
import { DraftKanBanLayout } from "../kanban/roots/draft-issue-root";
|
||||||
@ -39,18 +40,29 @@ export const DraftIssueLayoutRoot: React.FC = observer(() => {
|
|||||||
const activeLayout = issuesFilter?.issueFilters?.displayFilters?.layout || undefined;
|
const activeLayout = issuesFilter?.issueFilters?.displayFilters?.layout || undefined;
|
||||||
|
|
||||||
if (!workspaceSlug || !projectId) return <></>;
|
if (!workspaceSlug || !projectId) return <></>;
|
||||||
|
|
||||||
|
if (issues?.loader === "init-loader" || !issues?.groupedIssueIds) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{activeLayout ? (
|
||||||
|
<ActiveLoader layout={activeLayout} />
|
||||||
|
) : (
|
||||||
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex h-full w-full flex-col overflow-hidden">
|
<div className="relative flex h-full w-full flex-col overflow-hidden">
|
||||||
<DraftIssueAppliedFiltersRoot />
|
<DraftIssueAppliedFiltersRoot />
|
||||||
|
|
||||||
{issues?.loader === "init-loader" ? (
|
{issues?.groupedIssueIds?.length === 0 ? (
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
<div className="relative h-full w-full overflow-y-auto">
|
||||||
<Spinner />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{!issues?.groupedIssueIds ? (
|
|
||||||
<ProjectDraftEmptyState />
|
<ProjectDraftEmptyState />
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="relative h-full w-full overflow-auto">
|
<div className="relative h-full w-full overflow-auto">
|
||||||
{activeLayout === "list" ? (
|
{activeLayout === "list" ? (
|
||||||
@ -62,8 +74,6 @@ export const DraftIssueLayoutRoot: React.FC = observer(() => {
|
|||||||
<IssuePeekOverview />
|
<IssuePeekOverview />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React from "react";
|
import React, { Fragment } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
@ -15,6 +15,7 @@ import {
|
|||||||
ModuleListLayout,
|
ModuleListLayout,
|
||||||
ModuleSpreadsheetLayout,
|
ModuleSpreadsheetLayout,
|
||||||
} from "components/issues";
|
} from "components/issues";
|
||||||
|
import { ActiveLoader } from "components/ui";
|
||||||
// ui
|
// ui
|
||||||
import { Spinner } from "@plane/ui";
|
import { Spinner } from "@plane/ui";
|
||||||
// constants
|
// constants
|
||||||
@ -44,28 +45,39 @@ export const ModuleLayoutRoot: React.FC = observer(() => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!workspaceSlug || !projectId || !moduleId) return <></>;
|
||||||
|
|
||||||
const activeLayout = issuesFilter?.issueFilters?.displayFilters?.layout || undefined;
|
const activeLayout = issuesFilter?.issueFilters?.displayFilters?.layout || undefined;
|
||||||
|
|
||||||
if (!workspaceSlug || !projectId || !moduleId) return <></>;
|
if (issues?.loader === "init-loader" || !issues?.groupedIssueIds) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{activeLayout ? (
|
||||||
|
<ActiveLoader layout={activeLayout} />
|
||||||
|
) : (
|
||||||
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex h-full w-full flex-col overflow-hidden">
|
<div className="relative flex h-full w-full flex-col overflow-hidden">
|
||||||
<ModuleAppliedFiltersRoot />
|
<ModuleAppliedFiltersRoot />
|
||||||
|
|
||||||
{issues?.loader === "init-loader" || !issues?.groupedIssueIds ? (
|
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
|
||||||
<Spinner />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{issues?.groupedIssueIds?.length === 0 ? (
|
{issues?.groupedIssueIds?.length === 0 ? (
|
||||||
|
<div className="relative h-full w-full overflow-y-auto">
|
||||||
<ModuleEmptyState
|
<ModuleEmptyState
|
||||||
workspaceSlug={workspaceSlug.toString()}
|
workspaceSlug={workspaceSlug.toString()}
|
||||||
projectId={projectId.toString()}
|
projectId={projectId.toString()}
|
||||||
moduleId={moduleId.toString()}
|
moduleId={moduleId.toString()}
|
||||||
activeLayout={activeLayout}
|
activeLayout={activeLayout}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<Fragment>
|
||||||
<div className="h-full w-full overflow-auto">
|
<div className="h-full w-full overflow-auto">
|
||||||
{activeLayout === "list" ? (
|
{activeLayout === "list" ? (
|
||||||
<ModuleListLayout />
|
<ModuleListLayout />
|
||||||
@ -81,9 +93,7 @@ export const ModuleLayoutRoot: React.FC = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
{/* peek overview */}
|
{/* peek overview */}
|
||||||
<IssuePeekOverview />
|
<IssuePeekOverview />
|
||||||
</>
|
</Fragment>
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { FC } from "react";
|
import { FC, Fragment } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
@ -17,6 +17,8 @@ import {
|
|||||||
import { Spinner } from "@plane/ui";
|
import { Spinner } from "@plane/ui";
|
||||||
// hooks
|
// hooks
|
||||||
import { useIssues } from "hooks/store";
|
import { useIssues } from "hooks/store";
|
||||||
|
// helpers
|
||||||
|
import { ActiveLoader } from "components/ui";
|
||||||
// constants
|
// constants
|
||||||
import { EIssuesStoreType } from "constants/issue";
|
import { EIssuesStoreType } from "constants/issue";
|
||||||
|
|
||||||
@ -41,22 +43,19 @@ export const ProjectLayoutRoot: FC = observer(() => {
|
|||||||
const activeLayout = issuesFilter?.issueFilters?.displayFilters?.layout;
|
const activeLayout = issuesFilter?.issueFilters?.displayFilters?.layout;
|
||||||
|
|
||||||
if (!workspaceSlug || !projectId) return <></>;
|
if (!workspaceSlug || !projectId) return <></>;
|
||||||
|
|
||||||
|
if (issues?.loader === "init-loader" || !issues?.groupedIssueIds) {
|
||||||
|
return <>{activeLayout && <ActiveLoader layout={activeLayout} />}</>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex h-full w-full flex-col overflow-hidden">
|
<div className="relative flex h-full w-full flex-col overflow-hidden">
|
||||||
<ProjectAppliedFiltersRoot />
|
<ProjectAppliedFiltersRoot />
|
||||||
|
|
||||||
{issues?.loader === "init-loader" || !issues?.groupedIssueIds ? (
|
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
|
||||||
<Spinner />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{issues?.groupedIssueIds?.length === 0 ? (
|
{issues?.groupedIssueIds?.length === 0 ? (
|
||||||
<div className="relative h-full w-full overflow-y-auto">
|
|
||||||
<ProjectEmptyState />
|
<ProjectEmptyState />
|
||||||
</div>
|
|
||||||
) : (
|
) : (
|
||||||
<>
|
<Fragment>
|
||||||
<div className="relative h-full w-full overflow-auto bg-custom-background-90">
|
<div className="relative h-full w-full overflow-auto bg-custom-background-90">
|
||||||
{/* mutation loader */}
|
{/* mutation loader */}
|
||||||
{issues?.loader === "mutation" && (
|
{issues?.loader === "mutation" && (
|
||||||
@ -64,7 +63,6 @@ export const ProjectLayoutRoot: FC = observer(() => {
|
|||||||
<Spinner className="w-4 h-4" />
|
<Spinner className="w-4 h-4" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{activeLayout === "list" ? (
|
{activeLayout === "list" ? (
|
||||||
<ListLayout />
|
<ListLayout />
|
||||||
) : activeLayout === "kanban" ? (
|
) : activeLayout === "kanban" ? (
|
||||||
@ -80,9 +78,7 @@ export const ProjectLayoutRoot: FC = observer(() => {
|
|||||||
|
|
||||||
{/* peek overview */}
|
{/* peek overview */}
|
||||||
<IssuePeekOverview />
|
<IssuePeekOverview />
|
||||||
</>
|
</Fragment>
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useMemo } from "react";
|
import React, { Fragment, useMemo } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
@ -16,6 +16,7 @@ import {
|
|||||||
ProjectViewSpreadsheetLayout,
|
ProjectViewSpreadsheetLayout,
|
||||||
} from "components/issues";
|
} from "components/issues";
|
||||||
import { Spinner } from "@plane/ui";
|
import { Spinner } from "@plane/ui";
|
||||||
|
import { ActiveLoader } from "components/ui";
|
||||||
// constants
|
// constants
|
||||||
import { EIssuesStoreType } from "constants/issue";
|
import { EIssuesStoreType } from "constants/issue";
|
||||||
// types
|
// types
|
||||||
@ -63,20 +64,31 @@ export const ProjectViewLayoutRoot: React.FC = observer(() => {
|
|||||||
const activeLayout = issuesFilter?.issueFilters?.displayFilters?.layout;
|
const activeLayout = issuesFilter?.issueFilters?.displayFilters?.layout;
|
||||||
|
|
||||||
if (!workspaceSlug || !projectId || !viewId) return <></>;
|
if (!workspaceSlug || !projectId || !viewId) return <></>;
|
||||||
|
|
||||||
|
if (issues?.loader === "init-loader" || !issues?.groupedIssueIds) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{activeLayout ? (
|
||||||
|
<ActiveLoader layout={activeLayout} />
|
||||||
|
) : (
|
||||||
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex h-full w-full flex-col overflow-hidden">
|
<div className="relative flex h-full w-full flex-col overflow-hidden">
|
||||||
<ProjectViewAppliedFiltersRoot />
|
<ProjectViewAppliedFiltersRoot />
|
||||||
|
|
||||||
{issues?.loader === "init-loader" || !issues?.groupedIssueIds ? (
|
{issues?.groupedIssueIds?.length === 0 ? (
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
<div className="relative h-full w-full overflow-y-auto">
|
||||||
<Spinner />
|
<ProjectViewEmptyState />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<Fragment>
|
||||||
{issues?.groupedIssueIds?.length === 0 ? (
|
|
||||||
<ProjectViewEmptyState />
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<div className="relative h-full w-full overflow-auto">
|
<div className="relative h-full w-full overflow-auto">
|
||||||
{activeLayout === "list" ? (
|
{activeLayout === "list" ? (
|
||||||
<ProjectViewListLayout issueActions={issueActions} />
|
<ProjectViewListLayout issueActions={issueActions} />
|
||||||
@ -93,9 +105,7 @@ export const ProjectViewLayoutRoot: React.FC = observer(() => {
|
|||||||
|
|
||||||
{/* peek overview */}
|
{/* peek overview */}
|
||||||
<IssuePeekOverview />
|
<IssuePeekOverview />
|
||||||
</>
|
</Fragment>
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -8,7 +8,7 @@ import useLocalStorage from "hooks/use-local-storage";
|
|||||||
import { ModuleCardItem, ModuleListItem, ModulePeekOverview, ModulesListGanttChartView } from "components/modules";
|
import { ModuleCardItem, ModuleListItem, ModulePeekOverview, ModulesListGanttChartView } from "components/modules";
|
||||||
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
||||||
// ui
|
// ui
|
||||||
import { Loader, Spinner } from "@plane/ui";
|
import { CycleModuleBoardLayout, CycleModuleListLayout, GanttLayoutLoader } from "components/ui";
|
||||||
// constants
|
// constants
|
||||||
import { EUserProjectRoles } from "constants/project";
|
import { EUserProjectRoles } from "constants/project";
|
||||||
import { MODULE_EMPTY_STATE_DETAILS } from "constants/empty-state";
|
import { MODULE_EMPTY_STATE_DETAILS } from "constants/empty-state";
|
||||||
@ -35,23 +35,13 @@ export const ModulesListView: React.FC = observer(() => {
|
|||||||
|
|
||||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||||
|
|
||||||
if (loader)
|
if (loader || !projectModuleIds)
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center h-full w-full">
|
<>
|
||||||
<Spinner />
|
{modulesView === "list" && <CycleModuleListLayout />}
|
||||||
</div>
|
{modulesView === "grid" && <CycleModuleBoardLayout />}
|
||||||
);
|
{modulesView === "gantt_chart" && <GanttLayoutLoader />}
|
||||||
|
</>
|
||||||
if (!projectModuleIds)
|
|
||||||
return (
|
|
||||||
<Loader className="grid grid-cols-3 gap-4 p-8">
|
|
||||||
<Loader.Item height="176px" />
|
|
||||||
<Loader.Item height="176px" />
|
|
||||||
<Loader.Item height="176px" />
|
|
||||||
<Loader.Item height="176px" />
|
|
||||||
<Loader.Item height="176px" />
|
|
||||||
<Loader.Item height="176px" />
|
|
||||||
</Loader>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -9,7 +9,8 @@ import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
|||||||
// components
|
// components
|
||||||
import { EmptyState } from "components/common";
|
import { EmptyState } from "components/common";
|
||||||
import { SnoozeNotificationModal, NotificationCard, NotificationHeader } from "components/notifications";
|
import { SnoozeNotificationModal, NotificationCard, NotificationHeader } from "components/notifications";
|
||||||
import { Loader, Tooltip } from "@plane/ui";
|
import { Tooltip } from "@plane/ui";
|
||||||
|
import { NotificationsLoader } from "components/ui";
|
||||||
// images
|
// images
|
||||||
import emptyNotification from "public/empty-state/notification.svg";
|
import emptyNotification from "public/empty-state/notification.svg";
|
||||||
// helpers
|
// helpers
|
||||||
@ -188,13 +189,7 @@ export const NotificationPopover = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<Loader className="space-y-4 overflow-y-auto p-5">
|
<NotificationsLoader />
|
||||||
<Loader.Item height="50px" />
|
|
||||||
<Loader.Item height="50px" />
|
|
||||||
<Loader.Item height="50px" />
|
|
||||||
<Loader.Item height="50px" />
|
|
||||||
<Loader.Item height="50px" />
|
|
||||||
</Loader>
|
|
||||||
)}
|
)}
|
||||||
</Popover.Panel>
|
</Popover.Panel>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
@ -7,7 +7,7 @@ import { useTheme } from "next-themes";
|
|||||||
import { ProfileIssuesListLayout } from "components/issues/issue-layouts/list/roots/profile-issues-root";
|
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 { ProfileIssuesKanBanLayout } from "components/issues/issue-layouts/kanban/roots/profile-issues-root";
|
||||||
import { IssuePeekOverview, ProfileIssuesAppliedFiltersRoot } from "components/issues";
|
import { IssuePeekOverview, ProfileIssuesAppliedFiltersRoot } from "components/issues";
|
||||||
import { Spinner } from "@plane/ui";
|
import { KanbanLayoutLoader, ListLayoutLoader } from "components/ui";
|
||||||
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
||||||
// hooks
|
// hooks
|
||||||
import { useIssues, useUser } from "hooks/store";
|
import { useIssues, useUser } from "hooks/store";
|
||||||
@ -36,10 +36,14 @@ export const ProfileIssuesPage = observer((props: IProfileIssuesPage) => {
|
|||||||
currentUser,
|
currentUser,
|
||||||
} = useUser();
|
} = useUser();
|
||||||
const {
|
const {
|
||||||
issues: { loader, groupedIssueIds, fetchIssues },
|
issues: { loader, groupedIssueIds, fetchIssues, setViewId },
|
||||||
issuesFilter: { issueFilters, fetchFilters },
|
issuesFilter: { issueFilters, fetchFilters },
|
||||||
} = useIssues(EIssuesStoreType.PROFILE);
|
} = useIssues(EIssuesStoreType.PROFILE);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setViewId(type);
|
||||||
|
}, [type]);
|
||||||
|
|
||||||
useSWR(
|
useSWR(
|
||||||
workspaceSlug && userId ? `CURRENT_WORKSPACE_PROFILE_ISSUES_${workspaceSlug}_${userId}_${type}` : null,
|
workspaceSlug && userId ? `CURRENT_WORKSPACE_PROFILE_ISSUES_${workspaceSlug}_${userId}_${type}` : null,
|
||||||
async () => {
|
async () => {
|
||||||
@ -57,15 +61,22 @@ export const ProfileIssuesPage = observer((props: IProfileIssuesPage) => {
|
|||||||
|
|
||||||
const isEditingAllowed = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
const isEditingAllowed = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
||||||
|
|
||||||
|
if (!groupedIssueIds || loader === "init-loader")
|
||||||
|
return <>{activeLayout === "list" ? <ListLayoutLoader /> : <KanbanLayoutLoader />}</>;
|
||||||
|
|
||||||
|
if (groupedIssueIds.length === 0) {
|
||||||
|
return (
|
||||||
|
<EmptyState
|
||||||
|
image={emptyStateImage}
|
||||||
|
title={PROFILE_EMPTY_STATE_DETAILS[type].title}
|
||||||
|
description={PROFILE_EMPTY_STATE_DETAILS[type].description}
|
||||||
|
size="sm"
|
||||||
|
disabled={!isEditingAllowed}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
{loader === "init-loader" ? (
|
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
|
||||||
<Spinner />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{groupedIssueIds ? (
|
|
||||||
<>
|
<>
|
||||||
<ProfileIssuesAppliedFiltersRoot />
|
<ProfileIssuesAppliedFiltersRoot />
|
||||||
<div className="-z-1 relative h-full w-full overflow-auto">
|
<div className="-z-1 relative h-full w-full overflow-auto">
|
||||||
@ -78,17 +89,5 @@ export const ProfileIssuesPage = observer((props: IProfileIssuesPage) => {
|
|||||||
{/* peek overview */}
|
{/* peek overview */}
|
||||||
<IssuePeekOverview />
|
<IssuePeekOverview />
|
||||||
</>
|
</>
|
||||||
) : (
|
|
||||||
<EmptyState
|
|
||||||
image={emptyStateImage}
|
|
||||||
title={PROFILE_EMPTY_STATE_DETAILS[type].title}
|
|
||||||
description={PROFILE_EMPTY_STATE_DETAILS[type].description}
|
|
||||||
size="sm"
|
|
||||||
disabled={!isEditingAllowed}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -4,8 +4,8 @@ import { useTheme } from "next-themes";
|
|||||||
import { useApplication, useEventTracker, useProject, useUser } from "hooks/store";
|
import { useApplication, useEventTracker, useProject, useUser } from "hooks/store";
|
||||||
// components
|
// components
|
||||||
import { ProjectCard } from "components/project";
|
import { ProjectCard } from "components/project";
|
||||||
import { Loader } from "@plane/ui";
|
|
||||||
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
||||||
|
import { ProjectsLoader } from "components/ui";
|
||||||
// constants
|
// constants
|
||||||
import { EUserWorkspaceRoles } from "constants/workspace";
|
import { EUserWorkspaceRoles } from "constants/workspace";
|
||||||
import { WORKSPACE_EMPTY_STATE_DETAILS } from "constants/empty-state";
|
import { WORKSPACE_EMPTY_STATE_DETAILS } from "constants/empty-state";
|
||||||
@ -27,17 +27,7 @@ export const ProjectCardList = observer(() => {
|
|||||||
|
|
||||||
const isEditingAllowed = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
const isEditingAllowed = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
||||||
|
|
||||||
if (!workspaceProjectIds)
|
if (!workspaceProjectIds) return <ProjectsLoader />;
|
||||||
return (
|
|
||||||
<Loader className="grid grid-cols-3 gap-4">
|
|
||||||
<Loader.Item height="100px" />
|
|
||||||
<Loader.Item height="100px" />
|
|
||||||
<Loader.Item height="100px" />
|
|
||||||
<Loader.Item height="100px" />
|
|
||||||
<Loader.Item height="100px" />
|
|
||||||
<Loader.Item height="100px" />
|
|
||||||
</Loader>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -6,7 +6,8 @@ import { useEventTracker, useMember } from "hooks/store";
|
|||||||
// components
|
// components
|
||||||
import { ProjectMemberListItem, SendProjectInvitationModal } from "components/project";
|
import { ProjectMemberListItem, SendProjectInvitationModal } from "components/project";
|
||||||
// ui
|
// ui
|
||||||
import { Button, Loader } from "@plane/ui";
|
import { Button } from "@plane/ui";
|
||||||
|
import { MembersSettingsLoader } from "components/ui";
|
||||||
|
|
||||||
export const ProjectMemberList: React.FC = observer(() => {
|
export const ProjectMemberList: React.FC = observer(() => {
|
||||||
// states
|
// states
|
||||||
@ -56,12 +57,7 @@ export const ProjectMemberList: React.FC = observer(() => {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{!projectMemberIds ? (
|
{!projectMemberIds ? (
|
||||||
<Loader className="space-y-5">
|
<MembersSettingsLoader />
|
||||||
<Loader.Item height="40px" />
|
|
||||||
<Loader.Item height="40px" />
|
|
||||||
<Loader.Item height="40px" />
|
|
||||||
<Loader.Item height="40px" />
|
|
||||||
</Loader>
|
|
||||||
) : (
|
) : (
|
||||||
<div className="divide-y divide-custom-border-100">
|
<div className="divide-y divide-custom-border-100">
|
||||||
{projectMemberIds.length > 0
|
{projectMemberIds.length > 0
|
||||||
|
@ -7,3 +7,4 @@ export * from "./markdown-to-component";
|
|||||||
export * from "./integration-and-import-export-banner";
|
export * from "./integration-and-import-export-banner";
|
||||||
export * from "./range-datepicker";
|
export * from "./range-datepicker";
|
||||||
export * from "./profile-empty-state";
|
export * from "./profile-empty-state";
|
||||||
|
export * from "./loader";
|
||||||
|
38
web/components/ui/loader/cycle-module-board-loader.tsx
Normal file
38
web/components/ui/loader/cycle-module-board-loader.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
export const CycleModuleBoardLayout = () => (
|
||||||
|
<div className="h-full w-full animate-pulse">
|
||||||
|
<div className="flex h-full w-full justify-between">
|
||||||
|
<div className="grid h-full w-full grid-cols-1 gap-6 overflow-y-auto p-8 lg:grid-cols-2 xl:grid-cols-3 3xl:grid-cols-4 auto-rows-max transition-all">
|
||||||
|
{[...Array(5)].map(() => (
|
||||||
|
<div className="flex h-44 w-full flex-col justify-between rounded border border-custom-border-100 bg-custom-background-100 p-4 text-sm">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="h-6 w-24 bg-custom-background-80 rounded" />
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="h-6 w-20 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-6 w-6 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="h-5 w-5 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-5 w-20 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
<span className="h-5 w-5 bg-custom-background-80 rounded-full" />
|
||||||
|
</div>
|
||||||
|
<span className="h-1.5 bg-custom-background-80 rounded" />
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<span className="h-4 w-16 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="h-4 w-4 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-4 w-4 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
28
web/components/ui/loader/cycle-module-list-loader.tsx
Normal file
28
web/components/ui/loader/cycle-module-list-loader.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
export const CycleModuleListLayout = () => (
|
||||||
|
<div className="h-full overflow-y-auto animate-pulse">
|
||||||
|
<div className="flex h-full w-full justify-between">
|
||||||
|
<div className="flex h-full w-full flex-col overflow-y-auto">
|
||||||
|
{[...Array(5)].map(() => (
|
||||||
|
<div className="flex w-full items-center justify-between gap-5 border-b border-custom-border-100 flex-col sm:flex-row px-5 py-6">
|
||||||
|
<div className="relative flex w-full items-center gap-3 justify-between overflow-hidden">
|
||||||
|
<div className="relative w-full flex items-center gap-3 overflow-hidden">
|
||||||
|
<div className="flex items-center gap-4 truncate">
|
||||||
|
<span className="h-10 w-10 bg-custom-background-80 rounded-full" />
|
||||||
|
<span className="h-5 w-20 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span className="h-6 w-20 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
<div className="flex w-full sm:w-auto relative overflow-hidden items-center gap-2.5 justify-between sm:justify-end sm:flex-shrink-0 ">
|
||||||
|
<div className="flex-shrink-0 relative flex items-center gap-3">
|
||||||
|
<span className="h-5 w-5 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-5 w-5 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-5 w-5 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
9
web/components/ui/loader/index.ts
Normal file
9
web/components/ui/loader/index.ts
Normal file
@ -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";
|
48
web/components/ui/loader/layouts/calendar-layout-loader.tsx
Normal file
48
web/components/ui/loader/layouts/calendar-layout-loader.tsx
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { getRandomInt } from "../utils";
|
||||||
|
|
||||||
|
const CalendarDay = () => {
|
||||||
|
const dataCount = getRandomInt(0, 1);
|
||||||
|
const dataBlocks = Array.from({ length: dataCount }, (_, index) => (
|
||||||
|
<span key={index} className="h-8 w-full bg-custom-background-80 rounded mb-2" />
|
||||||
|
));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex w-full flex-col min-h-[9rem]">
|
||||||
|
<div className="flex items-center justify-end p-2 w-full">
|
||||||
|
<span className="h-6 w-6 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2.5 p-2">{dataBlocks}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CalendarLayoutLoader = () => (
|
||||||
|
<div className="h-full w-full overflow-y-auto bg-custom-background-100 pt-4 animate-pulse">
|
||||||
|
<div className="mb-4 flex items-center justify-between gap-2 px-3">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="h-7 w-10 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-7 w-32 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="h-7 w-12 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-7 w-20 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span className="relative grid divide-x-[0.5px] divide-custom-border-200 text-sm font-medium grid-cols-5">
|
||||||
|
{[...Array(5)].map((_, index) => (
|
||||||
|
<span key={index} className="h-11 w-full bg-custom-background-80" />
|
||||||
|
))}
|
||||||
|
</span>
|
||||||
|
<div className="h-full w-full overflow-y-auto">
|
||||||
|
<div className="grid h-full w-full grid-cols-1 divide-y-[0.5px] divide-custom-border-200 overflow-y-auto">
|
||||||
|
{[...Array(6)].map((_, index) => (
|
||||||
|
<div key={index} className="grid divide-x-[0.5px] divide-custom-border-200 grid-cols-5">
|
||||||
|
{[...Array(5)].map((_, index) => (
|
||||||
|
<CalendarDay key={index} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
50
web/components/ui/loader/layouts/gantt-layout-loader.tsx
Normal file
50
web/components/ui/loader/layouts/gantt-layout-loader.tsx
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { getRandomLength } from "../utils";
|
||||||
|
|
||||||
|
export const GanttLayoutLoader = () => (
|
||||||
|
<div className="flex flex-col h-full overflow-x-auto animate-pulse">
|
||||||
|
<div className="min-h-10 w-full border-b border-custom-border-200 ">
|
||||||
|
<span className="h-6 w-12 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
<div className="flex h-full">
|
||||||
|
<div className="h-full w-[25.5rem] border-r border-custom-border-200">
|
||||||
|
<div className="flex items-end h-[3.75rem] py-2 px-4 border-b border-custom-border-200">
|
||||||
|
<div className="flex items-center pl-6 justify-between w-full">
|
||||||
|
<span className="h-5 w-14 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-5 w-16 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-3 h-11 p-4 w-full">
|
||||||
|
{[...Array(6)].map((_, index) => (
|
||||||
|
<div key={index} className="flex items-center gap-3 h-11 pl-6 w-full">
|
||||||
|
<span className="h-6 w-6 bg-custom-background-80 rounded" />
|
||||||
|
<span className={`h-6 w-${getRandomLength(["32", "52", "72"])} bg-custom-background-80 rounded`} />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="h-full w-full border-r border-custom-border-200">
|
||||||
|
<div className="flex flex-col justify-between gap-2 h-[3.75rem] py-1.5 px-4 border-b border-custom-border-200">
|
||||||
|
<div className="flex items-center justify-center">
|
||||||
|
<span className="h-5 w-20 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3 justify-between w-full">
|
||||||
|
{[...Array(15)].map((_, index) => (
|
||||||
|
<span key={index} className="h-5 w-10 bg-custom-background-80 rounded" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-3 h-11 p-4 w-full">
|
||||||
|
{[...Array(6)].map((_, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={`flex items-center gap-3 h-11 w-full`}
|
||||||
|
style={{ paddingLeft: getRandomLength(["115px", "208px", "260px"]) }}
|
||||||
|
>
|
||||||
|
<span className={`h-6 w-40 w-${getRandomLength(["32", "52", "72"])} bg-custom-background-80 rounded`} />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
5
web/components/ui/loader/layouts/index.ts
Normal file
5
web/components/ui/loader/layouts/index.ts
Normal file
@ -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";
|
18
web/components/ui/loader/layouts/kanban-layout-loader.tsx
Normal file
18
web/components/ui/loader/layouts/kanban-layout-loader.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
export const KanbanLayoutLoader = ({ cardsInEachColumn = [2, 3, 2, 4, 3] }: { cardsInEachColumn?: number[] }) => (
|
||||||
|
<div className="flex gap-5 px-3.5 py-1.5 overflow-x-auto">
|
||||||
|
{cardsInEachColumn.map((cardsInColumn, columnIndex) => (
|
||||||
|
<div key={columnIndex} className="flex flex-col gap-3 animate-pulse">
|
||||||
|
<div className="flex items-center justify-between h-9 w-80">
|
||||||
|
<div className="flex item-center gap-1.5">
|
||||||
|
<span className="h-6 w-6 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-6 w-24 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
<span className="h-6 w-6 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
{Array.from({ length: cardsInColumn }, (_, cardIndex) => (
|
||||||
|
<span key={cardIndex} className="h-28 w-80 bg-custom-background-80 rounded" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
45
web/components/ui/loader/layouts/list-layout-loader.tsx
Normal file
45
web/components/ui/loader/layouts/list-layout-loader.tsx
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { getRandomInt, getRandomLength } from "../utils";
|
||||||
|
|
||||||
|
const ListItemRow = () => (
|
||||||
|
<div className="flex items-center justify-between h-11 p-3 border-b border-custom-border-200">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<span className="h-5 w-10 bg-custom-background-80 rounded" />
|
||||||
|
<span className={`h-5 w-${getRandomLength(["32", "52", "72"])} bg-custom-background-80 rounded`} />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{[...Array(6)].map((_, index) => (
|
||||||
|
<>
|
||||||
|
{getRandomInt(1, 2) % 2 === 0 ? (
|
||||||
|
<span key={index} className="h-5 w-5 bg-custom-background-80 rounded" />
|
||||||
|
) : (
|
||||||
|
<span className="h-5 w-16 bg-custom-background-80 rounded" />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const ListSection = ({ itemCount }: { itemCount: number }) => (
|
||||||
|
<div className="flex flex-shrink-0 flex-col">
|
||||||
|
<div className="sticky top-0 z-[2] w-full flex-shrink-0 border-b border-custom-border-200 bg-custom-background-90 px-3 py-1">
|
||||||
|
<div className="flex items-center gap-2 py-1.5 w-full">
|
||||||
|
<span className="h-6 w-6 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-6 w-24 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="relative h-full w-full">
|
||||||
|
{[...Array(itemCount)].map((_, index) => (
|
||||||
|
<ListItemRow key={index} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const ListLayoutLoader = () => (
|
||||||
|
<div className="flex flex-shrink-0 flex-col animate-pulse">
|
||||||
|
{[6, 5, 2].map((itemCount, index) => (
|
||||||
|
<ListSection key={index} itemCount={itemCount} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
@ -0,0 +1,38 @@
|
|||||||
|
import { getRandomLength } from "../utils";
|
||||||
|
|
||||||
|
export const SpreadsheetLayoutLoader = () => (
|
||||||
|
<div className="horizontal-scroll-enable h-full w-full animate-pulse">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th className="h-11 min-w-[28rem] bg-custom-background-90 border-r border-custom-border-100" />
|
||||||
|
{[...Array(10)].map((_, index) => (
|
||||||
|
<th
|
||||||
|
key={index}
|
||||||
|
className="h-11 w-full min-w-[8rem] bg-custom-background-90 border-r border-custom-border-100"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{[...Array(16)].map((_, rowIndex) => (
|
||||||
|
<tr key={rowIndex} className="border-b border-custom-border-100">
|
||||||
|
<td className="h-11 min-w-[28rem] border-r border-custom-border-100">
|
||||||
|
<div className="flex items-center gap-3 px-3">
|
||||||
|
<span className="h-5 w-10 bg-custom-background-80 rounded" />
|
||||||
|
<span className={`h-5 w-${getRandomLength(["32", "52", "72"])} bg-custom-background-80 rounded`} />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
{[...Array(10)].map((_, colIndex) => (
|
||||||
|
<td key={colIndex} className="h-11 w-full min-w-[8rem] border-r border-custom-border-100">
|
||||||
|
<div className="flex items-center justify-center gap-3 px-3">
|
||||||
|
<span className="h-5 w-20 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
16
web/components/ui/loader/notification-loader.tsx
Normal file
16
web/components/ui/loader/notification-loader.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export const NotificationsLoader = () => (
|
||||||
|
<div className="divide-y divide-custom-border-100 animate-pulse overflow-hidden">
|
||||||
|
{[...Array(3)].map(() => (
|
||||||
|
<div className="flex w-full items-center gap-4 p-3">
|
||||||
|
<span className="min-h-12 min-w-12 bg-custom-background-80 rounded-full" />
|
||||||
|
<div className="flex flex-col gap-2.5 w-full">
|
||||||
|
<span className="h-5 w-36 bg-custom-background-80 rounded" />
|
||||||
|
<div className="flex items-center justify-between gap-2 w-full">
|
||||||
|
<span className="h-5 w-28 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-5 w-16 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
28
web/components/ui/loader/pages-loader.tsx
Normal file
28
web/components/ui/loader/pages-loader.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
export const PagesLoader = () => (
|
||||||
|
<div className="flex h-full flex-col space-y-5 overflow-hidden p-6">
|
||||||
|
<div className="flex justify-between gap-4">
|
||||||
|
<h3 className="text-2xl font-semibold text-custom-text-100">Pages</h3>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
{[...Array(5)].map(() => (
|
||||||
|
<span className="h-8 w-20 bg-custom-background-80 rounded-full" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="divide-y divide-custom-border-200">
|
||||||
|
{[...Array(5)].map(() => (
|
||||||
|
<div className="h-12 w-full flex items-center justify-between px-3">
|
||||||
|
<div className="flex items-center gap-1.5">
|
||||||
|
<span className="h-5 w-5 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-5 w-20 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1.5">
|
||||||
|
<span className="h-5 w-16 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-5 w-5 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-5 w-5 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-5 w-5 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
34
web/components/ui/loader/projects-loader.tsx
Normal file
34
web/components/ui/loader/projects-loader.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
export const ProjectsLoader = () => (
|
||||||
|
<div className="h-full w-full overflow-y-auto p-8 animate-pulse">
|
||||||
|
<div className="grid grid-cols-1 gap-9 md:grid-cols-2 lg:grid-cols-3">
|
||||||
|
{[...Array(3)].map(() => (
|
||||||
|
<div className="flex cursor-pointer flex-col rounded border border-custom-border-200 bg-custom-background-100">
|
||||||
|
<div className="relative min-h-[118px] w-full rounded-t border-b border-custom-border-200 ">
|
||||||
|
<div className="absolute inset-0 z-[1] bg-gradient-to-t from-black/20 to-transparent">
|
||||||
|
<div className="absolute bottom-4 z-10 flex h-10 w-full items-center justify-between gap-3 px-4">
|
||||||
|
<div className="flex flex-grow items-center gap-2.5 truncate">
|
||||||
|
<span className="min-h-9 min-w-9 bg-custom-background-80 rounded" />
|
||||||
|
<div className="flex w-full flex-col justify-between gap-0.5 truncate">
|
||||||
|
<span className="h-4 w-28 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-4 w-16 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex h-full flex-shrink-0 items-center gap-2">
|
||||||
|
<span className="h-6 w-6 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-6 w-6 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex h-[104px] w-full flex-col justify-between rounded-b p-4">
|
||||||
|
<span className="h-4 w-36 bg-custom-background-80 rounded" />
|
||||||
|
<div className="item-center flex justify-between">
|
||||||
|
<span className="h-5 w-20 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-5 w-5 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
12
web/components/ui/loader/settings/activity.tsx
Normal file
12
web/components/ui/loader/settings/activity.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { getRandomLength } from "../utils";
|
||||||
|
|
||||||
|
export const ActivitySettingsLoader = () => (
|
||||||
|
<div className="flex flex-col gap-3 animate-pulse">
|
||||||
|
{[...Array(10)].map(() => (
|
||||||
|
<div className="relative flex items-center gap-2 h-12 border-b border-custom-border-200">
|
||||||
|
<span className="h-6 w-6 bg-custom-background-80 rounded" />
|
||||||
|
<span className={`h-6 w-${getRandomLength(["52", "72", "96"])} bg-custom-background-80 rounded`} />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
19
web/components/ui/loader/settings/api-token.tsx
Normal file
19
web/components/ui/loader/settings/api-token.tsx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
export const APITokenSettingsLoader = () => (
|
||||||
|
<section className="w-full overflow-y-auto py-8 pr-9">
|
||||||
|
<div className="mb-2 flex items-center justify-between border-b border-custom-border-200 py-3.5">
|
||||||
|
<h3 className="text-xl font-medium">API tokens</h3>
|
||||||
|
<span className="h-8 w-28 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
<div className="divide-y-[0.5px] divide-custom-border-200">
|
||||||
|
{[...Array(2)].map(() => (
|
||||||
|
<div className="flex flex-col gap-2 px-4 py-3">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="h-5 w-28 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-5 w-16 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
<span className="h-5 w-36 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
27
web/components/ui/loader/settings/email.tsx
Normal file
27
web/components/ui/loader/settings/email.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
export const EmailSettingsLoader = () => (
|
||||||
|
<div className="mx-auto mt-8 h-full w-full overflow-y-auto px-6 lg:px-20 pb- animate-pulse">
|
||||||
|
<div className="flex flex-col gap-2 pt-6 mb-2 pb-6 border-b border-custom-border-100">
|
||||||
|
<span className="h-7 w-40 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-5 w-96 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="flex items-center py-3">
|
||||||
|
<span className="h-7 w-32 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
{[...Array(4)].map(() => (
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex flex-col gap-2 py-3">
|
||||||
|
<span className="h-6 w-28 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-5 w-96 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<span className="h-5 w-5 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="flex items-center py-12">
|
||||||
|
<span className="h-8 w-32 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
18
web/components/ui/loader/settings/import-and-export.tsx
Normal file
18
web/components/ui/loader/settings/import-and-export.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
export const ImportExportSettingsLoader = () => (
|
||||||
|
<div className="divide-y-[0.5px] divide-custom-border-200 animate-pulse">
|
||||||
|
{[...Array(2)].map(() => (
|
||||||
|
<div className="flex items-center justify-between gap-2 px-4 py-3">
|
||||||
|
<div className="flex flex-col gap-1.5">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="h-5 w-16 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-5 w-16 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="h-4 w-28 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-4 w-28 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
7
web/components/ui/loader/settings/index.ts
Normal file
7
web/components/ui/loader/settings/index.ts
Normal file
@ -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";
|
16
web/components/ui/loader/settings/integration.tsx
Normal file
16
web/components/ui/loader/settings/integration.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export const IntegrationsSettingsLoader = () => (
|
||||||
|
<div className="divide-y-[0.5px] divide-custom-border-100 animate-pulse">
|
||||||
|
{[...Array(2)].map(() => (
|
||||||
|
<div className="flex items-center justify-between gap-2 border-b border-custom-border-100 bg-custom-background-100 px-4 py-6">
|
||||||
|
<div className="flex items-start gap-4">
|
||||||
|
<span className="h-10 w-10 bg-custom-background-80 rounded-full" />
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<span className="h-5 w-20 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-4 w-60 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span className="h-8 w-16 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
16
web/components/ui/loader/settings/members.tsx
Normal file
16
web/components/ui/loader/settings/members.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export const MembersSettingsLoader = () => (
|
||||||
|
<div className="divide-y-[0.5px] divide-custom-border-100 animate-pulse">
|
||||||
|
{[...Array(4)].map(() => (
|
||||||
|
<div className="group flex items-center justify-between px-3 py-4">
|
||||||
|
<div className="flex items-center gap-x-4 gap-y-2">
|
||||||
|
<span className="h-10 w-10 bg-custom-background-80 rounded-full" />
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<span className="h-5 w-20 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-4 w-36 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span className="h-6 w-16 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
20
web/components/ui/loader/settings/web-hook.tsx
Normal file
20
web/components/ui/loader/settings/web-hook.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export const WebhookSettingsLoader = () => (
|
||||||
|
<div className="h-full w-full overflow-hidden py-8 pr-9">
|
||||||
|
<div className="flex h-full w-full flex-col">
|
||||||
|
<div className="flex items-center justify-between gap-4 border-b border-custom-border-200 pb-3.5">
|
||||||
|
<div className="text-xl font-medium">Webhooks</div>
|
||||||
|
<span className="h-8 w-28 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
<div className="h-full w-full overflow-y-auto">
|
||||||
|
<div className="border-b border-custom-border-200">
|
||||||
|
<div>
|
||||||
|
<span className="flex items-center justify-between gap-4 px-3.5 py-[18px]">
|
||||||
|
<span className="h-5 w-36 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-6 w-12 bg-custom-background-80 rounded" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
35
web/components/ui/loader/utils.tsx
Normal file
35
web/components/ui/loader/utils.tsx
Normal file
@ -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> = (props) => {
|
||||||
|
const { layout } = props;
|
||||||
|
switch (layout) {
|
||||||
|
case "list":
|
||||||
|
return <ListLayoutLoader />;
|
||||||
|
case "kanban":
|
||||||
|
return <KanbanLayoutLoader />;
|
||||||
|
case "spreadsheet":
|
||||||
|
return <SpreadsheetLayoutLoader />;
|
||||||
|
case "calendar":
|
||||||
|
return <CalendarLayoutLoader />;
|
||||||
|
case "gantt_chart":
|
||||||
|
return <GanttLayoutLoader />;
|
||||||
|
default:
|
||||||
|
return <KanbanLayoutLoader />;
|
||||||
|
}
|
||||||
|
};
|
18
web/components/ui/loader/view-list-loader.tsx
Normal file
18
web/components/ui/loader/view-list-loader.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
export const ViewListLoader = () => (
|
||||||
|
<div className="flex h-full w-full flex-col animate-pulse">
|
||||||
|
{[...Array(8)].map(() => (
|
||||||
|
<div className="group border-b border-custom-border-200">
|
||||||
|
<div className="relative flex w-full items-center justify-between rounded p-4">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<span className="min-h-10 min-w-10 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-6 w-28 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="h-5 w-5 bg-custom-background-80 rounded" />
|
||||||
|
<span className="h-5 w-5 bg-custom-background-80 rounded" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
@ -8,7 +8,8 @@ import { useApplication, useProjectView, useUser } from "hooks/store";
|
|||||||
import { ProjectViewListItem } from "components/views";
|
import { ProjectViewListItem } from "components/views";
|
||||||
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
||||||
// ui
|
// ui
|
||||||
import { Input, Loader, Spinner } from "@plane/ui";
|
import { Input } from "@plane/ui";
|
||||||
|
import { ViewListLoader } from "components/ui";
|
||||||
// constants
|
// constants
|
||||||
import { EUserProjectRoles } from "constants/project";
|
import { EUserProjectRoles } from "constants/project";
|
||||||
import { VIEW_EMPTY_STATE_DETAILS } from "constants/empty-state";
|
import { VIEW_EMPTY_STATE_DETAILS } from "constants/empty-state";
|
||||||
@ -28,22 +29,7 @@ export const ProjectViewsList = observer(() => {
|
|||||||
} = useUser();
|
} = useUser();
|
||||||
const { projectViewIds, getViewById, loader } = useProjectView();
|
const { projectViewIds, getViewById, loader } = useProjectView();
|
||||||
|
|
||||||
if (loader)
|
if (loader || !projectViewIds) return <ViewListLoader />;
|
||||||
return (
|
|
||||||
<div className="flex items-center justify-center h-full w-full">
|
|
||||||
<Spinner />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!projectViewIds)
|
|
||||||
return (
|
|
||||||
<Loader className="space-y-4 p-4">
|
|
||||||
<Loader.Item height="72px" />
|
|
||||||
<Loader.Item height="72px" />
|
|
||||||
<Loader.Item height="72px" />
|
|
||||||
<Loader.Item height="72px" />
|
|
||||||
</Loader>
|
|
||||||
);
|
|
||||||
|
|
||||||
const viewsList = projectViewIds.map((viewId) => getViewById(viewId));
|
const viewsList = projectViewIds.map((viewId) => getViewById(viewId));
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import { useMember } from "hooks/store";
|
|||||||
// components
|
// components
|
||||||
import { WorkspaceInvitationsListItem, WorkspaceMembersListItem } from "components/workspace";
|
import { WorkspaceInvitationsListItem, WorkspaceMembersListItem } from "components/workspace";
|
||||||
// ui
|
// ui
|
||||||
import { Loader } from "@plane/ui";
|
import { MembersSettingsLoader } from "components/ui";
|
||||||
|
|
||||||
export const WorkspaceMembersList: FC<{ searchQuery: string }> = observer((props) => {
|
export const WorkspaceMembersList: FC<{ searchQuery: string }> = observer((props) => {
|
||||||
const { searchQuery } = props;
|
const { searchQuery } = props;
|
||||||
@ -30,15 +30,7 @@ export const WorkspaceMembersList: FC<{ searchQuery: string }> = observer((props
|
|||||||
workspaceSlug ? () => fetchWorkspaceMemberInvitations(workspaceSlug.toString()) : null
|
workspaceSlug ? () => fetchWorkspaceMemberInvitations(workspaceSlug.toString()) : null
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!workspaceMemberIds && !workspaceMemberInvitationIds)
|
if (!workspaceMemberIds && !workspaceMemberInvitationIds) return <MembersSettingsLoader />;
|
||||||
return (
|
|
||||||
<Loader className="space-y-5">
|
|
||||||
<Loader.Item height="40px" />
|
|
||||||
<Loader.Item height="40px" />
|
|
||||||
<Loader.Item height="40px" />
|
|
||||||
<Loader.Item height="40px" />
|
|
||||||
</Loader>
|
|
||||||
);
|
|
||||||
|
|
||||||
// derived values
|
// derived values
|
||||||
const searchedMemberIds = getSearchedWorkspaceMemberIds(searchQuery);
|
const searchedMemberIds = getSearchedWorkspaceMemberIds(searchQuery);
|
||||||
|
@ -6,7 +6,7 @@ import { useGlobalView } from "hooks/store";
|
|||||||
// components
|
// components
|
||||||
import { GlobalViewListItem } from "components/workspace";
|
import { GlobalViewListItem } from "components/workspace";
|
||||||
// ui
|
// ui
|
||||||
import { Loader } from "@plane/ui";
|
import { ViewListLoader } from "components/ui";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
@ -25,15 +25,7 @@ export const GlobalViewsList: React.FC<Props> = observer((props) => {
|
|||||||
workspaceSlug ? () => fetchAllGlobalViews(workspaceSlug.toString()) : null
|
workspaceSlug ? () => fetchAllGlobalViews(workspaceSlug.toString()) : null
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!currentWorkspaceViews)
|
if (!currentWorkspaceViews) return <ViewListLoader />;
|
||||||
return (
|
|
||||||
<Loader className="space-y-4 p-4">
|
|
||||||
<Loader.Item height="72px" />
|
|
||||||
<Loader.Item height="72px" />
|
|
||||||
<Loader.Item height="72px" />
|
|
||||||
<Loader.Item height="72px" />
|
|
||||||
</Loader>
|
|
||||||
);
|
|
||||||
|
|
||||||
const filteredViewsList = getSearchedViews(searchQuery);
|
const filteredViewsList = getSearchedViews(searchQuery);
|
||||||
|
|
||||||
|
@ -13,7 +13,8 @@ import { CyclesHeader } from "components/headers";
|
|||||||
import { CyclesView, ActiveCycleDetails, CycleCreateUpdateModal } from "components/cycles";
|
import { CyclesView, ActiveCycleDetails, CycleCreateUpdateModal } from "components/cycles";
|
||||||
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
||||||
// ui
|
// ui
|
||||||
import { Spinner, Tooltip } from "@plane/ui";
|
import { Tooltip } from "@plane/ui";
|
||||||
|
import { CycleModuleBoardLayout, CycleModuleListLayout, GanttLayoutLoader } from "components/ui";
|
||||||
// types
|
// types
|
||||||
import { TCycleView, TCycleLayout } from "@plane/types";
|
import { TCycleView, TCycleLayout } from "@plane/types";
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
@ -66,9 +67,11 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => {
|
|||||||
|
|
||||||
if (loader)
|
if (loader)
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center h-full w-full">
|
<>
|
||||||
<Spinner />
|
{cycleLayout === "list" && <CycleModuleListLayout />}
|
||||||
</div>
|
{cycleLayout === "board" && <CycleModuleBoardLayout />}
|
||||||
|
{cycleLayout === "gantt" && <GanttLayoutLoader />}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -16,7 +16,7 @@ import { AppLayout } from "layouts/app-layout";
|
|||||||
import { RecentPagesList, CreateUpdatePageModal } from "components/pages";
|
import { RecentPagesList, CreateUpdatePageModal } from "components/pages";
|
||||||
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
||||||
import { PagesHeader } from "components/headers";
|
import { PagesHeader } from "components/headers";
|
||||||
import { Spinner } from "@plane/ui";
|
import { PagesLoader } from "components/ui";
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
// constants
|
// constants
|
||||||
@ -125,12 +125,7 @@ const ProjectPagesPage: NextPageWithLayout = observer(() => {
|
|||||||
</Tab.List>
|
</Tab.List>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (loader || archivedPageLoader)
|
if (loader || archivedPageLoader) return <PagesLoader />;
|
||||||
return (
|
|
||||||
<div className="flex items-center justify-center h-full w-full">
|
|
||||||
<Spinner />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -15,7 +15,7 @@ import { IntegrationCard } from "components/project";
|
|||||||
import { ProjectSettingHeader } from "components/headers";
|
import { ProjectSettingHeader } from "components/headers";
|
||||||
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
||||||
// ui
|
// ui
|
||||||
import { Loader } from "@plane/ui";
|
import { IntegrationsSettingsLoader } from "components/ui";
|
||||||
// types
|
// types
|
||||||
import { IProject } from "@plane/types";
|
import { IProject } from "@plane/types";
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
@ -79,12 +79,7 @@ const ProjectIntegrationsPage: NextPageWithLayout = () => {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<Loader className="space-y-5">
|
<IntegrationsSettingsLoader />
|
||||||
<Loader.Item height="40px" />
|
|
||||||
<Loader.Item height="40px" />
|
|
||||||
<Loader.Item height="40px" />
|
|
||||||
<Loader.Item height="40px" />
|
|
||||||
</Loader>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -13,7 +13,8 @@ import { WorkspaceSettingHeader } from "components/headers";
|
|||||||
import { ApiTokenListItem, CreateApiTokenModal } from "components/api-token";
|
import { ApiTokenListItem, CreateApiTokenModal } from "components/api-token";
|
||||||
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
||||||
// ui
|
// ui
|
||||||
import { Button, Spinner } from "@plane/ui";
|
import { Button } from "@plane/ui";
|
||||||
|
import { APITokenSettingsLoader } from "components/ui";
|
||||||
// services
|
// services
|
||||||
import { APITokenService } from "services/api_token.service";
|
import { APITokenService } from "services/api_token.service";
|
||||||
// types
|
// types
|
||||||
@ -56,10 +57,13 @@ const ApiTokensPage: NextPageWithLayout = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!tokens) {
|
||||||
|
return <APITokenSettingsLoader />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CreateApiTokenModal isOpen={isCreateTokenModalOpen} onClose={() => setIsCreateTokenModalOpen(false)} />
|
<CreateApiTokenModal isOpen={isCreateTokenModalOpen} onClose={() => setIsCreateTokenModalOpen(false)} />
|
||||||
{tokens ? (
|
|
||||||
<section className="h-full w-full overflow-y-auto py-8 pr-9">
|
<section className="h-full w-full overflow-y-auto py-8 pr-9">
|
||||||
{tokens.length > 0 ? (
|
{tokens.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
@ -94,11 +98,6 @@ const ApiTokensPage: NextPageWithLayout = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
) : (
|
|
||||||
<div className="grid h-full w-full place-items-center p-4">
|
|
||||||
<Spinner />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -13,8 +13,7 @@ import { WorkspaceSettingLayout } from "layouts/settings-layout";
|
|||||||
import { SingleIntegrationCard } from "components/integration";
|
import { SingleIntegrationCard } from "components/integration";
|
||||||
import { WorkspaceSettingHeader } from "components/headers";
|
import { WorkspaceSettingHeader } from "components/headers";
|
||||||
// ui
|
// ui
|
||||||
import { IntegrationAndImportExportBanner } from "components/ui";
|
import { IntegrationAndImportExportBanner, IntegrationsSettingsLoader } from "components/ui";
|
||||||
import { Loader } from "@plane/ui";
|
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
@ -53,10 +52,7 @@ const WorkspaceIntegrationsPage: NextPageWithLayout = observer(() => {
|
|||||||
{appIntegrations ? (
|
{appIntegrations ? (
|
||||||
appIntegrations.map((integration) => <SingleIntegrationCard key={integration.id} integration={integration} />)
|
appIntegrations.map((integration) => <SingleIntegrationCard key={integration.id} integration={integration} />)
|
||||||
) : (
|
) : (
|
||||||
<Loader className="mt-4 space-y-2.5">
|
<IntegrationsSettingsLoader />
|
||||||
<Loader.Item height="89px" />
|
|
||||||
<Loader.Item height="89px" />
|
|
||||||
</Loader>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
@ -13,7 +13,8 @@ import { WorkspaceSettingHeader } from "components/headers";
|
|||||||
import { WebhooksList, CreateWebhookModal } from "components/web-hooks";
|
import { WebhooksList, CreateWebhookModal } from "components/web-hooks";
|
||||||
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
||||||
// ui
|
// ui
|
||||||
import { Button, Spinner } from "@plane/ui";
|
import { Button } from "@plane/ui";
|
||||||
|
import { WebhookSettingsLoader } from "components/ui";
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
// constants
|
// constants
|
||||||
@ -59,12 +60,7 @@ const WebhooksListPage: NextPageWithLayout = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!webhooks)
|
if (!webhooks) return <WebhookSettingsLoader />;
|
||||||
return (
|
|
||||||
<div className="grid h-full w-full place-items-center p-4">
|
|
||||||
<Spinner />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full w-full overflow-hidden py-8 pr-9">
|
<div className="h-full w-full overflow-hidden py-8 pr-9">
|
||||||
|
@ -14,7 +14,7 @@ import { RichReadOnlyEditor } from "@plane/rich-text-editor";
|
|||||||
// icons
|
// icons
|
||||||
import { History, MessageSquare } from "lucide-react";
|
import { History, MessageSquare } from "lucide-react";
|
||||||
// ui
|
// ui
|
||||||
import { Loader } from "@plane/ui";
|
import { ActivitySettingsLoader } from "components/ui";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { USER_ACTIVITY } from "constants/fetch-keys";
|
import { USER_ACTIVITY } from "constants/fetch-keys";
|
||||||
// helper
|
// helper
|
||||||
@ -31,7 +31,6 @@ const ProfileActivityPage: NextPageWithLayout = observer(() => {
|
|||||||
const { currentUser } = useUser();
|
const { currentUser } = useUser();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<section className="mx-auto mt-5 md:mt-16 flex h-full w-full flex-col overflow-hidden px-8 pb-8 lg:w-3/5">
|
<section className="mx-auto mt-5 md:mt-16 flex h-full w-full flex-col overflow-hidden px-8 pb-8 lg:w-3/5">
|
||||||
<div className="flex items-center border-b border-custom-border-100 gap-4 pb-3.5">
|
<div className="flex items-center border-b border-custom-border-100 gap-4 pb-3.5">
|
||||||
<SidebarHamburgerToggle />
|
<SidebarHamburgerToggle />
|
||||||
@ -182,15 +181,9 @@ const ProfileActivityPage: NextPageWithLayout = observer(() => {
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Loader className="space-y-5">
|
<ActivitySettingsLoader />
|
||||||
<Loader.Item height="40px" />
|
|
||||||
<Loader.Item height="40px" />
|
|
||||||
<Loader.Item height="40px" />
|
|
||||||
<Loader.Item height="40px" />
|
|
||||||
</Loader>
|
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import useSWR from "swr";
|
|||||||
// layouts
|
// layouts
|
||||||
import { ProfilePreferenceSettingsLayout } from "layouts/settings-layout/profile/preferences";
|
import { ProfilePreferenceSettingsLayout } from "layouts/settings-layout/profile/preferences";
|
||||||
// ui
|
// ui
|
||||||
import { Loader } from "@plane/ui";
|
import { EmailSettingsLoader } from "components/ui";
|
||||||
// components
|
// components
|
||||||
import { EmailNotificationForm } from "components/profile/preferences";
|
import { EmailNotificationForm } from "components/profile/preferences";
|
||||||
// services
|
// services
|
||||||
@ -20,18 +20,8 @@ const ProfilePreferencesThemePage: NextPageWithLayout = () => {
|
|||||||
userService.currentUserEmailNotificationSettings()
|
userService.currentUserEmailNotificationSettings()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isLoading) {
|
if (!data || isLoading) {
|
||||||
return (
|
return <EmailSettingsLoader />;
|
||||||
<Loader className="space-y-4 mt-8 px-6 lg:px-20">
|
|
||||||
<Loader.Item height="40px" />
|
|
||||||
<Loader.Item height="40px" />
|
|
||||||
<Loader.Item height="40px" />
|
|
||||||
</Loader>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -67,10 +67,11 @@ export class ArchivedIssues extends IssueHelperStore implements IArchivedIssues
|
|||||||
const orderBy = displayFilters?.order_by;
|
const orderBy = displayFilters?.order_by;
|
||||||
const layout = displayFilters?.layout;
|
const layout = displayFilters?.layout;
|
||||||
|
|
||||||
const archivedIssueIds = this.issues[projectId] ?? [];
|
const archivedIssueIds = this.issues[projectId];
|
||||||
|
if (!archivedIssueIds) return undefined;
|
||||||
|
|
||||||
const _issues = this.rootIssueStore.issues.getIssuesByIds(archivedIssueIds);
|
const _issues = this.rootIssueStore.issues.getIssuesByIds(archivedIssueIds);
|
||||||
if (!_issues) return undefined;
|
if (!_issues) return [];
|
||||||
|
|
||||||
let issues: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined = undefined;
|
let issues: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined = undefined;
|
||||||
|
|
||||||
|
@ -78,10 +78,11 @@ export class DraftIssues extends IssueHelperStore implements IDraftIssues {
|
|||||||
const orderBy = displayFilters?.order_by;
|
const orderBy = displayFilters?.order_by;
|
||||||
const layout = displayFilters?.layout;
|
const layout = displayFilters?.layout;
|
||||||
|
|
||||||
const draftIssueIds = this.issues[projectId] ?? [];
|
const draftIssueIds = this.issues[projectId];
|
||||||
|
if (!draftIssueIds) return undefined;
|
||||||
|
|
||||||
const _issues = this.rootIssueStore.issues.getIssuesByIds(draftIssueIds);
|
const _issues = this.rootIssueStore.issues.getIssuesByIds(draftIssueIds);
|
||||||
if (!_issues) return undefined;
|
if (!_issues) return [];
|
||||||
|
|
||||||
let issues: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined = undefined;
|
let issues: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined = undefined;
|
||||||
|
|
||||||
|
@ -9,9 +9,7 @@ import { IIssueRootStore } from "../root.store";
|
|||||||
import { TIssue, TLoader, TGroupedIssues, TSubGroupedIssues, TUnGroupedIssues, ViewFlags } from "@plane/types";
|
import { TIssue, TLoader, TGroupedIssues, TSubGroupedIssues, TUnGroupedIssues, ViewFlags } from "@plane/types";
|
||||||
|
|
||||||
interface IProfileIssueTabTypes {
|
interface IProfileIssueTabTypes {
|
||||||
assigned: string[];
|
[key: string]: string[];
|
||||||
created: string[];
|
|
||||||
subscribed: string[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IProfileIssues {
|
export interface IProfileIssues {
|
||||||
@ -23,6 +21,7 @@ export interface IProfileIssues {
|
|||||||
groupedIssueIds: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined;
|
groupedIssueIds: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined;
|
||||||
viewFlags: ViewFlags;
|
viewFlags: ViewFlags;
|
||||||
// actions
|
// actions
|
||||||
|
setViewId: (viewId: "assigned" | "created" | "subscribed") => void;
|
||||||
fetchIssues: (
|
fetchIssues: (
|
||||||
workspaceSlug: string,
|
workspaceSlug: string,
|
||||||
projectId: string | undefined,
|
projectId: string | undefined,
|
||||||
@ -73,6 +72,7 @@ export class ProfileIssues extends IssueHelperStore implements IProfileIssues {
|
|||||||
groupedIssueIds: computed,
|
groupedIssueIds: computed,
|
||||||
viewFlags: computed,
|
viewFlags: computed,
|
||||||
// action
|
// action
|
||||||
|
setViewId: action.bound,
|
||||||
fetchIssues: action,
|
fetchIssues: action,
|
||||||
createIssue: action,
|
createIssue: action,
|
||||||
updateIssue: action,
|
updateIssue: action,
|
||||||
@ -86,8 +86,11 @@ export class ProfileIssues extends IssueHelperStore implements IProfileIssues {
|
|||||||
|
|
||||||
get groupedIssueIds() {
|
get groupedIssueIds() {
|
||||||
const userId = this.rootIssueStore.userId;
|
const userId = this.rootIssueStore.userId;
|
||||||
|
const workspaceSlug = this.rootIssueStore.workspaceSlug;
|
||||||
const currentView = this.currentView;
|
const currentView = this.currentView;
|
||||||
if (!userId || !currentView) return undefined;
|
if (!userId || !currentView || !workspaceSlug) return undefined;
|
||||||
|
|
||||||
|
const uniqueViewId = `${workspaceSlug}_${currentView}`;
|
||||||
|
|
||||||
const displayFilters = this.rootIssueStore?.profileIssuesFilter?.issueFilters?.displayFilters;
|
const displayFilters = this.rootIssueStore?.profileIssuesFilter?.issueFilters?.displayFilters;
|
||||||
if (!displayFilters) return undefined;
|
if (!displayFilters) return undefined;
|
||||||
@ -97,12 +100,12 @@ export class ProfileIssues extends IssueHelperStore implements IProfileIssues {
|
|||||||
const orderBy = displayFilters?.order_by;
|
const orderBy = displayFilters?.order_by;
|
||||||
const layout = displayFilters?.layout;
|
const layout = displayFilters?.layout;
|
||||||
|
|
||||||
const userIssueIds = this.issues[userId]?.[currentView];
|
const userIssueIds = this.issues[userId]?.[uniqueViewId];
|
||||||
|
|
||||||
if (!userIssueIds) return;
|
if (!userIssueIds) return;
|
||||||
|
|
||||||
const _issues = this.rootStore.issues.getIssuesByIds(userIssueIds);
|
const _issues = this.rootStore.issues.getIssuesByIds(userIssueIds);
|
||||||
if (!_issues) return undefined;
|
if (!_issues) return [];
|
||||||
|
|
||||||
let issues: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined = undefined;
|
let issues: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined = undefined;
|
||||||
|
|
||||||
@ -131,6 +134,10 @@ export class ProfileIssues extends IssueHelperStore implements IProfileIssues {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setViewId(viewId: "assigned" | "created" | "subscribed") {
|
||||||
|
this.currentView = viewId;
|
||||||
|
}
|
||||||
|
|
||||||
fetchIssues = async (
|
fetchIssues = async (
|
||||||
workspaceSlug: string,
|
workspaceSlug: string,
|
||||||
projectId: string | undefined,
|
projectId: string | undefined,
|
||||||
@ -145,6 +152,8 @@ export class ProfileIssues extends IssueHelperStore implements IProfileIssues {
|
|||||||
this.loader = loadType;
|
this.loader = loadType;
|
||||||
this.currentView = view;
|
this.currentView = view;
|
||||||
|
|
||||||
|
const uniqueViewId = `${workspaceSlug}_${view}`;
|
||||||
|
|
||||||
let params: any = this.rootIssueStore?.profileIssuesFilter?.appliedFilters;
|
let params: any = this.rootIssueStore?.profileIssuesFilter?.appliedFilters;
|
||||||
params = {
|
params = {
|
||||||
...params,
|
...params,
|
||||||
@ -161,7 +170,7 @@ export class ProfileIssues extends IssueHelperStore implements IProfileIssues {
|
|||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
set(
|
set(
|
||||||
this.issues,
|
this.issues,
|
||||||
[userId, view],
|
[userId, uniqueViewId],
|
||||||
response.map((issue) => issue.id)
|
response.map((issue) => issue.id)
|
||||||
);
|
);
|
||||||
this.loader = undefined;
|
this.loader = undefined;
|
||||||
@ -187,8 +196,10 @@ export class ProfileIssues extends IssueHelperStore implements IProfileIssues {
|
|||||||
|
|
||||||
const response = await this.rootIssueStore.projectIssues.createIssue(workspaceSlug, projectId, data);
|
const response = await this.rootIssueStore.projectIssues.createIssue(workspaceSlug, projectId, data);
|
||||||
|
|
||||||
|
const uniqueViewId = `${workspaceSlug}_${this.currentView}`;
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.issues[userId][this.currentView].push(response.id);
|
this.issues[userId][uniqueViewId].push(response.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.rootStore.issues.addIssue([response]);
|
this.rootStore.issues.addIssue([response]);
|
||||||
@ -234,10 +245,12 @@ export class ProfileIssues extends IssueHelperStore implements IProfileIssues {
|
|||||||
try {
|
try {
|
||||||
const response = await this.rootIssueStore.projectIssues.removeIssue(workspaceSlug, projectId, issueId);
|
const response = await this.rootIssueStore.projectIssues.removeIssue(workspaceSlug, projectId, issueId);
|
||||||
|
|
||||||
const issueIndex = this.issues[userId][this.currentView].findIndex((_issueId) => _issueId === issueId);
|
const uniqueViewId = `${workspaceSlug}_${this.currentView}`;
|
||||||
|
|
||||||
|
const issueIndex = this.issues[userId][uniqueViewId].findIndex((_issueId) => _issueId === issueId);
|
||||||
if (issueIndex >= 0)
|
if (issueIndex >= 0)
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.issues[userId][this.currentView].splice(issueIndex, 1);
|
this.issues[userId][uniqueViewId].splice(issueIndex, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
@ -77,14 +77,17 @@ export class WorkspaceIssues extends IssueHelperStore implements IWorkspaceIssue
|
|||||||
|
|
||||||
get groupedIssueIds() {
|
get groupedIssueIds() {
|
||||||
const viewId = this.rootIssueStore.globalViewId;
|
const viewId = this.rootIssueStore.globalViewId;
|
||||||
if (!viewId) return { dataViewId: "", issueIds: undefined };
|
const workspaceSlug = this.rootIssueStore.workspaceSlug;
|
||||||
|
if (!workspaceSlug || !viewId) return { dataViewId: "", issueIds: undefined };
|
||||||
|
|
||||||
|
const uniqueViewId = `${workspaceSlug}_${viewId}`;
|
||||||
|
|
||||||
const displayFilters = this.rootIssueStore?.workspaceIssuesFilter?.filters?.[viewId]?.displayFilters;
|
const displayFilters = this.rootIssueStore?.workspaceIssuesFilter?.filters?.[viewId]?.displayFilters;
|
||||||
if (!displayFilters) return { dataViewId: viewId, issueIds: undefined };
|
if (!displayFilters) return { dataViewId: viewId, issueIds: undefined };
|
||||||
|
|
||||||
const orderBy = displayFilters?.order_by;
|
const orderBy = displayFilters?.order_by;
|
||||||
|
|
||||||
const viewIssueIds = this.issues[viewId];
|
const viewIssueIds = this.issues[uniqueViewId];
|
||||||
|
|
||||||
if (!viewIssueIds) return { dataViewId: viewId, issueIds: undefined };
|
if (!viewIssueIds) return { dataViewId: viewId, issueIds: undefined };
|
||||||
|
|
||||||
@ -102,13 +105,15 @@ export class WorkspaceIssues extends IssueHelperStore implements IWorkspaceIssue
|
|||||||
try {
|
try {
|
||||||
this.loader = loadType;
|
this.loader = loadType;
|
||||||
|
|
||||||
|
const uniqueViewId = `${workspaceSlug}_${viewId}`;
|
||||||
|
|
||||||
const params = this.rootIssueStore?.workspaceIssuesFilter?.getAppliedFilters(viewId);
|
const params = this.rootIssueStore?.workspaceIssuesFilter?.getAppliedFilters(viewId);
|
||||||
const response = await this.workspaceService.getViewIssues(workspaceSlug, params);
|
const response = await this.workspaceService.getViewIssues(workspaceSlug, params);
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
set(
|
set(
|
||||||
this.issues,
|
this.issues,
|
||||||
[viewId],
|
[uniqueViewId],
|
||||||
response.map((issue) => issue.id)
|
response.map((issue) => issue.id)
|
||||||
);
|
);
|
||||||
this.loader = undefined;
|
this.loader = undefined;
|
||||||
@ -133,10 +138,12 @@ export class WorkspaceIssues extends IssueHelperStore implements IWorkspaceIssue
|
|||||||
try {
|
try {
|
||||||
if (!viewId) throw new Error("View id is required");
|
if (!viewId) throw new Error("View id is required");
|
||||||
|
|
||||||
|
const uniqueViewId = `${workspaceSlug}_${viewId}`;
|
||||||
|
|
||||||
const response = await this.issueService.createIssue(workspaceSlug, projectId, data);
|
const response = await this.issueService.createIssue(workspaceSlug, projectId, data);
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.issues[viewId].push(response.id);
|
this.issues[uniqueViewId].push(response.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.rootStore.issues.addIssue([response]);
|
this.rootStore.issues.addIssue([response]);
|
||||||
@ -175,12 +182,14 @@ export class WorkspaceIssues extends IssueHelperStore implements IWorkspaceIssue
|
|||||||
try {
|
try {
|
||||||
if (!viewId) throw new Error("View id is required");
|
if (!viewId) throw new Error("View id is required");
|
||||||
|
|
||||||
|
const uniqueViewId = `${workspaceSlug}_${viewId}`;
|
||||||
|
|
||||||
const response = await this.issueService.deleteIssue(workspaceSlug, projectId, issueId);
|
const response = await this.issueService.deleteIssue(workspaceSlug, projectId, issueId);
|
||||||
|
|
||||||
const issueIndex = this.issues[viewId].findIndex((_issueId) => _issueId === issueId);
|
const issueIndex = this.issues[uniqueViewId].findIndex((_issueId) => _issueId === issueId);
|
||||||
if (issueIndex >= 0)
|
if (issueIndex >= 0)
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.issues[viewId].splice(issueIndex, 1);
|
this.issues[uniqueViewId].splice(issueIndex, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.rootStore.issues.removeIssue(issueId);
|
this.rootStore.issues.removeIssue(issueId);
|
||||||
|
@ -7172,9 +7172,9 @@ postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.29:
|
|||||||
source-map-js "^1.0.2"
|
source-map-js "^1.0.2"
|
||||||
|
|
||||||
posthog-js@^1.105.0:
|
posthog-js@^1.105.0:
|
||||||
version "1.105.6"
|
version "1.105.8"
|
||||||
resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.105.6.tgz#3544de4389d5c7743fa420178bd127e49c4dc825"
|
resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.105.8.tgz#934602f0c7a5e522a25828062b5841ad8780756f"
|
||||||
integrity sha512-5ITXsh29XIuNohHLy21nawGnfFZDpyt+yfnWge9sJl5yv0nNuoUmLiDgw1tJafoqGrfd5CUasKyzSI21HxsSeQ==
|
integrity sha512-zKZKNVLLQQgkJyY3DnzHHTasu6x4xM4MOvH7UbMz6BmrgUPboS6/3akgz+WKD+JV6qFj68bm80iJw0Jtj+pt8Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
fflate "^0.4.8"
|
fflate "^0.4.8"
|
||||||
preact "^10.19.3"
|
preact "^10.19.3"
|
||||||
|
Loading…
Reference in New Issue
Block a user