[WEB-5043] feat: web vite migration (#7973)
This commit is contained in:
parent
118ecc81ba
commit
696fb96e87
642 changed files with 3013 additions and 2311 deletions
|
|
@ -8,10 +8,10 @@ import { useTheme } from "next-themes";
|
|||
import { API_BASE_URL } from "@plane/constants";
|
||||
import { OAuthOptions } from "@plane/ui";
|
||||
// assets
|
||||
import GithubLightLogo from "/public/logos/github-black.png";
|
||||
import GithubDarkLogo from "/public/logos/github-dark.svg";
|
||||
import GitlabLogo from "/public/logos/gitlab-logo.svg";
|
||||
import GoogleLogo from "/public/logos/google-logo.svg";
|
||||
import GithubLightLogo from "@/app/assets/logos/github-black.png?url";
|
||||
import GithubDarkLogo from "@/app/assets/logos/github-dark.svg?url";
|
||||
import GitlabLogo from "@/app/assets/logos/gitlab-logo.svg?url";
|
||||
import GoogleLogo from "@/app/assets/logos/google-logo.svg?url";
|
||||
// helpers
|
||||
import type { TAuthErrorInfo } from "@/helpers/authentication.helper";
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
import React from "react";
|
||||
import Image from "next/image";
|
||||
import { useTheme } from "next-themes";
|
||||
// plane package imports
|
||||
import { cn } from "@plane/utils";
|
||||
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
|
||||
// assets
|
||||
import darkBackgroundAsset from "@/app/assets/empty-state/analytics/empty-grid-background-dark.webp?url";
|
||||
import lightBackgroundAsset from "@/app/assets/empty-state/analytics/empty-grid-background-light.webp?url";
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
|
|
@ -12,7 +15,9 @@ type Props = {
|
|||
};
|
||||
|
||||
const AnalyticsEmptyState = ({ title, description, assetPath, className }: Props) => {
|
||||
const backgroundReolvedPath = useResolvedAssetPath({ basePath: "/empty-state/analytics/empty-grid-background" });
|
||||
// theme hook
|
||||
const { resolvedTheme } = useTheme();
|
||||
const backgroundReolvedPath = resolvedTheme === "light" ? lightBackgroundAsset : darkBackgroundAsset;
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { lazy, Suspense } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useParams } from "next/navigation";
|
||||
import useSWR from "swr";
|
||||
// plane package imports
|
||||
|
|
@ -14,7 +14,7 @@ import { AnalyticsService } from "@/services/analytics.service";
|
|||
import AnalyticsSectionWrapper from "../analytics-section-wrapper";
|
||||
import { ProjectInsightsLoader } from "../loaders";
|
||||
|
||||
const RadarChart = dynamic(() =>
|
||||
const RadarChart = lazy(() =>
|
||||
import("@plane/propel/charts/radar-chart").then((mod) => ({
|
||||
default: mod.RadarChart,
|
||||
}))
|
||||
|
|
@ -63,29 +63,31 @@ const ProjectInsights = observer(() => {
|
|||
) : (
|
||||
<div className="gap-8 lg:flex">
|
||||
{projectInsightsData && (
|
||||
<RadarChart
|
||||
className="h-[350px] w-full lg:w-3/5"
|
||||
data={projectInsightsData}
|
||||
dataKey="key"
|
||||
radars={[
|
||||
{
|
||||
key: "count",
|
||||
name: "Count",
|
||||
fill: "rgba(var(--color-primary-300))",
|
||||
stroke: "rgba(var(--color-primary-300))",
|
||||
fillOpacity: 0.6,
|
||||
dot: {
|
||||
r: 4,
|
||||
fillOpacity: 1,
|
||||
<Suspense fallback={<ProjectInsightsLoader />}>
|
||||
<RadarChart
|
||||
className="h-[350px] w-full lg:w-3/5"
|
||||
data={projectInsightsData}
|
||||
dataKey="key"
|
||||
radars={[
|
||||
{
|
||||
key: "count",
|
||||
name: "Count",
|
||||
fill: "rgba(var(--color-primary-300))",
|
||||
stroke: "rgba(var(--color-primary-300))",
|
||||
fillOpacity: 0.6,
|
||||
dot: {
|
||||
r: 4,
|
||||
fillOpacity: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
]}
|
||||
margin={{ top: 0, right: 40, bottom: 10, left: 40 }}
|
||||
showTooltip
|
||||
angleAxis={{
|
||||
key: "name",
|
||||
}}
|
||||
/>
|
||||
]}
|
||||
margin={{ top: 0, right: 40, bottom: 10, left: 40 }}
|
||||
showTooltip
|
||||
angleAxis={{
|
||||
key: "name",
|
||||
}}
|
||||
/>
|
||||
</Suspense>
|
||||
)}
|
||||
<div className="w-full lg:w-2/5">
|
||||
<div className="text-sm text-custom-text-300">{t("workspace_analytics.summary_of_projects")}</div>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import Image from "next/image";
|
|||
// ui
|
||||
import { Button } from "@plane/propel/button";
|
||||
// assets
|
||||
import emptyApiTokens from "@/public/empty-state/api-token.svg";
|
||||
import emptyApiTokens from "@/app/assets/empty-state/api-token.svg?url";
|
||||
|
||||
type Props = {
|
||||
onClick: () => void;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import Image from "next/image";
|
||||
// assets
|
||||
import ProjectNotAuthorizedImg from "@/app/assets/auth/project-not-authorized.svg?url";
|
||||
import Unauthorized from "@/app/assets/auth/unauthorized.svg?url";
|
||||
import WorkspaceNotAuthorizedImg from "@/app/assets/auth/workspace-not-authorized.svg?url";
|
||||
// layouts
|
||||
import DefaultLayout from "@/layouts/default-layout";
|
||||
// images
|
||||
import ProjectNotAuthorizedImg from "@/public/auth/project-not-authorized.svg";
|
||||
import Unauthorized from "@/public/auth/unauthorized.svg";
|
||||
import WorkspaceNotAuthorizedImg from "@/public/auth/workspace-not-authorized.svg";
|
||||
|
||||
type Props = {
|
||||
actionButton?: React.ReactNode;
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ import { useParams } from "next/navigation";
|
|||
import { ClipboardList } from "lucide-react";
|
||||
// plane imports
|
||||
import { Button } from "@plane/propel/button";
|
||||
// assets
|
||||
import Unauthorized from "@/app/assets/auth/unauthorized.svg?url";
|
||||
// hooks
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
// assets
|
||||
import Unauthorized from "@/public/auth/unauthorized.svg";
|
||||
|
||||
type Props = {
|
||||
projectId?: string;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { useTheme } from "next-themes";
|
|||
// icons
|
||||
import { Lightbulb } from "lucide-react";
|
||||
// images
|
||||
import latestFeatures from "@/public/onboarding/onboarding-pages.webp";
|
||||
import latestFeatures from "@/app/assets/onboarding/onboarding-pages.webp?url";
|
||||
|
||||
export const LatestFeatureBlock = () => {
|
||||
const { resolvedTheme } = useTheme();
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import Image from "next/image";
|
||||
import { useTheme } from "next-themes";
|
||||
// assets
|
||||
import LogoSpinnerDark from "@/public/images/logo-spinner-dark.gif";
|
||||
import LogoSpinnerLight from "@/public/images/logo-spinner-light.gif";
|
||||
import LogoSpinnerDark from "@/app/assets/images/logo-spinner-dark.gif?url";
|
||||
import LogoSpinnerLight from "@/app/assets/images/logo-spinner-light.gif?url";
|
||||
|
||||
export const LogoSpinner = () => {
|
||||
const { resolvedTheme } = useTheme();
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { useTheme } from "next-themes";
|
||||
import type { SubmitHandler } from "react-hook-form";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { Search } from "lucide-react";
|
||||
|
|
@ -14,13 +15,17 @@ import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
|||
import type { ISearchIssueResponse, IUser } from "@plane/types";
|
||||
import { EIssuesStoreType } from "@plane/types";
|
||||
import { Loader } from "@plane/ui";
|
||||
// assets
|
||||
import darkIssuesAsset from "@/app/assets/empty-state/search/issues-dark.webp?url";
|
||||
import lightIssuesAsset from "@/app/assets/empty-state/search/issues-light.webp?url";
|
||||
import darkSearchAsset from "@/app/assets/empty-state/search/search-dark.webp?url";
|
||||
import lightSearchAsset from "@/app/assets/empty-state/search/search-light.webp?url";
|
||||
// components
|
||||
import { SimpleEmptyState } from "@/components/empty-state/simple-empty-state-root";
|
||||
// hooks
|
||||
import { useIssues } from "@/hooks/store/use-issues";
|
||||
import useDebounce from "@/hooks/use-debounce";
|
||||
// services
|
||||
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
|
||||
import { ProjectService } from "@/services/project";
|
||||
// local components
|
||||
import { BulkDeleteIssuesModalItem } from "./bulk-delete-issues-modal-item";
|
||||
|
|
@ -45,6 +50,8 @@ export const BulkDeleteIssuesModal: React.FC<Props> = observer((props) => {
|
|||
const [query, setQuery] = useState("");
|
||||
const [issues, setIssues] = useState<ISearchIssueResponse[]>([]);
|
||||
const [isSearching, setIsSearching] = useState(false);
|
||||
// theme hook
|
||||
const { resolvedTheme } = useTheme();
|
||||
// hooks
|
||||
const {
|
||||
issues: { removeBulkIssues },
|
||||
|
|
@ -52,8 +59,8 @@ export const BulkDeleteIssuesModal: React.FC<Props> = observer((props) => {
|
|||
const { t } = useTranslation();
|
||||
// derived values
|
||||
const debouncedSearchTerm: string = useDebounce(query, 500);
|
||||
const searchResolvedPath = useResolvedAssetPath({ basePath: "/empty-state/search/search" });
|
||||
const issuesResolvedPath = useResolvedAssetPath({ basePath: "/empty-state/search/issues" });
|
||||
const searchResolvedPath = resolvedTheme === "light" ? lightSearchAsset : darkSearchAsset;
|
||||
const issuesResolvedPath = resolvedTheme === "light" ? lightIssuesAsset : darkIssuesAsset;
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen || !workspaceSlug || !projectId) return;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,15 @@
|
|||
import React from "react";
|
||||
import { useTheme } from "next-themes";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type { ISearchIssueResponse } from "@plane/types";
|
||||
// assets
|
||||
import darkIssuesAsset from "@/app/assets/empty-state/search/issues-dark.webp?url";
|
||||
import lightIssuesAsset from "@/app/assets/empty-state/search/issues-light.webp?url";
|
||||
import darkSearchAsset from "@/app/assets/empty-state/search/search-dark.webp?url";
|
||||
import lightSearchAsset from "@/app/assets/empty-state/search/search-light.webp?url";
|
||||
// components
|
||||
import { SimpleEmptyState } from "@/components/empty-state/simple-empty-state-root";
|
||||
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
|
||||
|
||||
interface EmptyStateProps {
|
||||
issues: ISearchIssueResponse[];
|
||||
|
|
@ -19,11 +24,13 @@ export const IssueSearchModalEmptyState: React.FC<EmptyStateProps> = ({
|
|||
debouncedSearchTerm,
|
||||
isSearching,
|
||||
}) => {
|
||||
// theme hook
|
||||
const { resolvedTheme } = useTheme();
|
||||
// plane hooks
|
||||
const { t } = useTranslation();
|
||||
// derived values
|
||||
const searchResolvedPath = useResolvedAssetPath({ basePath: "/empty-state/search/search" });
|
||||
const issuesResolvedPath = useResolvedAssetPath({ basePath: "/empty-state/search/issues" });
|
||||
const searchResolvedPath = resolvedTheme === "light" ? lightSearchAsset : darkSearchAsset;
|
||||
const issuesResolvedPath = resolvedTheme === "light" ? lightIssuesAsset : darkIssuesAsset;
|
||||
|
||||
const EmptyStateContainer = ({ children }: { children: React.ReactNode }) => (
|
||||
<div className="flex flex-col items-center justify-center px-3 py-8 text-center">{children}</div>
|
||||
|
|
|
|||
|
|
@ -4,10 +4,11 @@ import Image from "next/image";
|
|||
import { useTranslation } from "@plane/i18n";
|
||||
import { Avatar } from "@plane/ui";
|
||||
import { getFileURL } from "@plane/utils";
|
||||
// assets
|
||||
import emptyMembers from "@/app/assets/empty-state/empty_members.svg?url";
|
||||
import userImage from "@/app/assets/user.png?url";
|
||||
// components
|
||||
import { SingleProgressStats } from "@/components/core/sidebar/single-progress-stats";
|
||||
// public
|
||||
import emptyMembers from "@/public/empty-state/empty_members.svg";
|
||||
|
||||
export type TAssigneeData = {
|
||||
id: string | undefined;
|
||||
|
|
@ -56,7 +57,7 @@ export const AssigneeStatComponent = observer((props: TAssigneeStatComponent) =>
|
|||
title={
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="h-4 w-4 rounded-full border-2 border-custom-border-200 bg-custom-background-80">
|
||||
<img src="/user.png" height="100%" width="100%" className="rounded-full" alt="User" />
|
||||
<img src={userImage} height="100%" width="100%" className="rounded-full" alt="User" />
|
||||
</div>
|
||||
<span>{t("no_assignee")}</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ import { observer } from "mobx-react";
|
|||
import Image from "next/image";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
// assets
|
||||
import emptyLabel from "@/app/assets/empty-state/empty_label.svg?url";
|
||||
// components
|
||||
import { SingleProgressStats } from "@/components/core/sidebar/single-progress-stats";
|
||||
// public
|
||||
import emptyLabel from "@/public/empty-state/empty_label.svg";
|
||||
|
||||
export type TLabelData = {
|
||||
id: string | undefined;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import type { FC } from "react";
|
|||
import { Fragment, useCallback, useRef, useState } from "react";
|
||||
import { isEmpty } from "lodash-es";
|
||||
import { observer } from "mobx-react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { CalendarCheck } from "lucide-react";
|
||||
// headless ui
|
||||
import { Tab } from "@headlessui/react";
|
||||
|
|
@ -17,18 +18,24 @@ import { EIssuesStoreType } from "@plane/types";
|
|||
// ui
|
||||
import { Loader, Avatar } from "@plane/ui";
|
||||
import { cn, renderFormattedDate, renderFormattedDateWithoutYear, getFileURL } from "@plane/utils";
|
||||
// assets
|
||||
import darkAssigneeAsset from "@/app/assets/empty-state/active-cycle/assignee-dark.webp?url";
|
||||
import lightAssigneeAsset from "@/app/assets/empty-state/active-cycle/assignee-light.webp?url";
|
||||
import darkLabelAsset from "@/app/assets/empty-state/active-cycle/label-dark.webp?url";
|
||||
import lightLabelAsset from "@/app/assets/empty-state/active-cycle/label-light.webp?url";
|
||||
import darkPriorityAsset from "@/app/assets/empty-state/active-cycle/priority-dark.webp?url";
|
||||
import lightPriorityAsset from "@/app/assets/empty-state/active-cycle/priority-light.webp?url";
|
||||
import userImage from "@/app/assets/user.png?url";
|
||||
// components
|
||||
import { SingleProgressStats } from "@/components/core/sidebar/single-progress-stats";
|
||||
import { StateDropdown } from "@/components/dropdowns/state/dropdown";
|
||||
import { SimpleEmptyState } from "@/components/empty-state/simple-empty-state-root";
|
||||
// helpers
|
||||
// hooks
|
||||
import { useIssueDetail } from "@/hooks/store/use-issue-detail";
|
||||
import { useIssues } from "@/hooks/store/use-issues";
|
||||
import { useIntersectionObserver } from "@/hooks/use-intersection-observer";
|
||||
import useLocalStorage from "@/hooks/use-local-storage";
|
||||
// plane web components
|
||||
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
|
||||
import { IssueIdentifier } from "@/plane-web/components/issues/issue-details/issue-identifier";
|
||||
// store
|
||||
import type { ActiveCycleIssueDetails } from "@/store/issue/cycle";
|
||||
|
|
@ -50,12 +57,14 @@ export const ActiveCycleStats: FC<ActiveCycleStatsProps> = observer((props) => {
|
|||
const issuesContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
// states
|
||||
const [issuesLoaderElement, setIssueLoaderElement] = useState<HTMLDivElement | null>(null);
|
||||
// theme hook
|
||||
const { resolvedTheme } = useTheme();
|
||||
// plane hooks
|
||||
const { t } = useTranslation();
|
||||
// derived values
|
||||
const priorityResolvedPath = useResolvedAssetPath({ basePath: "/empty-state/active-cycle/priority" });
|
||||
const assigneesResolvedPath = useResolvedAssetPath({ basePath: "/empty-state/active-cycle/assignee" });
|
||||
const labelsResolvedPath = useResolvedAssetPath({ basePath: "/empty-state/active-cycle/label" });
|
||||
const priorityResolvedPath = resolvedTheme === "light" ? lightPriorityAsset : darkPriorityAsset;
|
||||
const assigneesResolvedPath = resolvedTheme === "light" ? lightAssigneeAsset : darkAssigneeAsset;
|
||||
const labelsResolvedPath = resolvedTheme === "light" ? lightLabelAsset : darkLabelAsset;
|
||||
|
||||
const currentValue = (tab: string | null) => {
|
||||
switch (tab) {
|
||||
|
|
@ -294,7 +303,7 @@ export const ActiveCycleStats: FC<ActiveCycleStatsProps> = observer((props) => {
|
|||
title={
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="h-5 w-5 rounded-full border-2 border-custom-border-200 bg-custom-background-80">
|
||||
<img src="/user.png" height="100%" width="100%" className="rounded-full" alt="User" />
|
||||
<img src={userImage} height="100%" width="100%" className="rounded-full" alt="User" />
|
||||
</div>
|
||||
<span>{t("no_assignee")}</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,17 +2,19 @@ import type { FC } from "react";
|
|||
import { Fragment } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import Link from "next/link";
|
||||
import { useTheme } from "next-themes";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type { ICycle, TCycleEstimateType } from "@plane/types";
|
||||
import { Loader } from "@plane/ui";
|
||||
// assets
|
||||
import darkChartAsset from "@/app/assets/empty-state/active-cycle/chart-dark.webp?url";
|
||||
import lightChartAsset from "@/app/assets/empty-state/active-cycle/chart-light.webp?url";
|
||||
// components
|
||||
import ProgressChart from "@/components/core/sidebar/progress-chart";
|
||||
import { SimpleEmptyState } from "@/components/empty-state/simple-empty-state-root";
|
||||
// constants
|
||||
// hooks
|
||||
import { useCycle } from "@/hooks/store/use-cycle";
|
||||
// plane web constants
|
||||
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
|
||||
import { EstimateTypeDropdown } from "../dropdowns/estimate-type-dropdown";
|
||||
|
||||
export type ActiveCycleProductivityProps = {
|
||||
|
|
@ -23,13 +25,15 @@ export type ActiveCycleProductivityProps = {
|
|||
|
||||
export const ActiveCycleProductivity: FC<ActiveCycleProductivityProps> = observer((props) => {
|
||||
const { workspaceSlug, projectId, cycle } = props;
|
||||
// theme hook
|
||||
const { resolvedTheme } = useTheme();
|
||||
// plane hooks
|
||||
const { t } = useTranslation();
|
||||
// hooks
|
||||
const { getEstimateTypeByCycleId, setEstimateType } = useCycle();
|
||||
// derived values
|
||||
const estimateType: TCycleEstimateType = (cycle && getEstimateTypeByCycleId(cycle.id)) || "issues";
|
||||
const resolvedPath = useResolvedAssetPath({ basePath: "/empty-state/active-cycle/chart" });
|
||||
const resolvedPath = resolvedTheme === "light" ? lightChartAsset : darkChartAsset;
|
||||
|
||||
const onChange = async (value: TCycleEstimateType) => {
|
||||
if (!workspaceSlug || !projectId || !cycle || !cycle.id) return;
|
||||
|
|
|
|||
|
|
@ -2,16 +2,18 @@
|
|||
|
||||
import type { FC } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useTheme } from "next-themes";
|
||||
// plane imports
|
||||
import { PROGRESS_STATE_GROUPS_DETAILS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type { TWorkItemFilterCondition } from "@plane/shared-state";
|
||||
import type { ICycle } from "@plane/types";
|
||||
import { LinearProgressIndicator, Loader } from "@plane/ui";
|
||||
// assets
|
||||
import darkProgressAsset from "@/app/assets/empty-state/active-cycle/progress-dark.webp?url";
|
||||
import lightProgressAsset from "@/app/assets/empty-state/active-cycle/progress-light.webp?url";
|
||||
// components
|
||||
import { SimpleEmptyState } from "@/components/empty-state/simple-empty-state-root";
|
||||
// hooks
|
||||
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
|
||||
|
||||
export type ActiveCycleProgressProps = {
|
||||
cycle: ICycle | null;
|
||||
|
|
@ -22,6 +24,8 @@ export type ActiveCycleProgressProps = {
|
|||
|
||||
export const ActiveCycleProgress: FC<ActiveCycleProgressProps> = observer((props) => {
|
||||
const { handleFiltersUpdate, cycle } = props;
|
||||
// theme hook
|
||||
const { resolvedTheme } = useTheme();
|
||||
// plane hooks
|
||||
const { t } = useTranslation();
|
||||
// derived values
|
||||
|
|
@ -39,7 +43,7 @@ export const ActiveCycleProgress: FC<ActiveCycleProgressProps> = observer((props
|
|||
backlog: cycle?.backlog_issues,
|
||||
}
|
||||
: {};
|
||||
const resolvedPath = useResolvedAssetPath({ basePath: "/empty-state/active-cycle/progress" });
|
||||
const resolvedPath = resolvedTheme === "light" ? lightProgressAsset : darkProgressAsset;
|
||||
|
||||
return cycle && cycle.hasOwnProperty("started_issues") ? (
|
||||
<div className="flex flex-col min-h-[17rem] gap-5 py-4 px-3.5 bg-custom-background-100 border border-custom-border-200 rounded-lg">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import type { FC } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import Image from "next/image";
|
||||
// assets
|
||||
import AllFiltersImage from "@/app/assets/empty-state/cycle/all-filters.svg?url";
|
||||
import NameFilterImage from "@/app/assets/empty-state/cycle/name-filter.svg?url";
|
||||
// components
|
||||
import { CyclesList } from "@/components/cycles/list";
|
||||
// ui
|
||||
|
|
@ -8,9 +11,6 @@ import { CycleModuleListLayoutLoader } from "@/components/ui/loader/cycle-module
|
|||
// hooks
|
||||
import { useCycle } from "@/hooks/store/use-cycle";
|
||||
import { useCycleFilter } from "@/hooks/store/use-cycle-filter";
|
||||
// assets
|
||||
import AllFiltersImage from "@/public/empty-state/cycle/all-filters.svg";
|
||||
import NameFilterImage from "@/public/empty-state/cycle/name-filter.svg";
|
||||
|
||||
export interface IArchivedCyclesView {
|
||||
workspaceSlug: string;
|
||||
|
|
|
|||
|
|
@ -3,15 +3,15 @@ import { observer } from "mobx-react";
|
|||
import Image from "next/image";
|
||||
// components
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
// assets
|
||||
import AllFiltersImage from "@/app/assets/empty-state/cycle/all-filters.svg?url";
|
||||
import NameFilterImage from "@/app/assets/empty-state/cycle/name-filter.svg?url";
|
||||
// components
|
||||
import { CyclesList } from "@/components/cycles/list";
|
||||
// ui
|
||||
import { CycleModuleListLayoutLoader } from "@/components/ui/loader/cycle-module-list-loader";
|
||||
// hooks
|
||||
import { useCycle } from "@/hooks/store/use-cycle";
|
||||
import { useCycleFilter } from "@/hooks/store/use-cycle-filter";
|
||||
// assets
|
||||
import AllFiltersImage from "@/public/empty-state/cycle/all-filters.svg";
|
||||
import NameFilterImage from "@/public/empty-state/cycle/name-filter.svg";
|
||||
|
||||
export interface ICyclesView {
|
||||
workspaceSlug: string;
|
||||
|
|
|
|||
|
|
@ -3,30 +3,40 @@
|
|||
import type { PageProps } from "@react-pdf/renderer";
|
||||
import { Document, Font, Page } from "@react-pdf/renderer";
|
||||
import { Html } from "react-pdf-html";
|
||||
// assets
|
||||
import interBold from "@/app/assets/fonts/inter/bold.ttf?url";
|
||||
import interHeavy from "@/app/assets/fonts/inter/heavy.ttf?url";
|
||||
import interLight from "@/app/assets/fonts/inter/light.ttf?url";
|
||||
import interMedium from "@/app/assets/fonts/inter/medium.ttf?url";
|
||||
import interRegular from "@/app/assets/fonts/inter/regular.ttf?url";
|
||||
import interSemibold from "@/app/assets/fonts/inter/semibold.ttf?url";
|
||||
import interThin from "@/app/assets/fonts/inter/thin.ttf?url";
|
||||
import interUltraBold from "@/app/assets/fonts/inter/ultrabold.ttf?url";
|
||||
import interUltraLight from "@/app/assets/fonts/inter/ultralight.ttf?url";
|
||||
// constants
|
||||
import { EDITOR_PDF_DOCUMENT_STYLESHEET } from "@/constants/editor";
|
||||
|
||||
Font.register({
|
||||
family: "Inter",
|
||||
fonts: [
|
||||
{ src: "/fonts/inter/thin.ttf", fontWeight: "thin" },
|
||||
{ src: "/fonts/inter/thin.ttf", fontWeight: "thin", fontStyle: "italic" },
|
||||
{ src: "/fonts/inter/ultralight.ttf", fontWeight: "ultralight" },
|
||||
{ src: "/fonts/inter/ultralight.ttf", fontWeight: "ultralight", fontStyle: "italic" },
|
||||
{ src: "/fonts/inter/light.ttf", fontWeight: "light" },
|
||||
{ src: "/fonts/inter/light.ttf", fontWeight: "light", fontStyle: "italic" },
|
||||
{ src: "/fonts/inter/regular.ttf", fontWeight: "normal" },
|
||||
{ src: "/fonts/inter/regular.ttf", fontWeight: "normal", fontStyle: "italic" },
|
||||
{ src: "/fonts/inter/medium.ttf", fontWeight: "medium" },
|
||||
{ src: "/fonts/inter/medium.ttf", fontWeight: "medium", fontStyle: "italic" },
|
||||
{ src: "/fonts/inter/semibold.ttf", fontWeight: "semibold" },
|
||||
{ src: "/fonts/inter/semibold.ttf", fontWeight: "semibold", fontStyle: "italic" },
|
||||
{ src: "/fonts/inter/bold.ttf", fontWeight: "bold" },
|
||||
{ src: "/fonts/inter/bold.ttf", fontWeight: "bold", fontStyle: "italic" },
|
||||
{ src: "/fonts/inter/extrabold.ttf", fontWeight: "ultrabold" },
|
||||
{ src: "/fonts/inter/extrabold.ttf", fontWeight: "ultrabold", fontStyle: "italic" },
|
||||
{ src: "/fonts/inter/heavy.ttf", fontWeight: "heavy" },
|
||||
{ src: "/fonts/inter/heavy.ttf", fontWeight: "heavy", fontStyle: "italic" },
|
||||
{ src: interThin, fontWeight: "thin" },
|
||||
{ src: interThin, fontWeight: "thin", fontStyle: "italic" },
|
||||
{ src: interUltraLight, fontWeight: "ultralight" },
|
||||
{ src: interUltraLight, fontWeight: "ultralight", fontStyle: "italic" },
|
||||
{ src: interLight, fontWeight: "light" },
|
||||
{ src: interLight, fontWeight: "light", fontStyle: "italic" },
|
||||
{ src: interRegular, fontWeight: "normal" },
|
||||
{ src: interRegular, fontWeight: "normal", fontStyle: "italic" },
|
||||
{ src: interMedium, fontWeight: "medium" },
|
||||
{ src: interMedium, fontWeight: "medium", fontStyle: "italic" },
|
||||
{ src: interSemibold, fontWeight: "semibold" },
|
||||
{ src: interSemibold, fontWeight: "semibold", fontStyle: "italic" },
|
||||
{ src: interBold, fontWeight: "bold" },
|
||||
{ src: interBold, fontWeight: "bold", fontStyle: "italic" },
|
||||
{ src: interUltraBold, fontWeight: "ultrabold" },
|
||||
{ src: interUltraBold, fontWeight: "ultrabold", fontStyle: "italic" },
|
||||
{ src: interHeavy, fontWeight: "heavy" },
|
||||
{ src: interHeavy, fontWeight: "heavy", fontStyle: "italic" },
|
||||
],
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import Image from "next/image";
|
||||
// ui
|
||||
import { Button } from "@plane/propel/button";
|
||||
// utils
|
||||
|
|
@ -85,9 +84,7 @@ export const DetailedEmptyState: React.FC<Props> = observer((props) => {
|
|||
{description && <p className="text-sm">{description}</p>}
|
||||
</div>
|
||||
|
||||
{assetPath && (
|
||||
<Image src={assetPath} alt={title} width={384} height={250} layout="responsive" lazyBoundary="100%" />
|
||||
)}
|
||||
{assetPath && <img src={assetPath} alt={title} className="w-full h-auto" loading="lazy" />}
|
||||
|
||||
{hasButtons && (
|
||||
<div className="relative flex items-center justify-center gap-2 flex-shrink-0 w-full">
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
import { observer } from "mobx-react";
|
||||
import { useParams, usePathname } from "next/navigation";
|
||||
import { useTheme } from "next-themes";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type { THomeWidgetKeys, THomeWidgetProps } from "@plane/types";
|
||||
// assets
|
||||
import darkWidgetsAsset from "@/app/assets/empty-state/dashboard/widgets-dark.webp?url";
|
||||
import lightWidgetsAsset from "@/app/assets/empty-state/dashboard/widgets-light.webp?url";
|
||||
// components
|
||||
import { SimpleEmptyState } from "@/components/empty-state/simple-empty-state-root";
|
||||
// hooks
|
||||
import { useHome } from "@/hooks/store/use-home";
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
// plane web components
|
||||
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
|
||||
import { HomePageHeader } from "@/plane-web/components/home/header";
|
||||
// local imports
|
||||
import { StickiesWidget } from "../stickies/widget";
|
||||
|
|
@ -56,6 +59,8 @@ export const DashboardWidgets = observer(() => {
|
|||
const { workspaceSlug } = useParams();
|
||||
// navigation
|
||||
const pathname = usePathname();
|
||||
// theme hook
|
||||
const { resolvedTheme } = useTheme();
|
||||
// store hooks
|
||||
const { toggleWidgetSettings, widgetsMap, showWidgetSettings, orderedWidgets, isAnyWidgetEnabled, loading } =
|
||||
useHome();
|
||||
|
|
@ -63,7 +68,7 @@ export const DashboardWidgets = observer(() => {
|
|||
// plane hooks
|
||||
const { t } = useTranslation();
|
||||
// derived values
|
||||
const noWidgetsResolvedPath = useResolvedAssetPath({ basePath: "/empty-state/dashboard/widgets" });
|
||||
const noWidgetsResolvedPath = resolvedTheme === "light" ? lightWidgetsAsset : darkWidgetsAsset;
|
||||
|
||||
// derived values
|
||||
const isWikiApp = pathname.includes(`/${workspaceSlug.toString()}/pages`);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import { Controller, useForm } from "react-hook-form";
|
|||
import { useTranslation } from "@plane/i18n";
|
||||
import { Button } from "@plane/propel/button";
|
||||
import type { TLinkEditableFields } from "@plane/types";
|
||||
import { TLink } from "@plane/types";
|
||||
import { Input, ModalCore } from "@plane/ui";
|
||||
import type { TLinkOperations } from "./use-links";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import Image from "next/image";
|
||||
// image
|
||||
import AudioFileIcon from "@/public/attachment/audio-icon.png";
|
||||
import AudioFileIcon from "@/app/assets/attachment/audio-icon.png?url";
|
||||
|
||||
export type AudioIconProps = {
|
||||
width?: number;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import Image from "next/image";
|
||||
// image
|
||||
import CssFileIcon from "@/public/attachment/css-icon.png";
|
||||
import CssFileIcon from "@/app/assets/attachment/css-icon.png?url";
|
||||
// type
|
||||
import type { ImageIconPros } from "../types";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import Image from "next/image";
|
||||
// image
|
||||
import CSVFileIcon from "@/public/attachment/csv-icon.png";
|
||||
import CSVFileIcon from "@/app/assets/attachment/csv-icon.png?url";
|
||||
// type
|
||||
import type { ImageIconPros } from "../types";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import Image from "next/image";
|
||||
// image
|
||||
import DefaultFileIcon from "@/public/attachment/default-icon.png";
|
||||
import DefaultFileIcon from "@/app/assets/attachment/default-icon.png?url";
|
||||
// type
|
||||
import type { ImageIconPros } from "../types";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import Image from "next/image";
|
||||
// image
|
||||
import DocFileIcon from "@/public/attachment/doc-icon.png";
|
||||
import DocFileIcon from "@/app/assets/attachment/doc-icon.png?url";
|
||||
// type
|
||||
import type { ImageIconPros } from "../types";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import Image from "next/image";
|
||||
// image
|
||||
import FigmaFileIcon from "@/public/attachment/figma-icon.png";
|
||||
import FigmaFileIcon from "@/app/assets/attachment/figma-icon.png?url";
|
||||
// type
|
||||
import type { ImageIconPros } from "../types";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import Image from "next/image";
|
||||
// image
|
||||
import HtmlFileIcon from "@/public/attachment/html-icon.png";
|
||||
import HtmlFileIcon from "@/app/assets/attachment/html-icon.png?url";
|
||||
// type
|
||||
import type { ImageIconPros } from "../types";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import Image from "next/image";
|
||||
// image
|
||||
import ImgFileIcon from "@/public/attachment/img-icon.png";
|
||||
import ImgFileIcon from "@/app/assets/attachment/img-icon.png?url";
|
||||
// type
|
||||
import type { ImageIconPros } from "../types";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import Image from "next/image";
|
||||
// image
|
||||
import JpgFileIcon from "@/public/attachment/jpg-icon.png";
|
||||
import JpgFileIcon from "@/app/assets/attachment/jpg-icon.png?url";
|
||||
// type
|
||||
import type { ImageIconPros } from "../types";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import Image from "next/image";
|
||||
// image
|
||||
import JsFileIcon from "@/public/attachment/js-icon.png";
|
||||
import JsFileIcon from "@/app/assets/attachment/js-icon.png?url";
|
||||
// type
|
||||
import type { ImageIconPros } from "../types";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import Image from "next/image";
|
||||
// image
|
||||
import PDFFileIcon from "@/public/attachment/pdf-icon.png";
|
||||
import PDFFileIcon from "@/app/assets/attachment/pdf-icon.png?url";
|
||||
// type
|
||||
import type { ImageIconPros } from "../types";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import Image from "next/image";
|
||||
// image
|
||||
import PngFileIcon from "@/public/attachment/png-icon.png";
|
||||
import PngFileIcon from "@/app/assets/attachment/png-icon.png?url";
|
||||
// type
|
||||
import type { ImageIconPros } from "../types";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import Image from "next/image";
|
||||
// image
|
||||
import RarFileIcon from "@/public/attachment/rar-icon.png";
|
||||
import RarFileIcon from "@/app/assets/attachment/rar-icon.png?url";
|
||||
// type
|
||||
import type { ImageIconPros } from "../types";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import Image from "next/image";
|
||||
// image
|
||||
import SheetFileIcon from "@/public/attachment/excel-icon.png";
|
||||
import SheetFileIcon from "@/app/assets/attachment/excel-icon.png?url";
|
||||
// type
|
||||
import type { ImageIconPros } from "../types";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import Image from "next/image";
|
||||
// image
|
||||
import SvgFileIcon from "@/public/attachment/svg-icon.png";
|
||||
import SvgFileIcon from "@/app/assets/attachment/svg-icon.png?url";
|
||||
// type
|
||||
import type { ImageIconPros } from "../types";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import Image from "next/image";
|
||||
// image
|
||||
import TxtFileIcon from "@/public/attachment/txt-icon.png";
|
||||
import TxtFileIcon from "@/app/assets/attachment/txt-icon.png?url";
|
||||
// type
|
||||
import type { ImageIconPros } from "../types";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import Image from "next/image";
|
||||
// image
|
||||
import VideoFileIcon from "@/public/attachment/video-icon.png";
|
||||
import VideoFileIcon from "@/app/assets/attachment/video-icon.png?url";
|
||||
// type
|
||||
import type { ImageIconPros } from "../types";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import Image from "next/image";
|
||||
// image
|
||||
import ZipFileIcon from "@/public/attachment/zip-icon.png";
|
||||
import ZipFileIcon from "@/app/assets/attachment/zip-icon.png?url";
|
||||
// type
|
||||
import type { ImageIconPros } from "../types";
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { useTheme } from "next-themes";
|
||||
import { Search } from "lucide-react";
|
||||
import { Combobox, Dialog, Transition } from "@headlessui/react";
|
||||
// plane imports
|
||||
|
|
@ -9,12 +10,16 @@ import { useTranslation } from "@plane/i18n";
|
|||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import type { ISearchIssueResponse } from "@plane/types";
|
||||
import { Loader } from "@plane/ui";
|
||||
// assets
|
||||
import darkIssuesAsset from "@/app/assets/empty-state/search/issues-dark.webp?url";
|
||||
import lightIssuesAsset from "@/app/assets/empty-state/search/issues-light.webp?url";
|
||||
import darkSearchAsset from "@/app/assets/empty-state/search/search-dark.webp?url";
|
||||
import lightSearchAsset from "@/app/assets/empty-state/search/search-light.webp?url";
|
||||
// components
|
||||
import { SimpleEmptyState } from "@/components/empty-state/simple-empty-state-root";
|
||||
// hooks
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
import useDebounce from "@/hooks/use-debounce";
|
||||
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
|
||||
// services
|
||||
import { ProjectService } from "@/services/project";
|
||||
|
||||
|
|
@ -35,13 +40,15 @@ export const SelectDuplicateInboxIssueModal: React.FC<Props> = (props) => {
|
|||
const [query, setQuery] = useState("");
|
||||
const [issues, setIssues] = useState<ISearchIssueResponse[]>([]);
|
||||
const [isSearching, setIsSearching] = useState(false);
|
||||
// theme hook
|
||||
const { resolvedTheme } = useTheme();
|
||||
// hooks
|
||||
const { getProjectById } = useProject();
|
||||
const { t } = useTranslation();
|
||||
// derived values
|
||||
const debouncedSearchTerm: string = useDebounce(query, 500);
|
||||
const searchResolvedPath = useResolvedAssetPath({ basePath: "/empty-state/search/search" });
|
||||
const issuesResolvedPath = useResolvedAssetPath({ basePath: "/empty-state/search/issues" });
|
||||
const searchResolvedPath = resolvedTheme === "light" ? lightSearchAsset : darkSearchAsset;
|
||||
const issuesResolvedPath = resolvedTheme === "light" ? lightIssuesAsset : darkIssuesAsset;
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen || !workspaceSlug || !projectId) return;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import { InboxSidebar } from "@/components/inbox/sidebar";
|
|||
import { InboxLayoutLoader } from "@/components/ui/loader/layouts/project-inbox/inbox-layout-loader";
|
||||
// hooks
|
||||
import { useProjectInbox } from "@/hooks/store/use-project-inbox";
|
||||
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
|
||||
|
||||
type TInboxIssueRoot = {
|
||||
workspaceSlug: string;
|
||||
|
|
@ -32,8 +31,6 @@ export const InboxIssueRoot: FC<TInboxIssueRoot> = observer((props) => {
|
|||
const { t } = useTranslation();
|
||||
// hooks
|
||||
const { loader, error, currentTab, handleCurrentTab, fetchInboxIssues } = useProjectInbox();
|
||||
// derived values
|
||||
const resolvedPath = useResolvedAssetPath({ basePath: "/empty-state/intake/issue-detail" });
|
||||
|
||||
useEffect(() => {
|
||||
if (!inboxAccessible || !workspaceSlug || !projectId) return;
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
import type { FC } from "react";
|
||||
import Image from "next/image";
|
||||
import { useTheme } from "next-themes";
|
||||
// assets
|
||||
import maintenanceModeDarkModeImage from "@/app/assets/instance/maintenance-mode-dark.svg?url";
|
||||
import maintenanceModeLightModeImage from "@/app/assets/instance/maintenance-mode-light.svg?url";
|
||||
// layouts
|
||||
import DefaultLayout from "@/layouts/default-layout";
|
||||
// components
|
||||
import { MaintenanceMessage } from "@/plane-web/components/instance";
|
||||
// images
|
||||
import maintenanceModeDarkModeImage from "@/public/instance/maintenance-mode-dark.svg";
|
||||
import maintenanceModeLightModeImage from "@/public/instance/maintenance-mode-light.svg";
|
||||
|
||||
export const MaintenanceView: FC = () => {
|
||||
// hooks
|
||||
|
|
|
|||
|
|
@ -7,12 +7,10 @@ import { useTheme } from "next-themes";
|
|||
import { GOD_MODE_URL } from "@plane/constants";
|
||||
import { Button } from "@plane/propel/button";
|
||||
import { PlaneLockup } from "@plane/propel/icons";
|
||||
// helpers
|
||||
// images
|
||||
// assets
|
||||
import PlaneBackgroundPatternDark from "@/public/auth/background-pattern-dark.svg";
|
||||
import PlaneBackgroundPattern from "@/public/auth/background-pattern.svg";
|
||||
import PlaneTakeOffImage from "@/public/plane-takeoff.png";
|
||||
import PlaneBackgroundPatternDark from "@/app/assets/auth/background-pattern-dark.svg?url";
|
||||
import PlaneBackgroundPattern from "@/app/assets/auth/background-pattern.svg?url";
|
||||
import PlaneTakeOffImage from "@/app/assets/plane-takeoff.png?url";
|
||||
|
||||
export const InstanceNotReady: FC = () => {
|
||||
const { resolvedTheme } = useTheme();
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ import { MembersPropertyIcon } from "@plane/propel/icons";
|
|||
// types
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import type { IGithubRepoCollaborator, IGithubServiceImportFormData } from "@plane/types";
|
||||
// ui
|
||||
// assets
|
||||
import GithubLogo from "@/app/assets/services/github.png?url";
|
||||
// components
|
||||
import {
|
||||
GithubImportConfigure,
|
||||
|
|
@ -24,8 +25,6 @@ import {
|
|||
import { APP_INTEGRATIONS, IMPORTER_SERVICES_LIST, WORKSPACE_INTEGRATIONS } from "@/constants/fetch-keys";
|
||||
// hooks
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
// images
|
||||
import GithubLogo from "@/public/services/github.png";
|
||||
// services
|
||||
import { IntegrationService, GithubIntegrationService } from "@/services/integrations";
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,9 @@ import { useTranslation } from "@plane/i18n";
|
|||
// types
|
||||
import { Button } from "@plane/propel/button";
|
||||
import type { IImporterService } from "@plane/types";
|
||||
// ui
|
||||
// assets
|
||||
import GithubLogo from "@/app/assets/services/github.png?url";
|
||||
import JiraLogo from "@/app/assets/services/jira.svg?url";
|
||||
// components
|
||||
import { DeleteImportModal, GithubImporterRoot, JiraImporterRoot, SingleImport } from "@/components/integration";
|
||||
import { ImportExportSettingsLoader } from "@/components/ui/loader/settings/import-and-export";
|
||||
|
|
@ -22,9 +24,6 @@ import { ImportExportSettingsLoader } from "@/components/ui/loader/settings/impo
|
|||
import { IMPORTER_SERVICES_LIST } from "@/constants/fetch-keys";
|
||||
// hooks
|
||||
import { useUser } from "@/hooks/store/user";
|
||||
// assets
|
||||
import GithubLogo from "@/public/services/github.png";
|
||||
import JiraLogo from "@/public/services/jira.svg";
|
||||
// services
|
||||
import { IntegrationService } from "@/services/integrations";
|
||||
|
||||
|
|
|
|||
|
|
@ -12,13 +12,12 @@ import { Button } from "@plane/propel/button";
|
|||
import { MembersPropertyIcon } from "@plane/propel/icons";
|
||||
// types
|
||||
import type { IJiraImporterForm } from "@plane/types";
|
||||
// ui
|
||||
// assets
|
||||
import JiraLogo from "@/app/assets/services/jira.svg?url";
|
||||
// fetch keys
|
||||
import { IMPORTER_SERVICES_LIST } from "@/constants/fetch-keys";
|
||||
// hooks
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
// assets
|
||||
import JiraLogo from "@/public/services/jira.svg";
|
||||
// services
|
||||
import { JiraImporterService } from "@/services/integrations";
|
||||
// components
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ import { Tooltip } from "@plane/propel/tooltip";
|
|||
import type { IAppIntegration, IWorkspaceIntegration } from "@plane/types";
|
||||
// ui
|
||||
import { Loader } from "@plane/ui";
|
||||
// assets
|
||||
import GithubLogo from "@/app/assets/services/github.png?url";
|
||||
import SlackLogo from "@/app/assets/services/slack.png?url";
|
||||
// constants
|
||||
import { WORKSPACE_INTEGRATIONS } from "@/constants/fetch-keys";
|
||||
// hooks
|
||||
|
|
@ -21,9 +24,6 @@ import { useUserPermissions } from "@/hooks/store/user";
|
|||
import useIntegrationPopup from "@/hooks/use-integration-popup";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
// services
|
||||
// icons
|
||||
import GithubLogo from "@/public/services/github.png";
|
||||
import SlackLogo from "@/public/services/slack.png";
|
||||
import { IntegrationService } from "@/services/integrations";
|
||||
|
||||
type Props = {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import { useTranslation } from "@plane/i18n";
|
|||
import { TOAST_TYPE, setPromiseToast, setToast } from "@plane/propel/toast";
|
||||
import type { TIssue } from "@plane/types";
|
||||
import { EIssuesStoreType } from "@plane/types";
|
||||
// assets
|
||||
import emptyIssue from "@/app/assets/empty-state/issue.svg?url";
|
||||
// components
|
||||
import { EmptyState } from "@/components/common/empty-state";
|
||||
// hooks
|
||||
|
|
@ -18,8 +20,6 @@ import { useIssueDetail } from "@/hooks/store/use-issue-detail";
|
|||
import { useIssues } from "@/hooks/store/use-issues";
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
// images
|
||||
import emptyIssue from "@/public/empty-state/issue.svg";
|
||||
// local components
|
||||
import { IssuePeekOverview } from "../peek-overview";
|
||||
import { IssueMainContent } from "./main-content";
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { observer } from "mobx-react";
|
|||
import { EUserPermissions, EUserPermissionsLevel, WORK_ITEM_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
import { EmptyStateDetailed } from "@plane/propel/empty-state";
|
||||
import { EIssuesStoreType } from "@plane/types";
|
||||
// components
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
// hooks
|
||||
import { useCommandPalette } from "@/hooks/store/use-command-palette";
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import { GLOBAL_VIEW_TRACKER_ELEMENTS, ISSUE_DISPLAY_FILTERS_BY_PAGE } from "@pl
|
|||
import { EmptyStateDetailed } from "@plane/propel/empty-state";
|
||||
import type { EIssueLayoutTypes } from "@plane/types";
|
||||
import { EIssuesStoreType, STATIC_VIEW_TYPES } from "@plane/types";
|
||||
// assets
|
||||
import emptyView from "@/app/assets/empty-state/view.svg?url";
|
||||
// components
|
||||
import { IssuePeekOverview } from "@/components/issues/peek-overview";
|
||||
import { WorkspaceActiveLayout } from "@/components/views/helper";
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@
|
|||
import type { FC } from "react";
|
||||
import { MoveRight } from "lucide-react";
|
||||
import { Tooltip } from "@plane/propel/tooltip";
|
||||
// assets
|
||||
import emptyIssue from "@/app/assets/empty-state/issue.svg?url";
|
||||
// components
|
||||
import { EmptyState } from "@/components/common/empty-state";
|
||||
// hooks
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
// images
|
||||
import emptyIssue from "@/public/empty-state/issue.svg";
|
||||
|
||||
type TIssuePeekOverviewError = {
|
||||
removeRoutePeekId: () => void;
|
||||
|
|
|
|||
|
|
@ -12,15 +12,11 @@ import { EUserWorkspaceRoles } from "@plane/types";
|
|||
// components
|
||||
import { cn } from "@plane/utils";
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
// constants
|
||||
|
||||
// helpers
|
||||
// hooks
|
||||
import { useCommandPalette } from "@/hooks/store/use-command-palette";
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
import { useWorkspaceDraftIssues } from "@/hooks/store/workspace-draft";
|
||||
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
|
||||
import { useWorkspaceIssueProperties } from "@/hooks/use-workspace-issue-properties";
|
||||
// components
|
||||
import { DraftIssueBlock } from "./draft-issue-block";
|
||||
|
|
@ -45,7 +41,6 @@ export const WorkspaceDraftIssuesRoot: FC<TWorkspaceDraftIssuesRoot> = observer(
|
|||
[EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER],
|
||||
EUserPermissionsLevel.WORKSPACE
|
||||
);
|
||||
const noProjectResolvedPath = useResolvedAssetPath({ basePath: "/empty-state/draft/draft-issues-empty" });
|
||||
|
||||
//swr hook for fetching issue properties
|
||||
useWorkspaceIssueProperties(workspaceSlug);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import type { FC } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import Image from "next/image";
|
||||
// assets
|
||||
import AllFiltersImage from "@/app/assets/empty-state/module/all-filters.svg?url";
|
||||
import NameFilterImage from "@/app/assets/empty-state/module/name-filter.svg?url";
|
||||
// components
|
||||
import { ModuleListItem, ModulePeekOverview } from "@/components/modules";
|
||||
// ui
|
||||
|
|
@ -8,9 +11,6 @@ import { CycleModuleListLayoutLoader } from "@/components/ui/loader/cycle-module
|
|||
// hooks
|
||||
import { useModule } from "@/hooks/store/use-module";
|
||||
import { useModuleFilter } from "@/hooks/store/use-module-filter";
|
||||
// assets
|
||||
import AllFiltersImage from "@/public/empty-state/module/all-filters.svg";
|
||||
import NameFilterImage from "@/public/empty-state/module/name-filter.svg";
|
||||
|
||||
export interface IArchivedModulesView {
|
||||
workspaceSlug: string;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { useTranslation } from "@plane/i18n";
|
|||
import { EmptyStateDetailed } from "@plane/propel/empty-state";
|
||||
import { EUserProjectRoles } from "@plane/types";
|
||||
import { ContentWrapper, Row, ERowVariant } from "@plane/ui";
|
||||
// components
|
||||
import { ListLayout } from "@/components/core/list";
|
||||
import { ModuleCardItem, ModuleListItem, ModulePeekOverview, ModulesListGanttChartView } from "@/components/modules";
|
||||
import { CycleModuleBoardLayoutLoader } from "@/components/ui/loader/cycle-module-board-loader";
|
||||
|
|
|
|||
|
|
@ -2,23 +2,22 @@
|
|||
|
||||
import { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import type { StaticImageData } from "next/image";
|
||||
import Image from "next/image";
|
||||
// plane imports
|
||||
import { PRODUCT_TOUR_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
import { Button } from "@plane/propel/button";
|
||||
import { CloseIcon, PlaneLockup } from "@plane/propel/icons";
|
||||
// assets
|
||||
import CyclesTour from "@/app/assets/onboarding/cycles.webp?url";
|
||||
import IssuesTour from "@/app/assets/onboarding/issues.webp?url";
|
||||
import ModulesTour from "@/app/assets/onboarding/modules.webp?url";
|
||||
import PagesTour from "@/app/assets/onboarding/pages.webp?url";
|
||||
import ViewsTour from "@/app/assets/onboarding/views.webp?url";
|
||||
// helpers
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
// hooks
|
||||
import { useCommandPalette } from "@/hooks/store/use-command-palette";
|
||||
import { useUser } from "@/hooks/store/user";
|
||||
// assets
|
||||
import CyclesTour from "@/public/onboarding/cycles.webp";
|
||||
import IssuesTour from "@/public/onboarding/issues.webp";
|
||||
import ModulesTour from "@/public/onboarding/modules.webp";
|
||||
import PagesTour from "@/public/onboarding/pages.webp";
|
||||
import ViewsTour from "@/public/onboarding/views.webp";
|
||||
// local imports
|
||||
import { TourSidebar } from "./sidebar";
|
||||
|
||||
|
|
@ -32,7 +31,7 @@ const TOUR_STEPS: {
|
|||
key: TTourSteps;
|
||||
title: string;
|
||||
description: string;
|
||||
image: StaticImageData;
|
||||
image: any;
|
||||
prevStep?: TTourSteps;
|
||||
nextStep?: TTourSteps;
|
||||
}[] = [
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
"use client";
|
||||
|
||||
import { WORKSPACE_SETTINGS_ICONS } from "app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/sidebar";
|
||||
import { observer } from "mobx-react";
|
||||
// plane types
|
||||
import { EUserPermissionsLevel, WORKSPACE_SETTINGS } from "@plane/constants";
|
||||
|
|
@ -11,6 +10,7 @@ import { PowerKSettingsMenu } from "@/components/power-k/menus/settings";
|
|||
// hooks
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
import { shouldRenderSettingLink } from "@/plane-web/helpers/workspace.helper";
|
||||
import { WORKSPACE_SETTINGS_ICONS } from "app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/sidebar";
|
||||
|
||||
type Props = {
|
||||
context: TPowerKContext;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import { useTranslation } from "@plane/i18n";
|
|||
import { EmptyStateCompact } from "@plane/propel/empty-state";
|
||||
import { Loader, Card } from "@plane/ui";
|
||||
import { calculateTimeAgo, getFileURL } from "@plane/utils";
|
||||
// assets
|
||||
import recentActivityEmptyState from "@/app/assets/empty-state/recent_activity.svg?url";
|
||||
// components
|
||||
import { ActivityMessage, IssueLink } from "@/components/core/activity";
|
||||
import { ProfileEmptyState } from "@/components/ui/profile-empty-state";
|
||||
|
|
@ -16,8 +18,6 @@ import { USER_PROFILE_ACTIVITY } from "@/constants/fetch-keys";
|
|||
// helpers
|
||||
// hooks
|
||||
import { useUser } from "@/hooks/store/user";
|
||||
// assets
|
||||
import recentActivityEmptyState from "@/public/empty-state/recent_activity.svg";
|
||||
// services
|
||||
import { UserService } from "@/services/user.service";
|
||||
|
||||
|
|
|
|||
|
|
@ -6,15 +6,15 @@ import { useParams } from "next/navigation";
|
|||
import useSWR, { mutate } from "swr";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import type { IWorkspaceIntegration } from "@plane/types";
|
||||
// assets
|
||||
import GithubLogo from "@/app/assets/logos/github-square.png?url";
|
||||
import SlackLogo from "@/app/assets/services/slack.png?url";
|
||||
// components
|
||||
import { SelectRepository, SelectChannel } from "@/components/integration";
|
||||
// constants
|
||||
import { PROJECT_GITHUB_REPOSITORY } from "@/constants/fetch-keys";
|
||||
// icons
|
||||
import GithubLogo from "@/public/logos/github-square.png";
|
||||
import SlackLogo from "@/public/services/slack.png";
|
||||
// services
|
||||
import { ProjectService } from "@/services/project";
|
||||
// types
|
||||
|
||||
type Props = {
|
||||
integration: IWorkspaceIntegration;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { xor } from "lodash-es";
|
||||
import { observer } from "mobx-react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { Search } from "lucide-react";
|
||||
import { Combobox } from "@headlessui/react";
|
||||
// plane ui
|
||||
|
|
@ -8,14 +9,15 @@ import { useTranslation } from "@plane/i18n";
|
|||
import { Button } from "@plane/propel/button";
|
||||
import { CloseIcon } from "@plane/propel/icons";
|
||||
import { Checkbox, EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
|
||||
// components
|
||||
import { cn } from "@plane/utils";
|
||||
// assets
|
||||
import darkProjectAsset from "@/app/assets/empty-state/search/project-dark.webp?url";
|
||||
import lightProjectAsset from "@/app/assets/empty-state/search/project-light.webp?url";
|
||||
// components
|
||||
import { Logo } from "@/components/common/logo";
|
||||
import { SimpleEmptyState } from "@/components/empty-state/simple-empty-state-root";
|
||||
// helpers
|
||||
// hooks
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
|
|
@ -33,6 +35,8 @@ export const ProjectMultiSelectModal: React.FC<Props> = observer((props) => {
|
|||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
// refs
|
||||
const moveButtonRef = useRef<HTMLButtonElement>(null);
|
||||
// theme hook
|
||||
const { resolvedTheme } = useTheme();
|
||||
// plane hooks
|
||||
const { t } = useTranslation();
|
||||
// store hooks
|
||||
|
|
@ -48,9 +52,7 @@ export const ProjectMultiSelectModal: React.FC<Props> = observer((props) => {
|
|||
const projectQuery = `${project?.identifier} ${project?.name}`.toLowerCase();
|
||||
return projectQuery.includes(searchTerm.toLowerCase());
|
||||
});
|
||||
const filteredProjectResolvedPath = useResolvedAssetPath({
|
||||
basePath: "/empty-state/search/project",
|
||||
});
|
||||
const filteredProjectResolvedPath = resolvedTheme === "light" ? lightProjectAsset : darkProjectAsset;
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) setSelectedProjectIds(selectedProjectIdsProp);
|
||||
|
|
|
|||
|
|
@ -71,9 +71,7 @@ export const ProjectRoot = observer(() => {
|
|||
}, [clearAllFilters, clearAllAppliedDisplayFilters, workspaceSlug]);
|
||||
|
||||
useEffect(() => {
|
||||
isArchived
|
||||
? updateDisplayFilters(workspaceSlug.toString(), { archived_projects: true })
|
||||
: updateDisplayFilters(workspaceSlug.toString(), { archived_projects: false });
|
||||
updateDisplayFilters(workspaceSlug.toString(), { archived_projects: isArchived });
|
||||
}, [pathname]);
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { ChevronRight } from "lucide-react";
|
|||
import { PROJECT_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
import { EPillVariant, Pill, EPillSize } from "@plane/propel/pill";
|
||||
import { ToggleSwitch } from "@plane/ui";
|
||||
import { joinUrlPath } from "@plane/utils";
|
||||
import type { TProperties } from "@/plane-web/constants/project/settings/features";
|
||||
|
||||
type Props = {
|
||||
|
|
@ -17,7 +18,7 @@ type Props = {
|
|||
export const ProjectFeatureToggle = (props: Props) => {
|
||||
const { workspaceSlug, projectId, featureItem, value, handleSubmit, disabled } = props;
|
||||
return featureItem.href ? (
|
||||
<Link href={`/${workspaceSlug}/settings/projects/${projectId}/features/${featureItem.href}`}>
|
||||
<Link href={joinUrlPath(workspaceSlug, "settings", "projects", projectId, "features", featureItem.href)}>
|
||||
<div className="flex items-center gap-2">
|
||||
<Pill
|
||||
variant={value ? EPillVariant.PRIMARY : EPillVariant.DEFAULT}
|
||||
|
|
|
|||
|
|
@ -6,19 +6,24 @@ import type {
|
|||
import type { ElementDragPayload } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
|
||||
import { observer } from "mobx-react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useTheme } from "next-themes";
|
||||
import Masonry from "react-masonry-component";
|
||||
import { Plus } from "lucide-react";
|
||||
// plane imports
|
||||
import { EUserPermissionsLevel } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { EUserWorkspaceRoles } from "@plane/types";
|
||||
// assets
|
||||
import darkStickiesAsset from "@/app/assets/empty-state/stickies/stickies-dark.webp?url";
|
||||
import lightStickiesAsset from "@/app/assets/empty-state/stickies/stickies-light.webp?url";
|
||||
import darkStickiesSearchAsset from "@/app/assets/empty-state/stickies/stickies-search-dark.webp?url";
|
||||
import lightStickiesSearchAsset from "@/app/assets/empty-state/stickies/stickies-search-light.webp?url";
|
||||
// components
|
||||
import { DetailedEmptyState } from "@/components/empty-state/detailed-empty-state-root";
|
||||
import { SimpleEmptyState } from "@/components/empty-state/simple-empty-state-root";
|
||||
import { StickiesEmptyState } from "@/components/home/widgets/empty-states/stickies";
|
||||
// hooks
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
|
||||
import { useSticky } from "@/hooks/use-stickies";
|
||||
// local imports
|
||||
import { useStickyOperations } from "../sticky/use-operations";
|
||||
|
|
@ -39,6 +44,8 @@ export const StickiesList = observer((props: TProps) => {
|
|||
const { workspaceSlug, intersectionElement, columnCount } = props;
|
||||
// navigation
|
||||
const pathname = usePathname();
|
||||
// theme hook
|
||||
const { resolvedTheme } = useTheme();
|
||||
// plane hooks
|
||||
const { t } = useTranslation();
|
||||
// store hooks
|
||||
|
|
@ -55,10 +62,8 @@ export const StickiesList = observer((props: TProps) => {
|
|||
[EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER, EUserWorkspaceRoles.GUEST],
|
||||
EUserPermissionsLevel.WORKSPACE
|
||||
);
|
||||
const stickiesResolvedPath = useResolvedAssetPath({ basePath: "/empty-state/stickies/stickies" });
|
||||
const stickiesSearchResolvedPath = useResolvedAssetPath({
|
||||
basePath: "/empty-state/stickies/stickies-search",
|
||||
});
|
||||
const stickiesResolvedPath = resolvedTheme === "light" ? lightStickiesAsset : darkStickiesAsset;
|
||||
const stickiesSearchResolvedPath = resolvedTheme === "light" ? lightStickiesSearchAsset : darkStickiesSearchAsset;
|
||||
const masonryRef = useRef<any>(null);
|
||||
|
||||
const handleLayout = () => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { useCallback, useEffect, useRef } from "react";
|
||||
// import dynamic from "next/dynamic";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
// plane imports
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import Image from "next/image";
|
|||
// ui
|
||||
import { Button } from "@plane/propel/button";
|
||||
// assets
|
||||
import EmptyWebhook from "@/public/empty-state/web-hook.svg";
|
||||
import EmptyWebhook from "@/app/assets/empty-state/web-hook.svg?url";
|
||||
|
||||
type Props = {
|
||||
onClick: () => void;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import { LogoSpinner } from "@/components/common/logo-spinner";
|
|||
import { useWorkspaceNotifications } from "@/hooks/store/notifications";
|
||||
import { useWorkspace } from "@/hooks/store/use-workspace";
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
|
||||
import { useWorkspaceIssueProperties } from "@/hooks/use-workspace-issue-properties";
|
||||
// plane web imports
|
||||
import { useNotificationPreview } from "@/plane-web/hooks/use-notification-preview";
|
||||
|
|
@ -42,7 +41,6 @@ export const NotificationsRoot = observer(({ workspaceSlug }: NotificationsRootP
|
|||
// derived values
|
||||
const { workspace_slug, project_id, issue_id, is_inbox_issue } =
|
||||
notificationLiteByNotificationId(currentSelectedNotificationId);
|
||||
const resolvedPath = useResolvedAssetPath({ basePath: "/empty-state/intake/issue-detail" });
|
||||
|
||||
// fetching workspace work item properties
|
||||
useWorkspaceIssueProperties(workspaceSlug);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// router from n-progress-bar
|
||||
import { useRouter } from "@/lib/b-progress";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
export const useAppRouter = () => useRouter();
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
import { useTheme } from "next-themes";
|
||||
|
||||
type AssetPathConfig = {
|
||||
basePath: string;
|
||||
additionalPath?: string;
|
||||
extension?: string;
|
||||
includeThemeInPath?: boolean;
|
||||
};
|
||||
|
||||
export const useResolvedAssetPath = ({
|
||||
basePath,
|
||||
additionalPath = "",
|
||||
extension = "webp",
|
||||
includeThemeInPath = true,
|
||||
}: AssetPathConfig) => {
|
||||
// hooks
|
||||
const { resolvedTheme } = useTheme();
|
||||
// resolved theme
|
||||
const theme = resolvedTheme === "light" ? "light" : "dark";
|
||||
|
||||
if (!includeThemeInPath) {
|
||||
return `${additionalPath && additionalPath !== "" ? `${basePath}${additionalPath}` : basePath}.${extension}`;
|
||||
}
|
||||
|
||||
return `${additionalPath && additionalPath !== "" ? `${basePath}${additionalPath}` : basePath}-${theme}.${extension}`;
|
||||
};
|
||||
|
|
@ -32,7 +32,7 @@ import { persistence } from "@/local-db/storage.sqlite";
|
|||
|
||||
interface IProjectAuthWrapper {
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
projectId?: string;
|
||||
children: ReactNode;
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@ import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
|||
import { Tooltip } from "@plane/propel/tooltip";
|
||||
// components
|
||||
import { cn } from "@plane/utils";
|
||||
// assets
|
||||
import PlaneBlackLogo from "@/app/assets/plane-logos/black-horizontal-with-blue-logo.png?url";
|
||||
import PlaneWhiteLogo from "@/app/assets/plane-logos/white-horizontal-with-blue-logo.png?url";
|
||||
import WorkSpaceNotAvailable from "@/app/assets/workspace/workspace-not-available.png?url";
|
||||
// components
|
||||
import { LogoSpinner } from "@/components/common/logo-spinner";
|
||||
// hooks
|
||||
import { useFavorite } from "@/hooks/store/use-favorite";
|
||||
|
|
@ -28,10 +33,6 @@ import { useUser, useUserPermissions } from "@/hooks/store/user";
|
|||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
// local
|
||||
import { persistence } from "@/local-db/storage.sqlite";
|
||||
// images
|
||||
import PlaneBlackLogo from "@/public/plane-logos/black-horizontal-with-blue-logo.png";
|
||||
import PlaneWhiteLogo from "@/public/plane-logos/white-horizontal-with-blue-logo.png";
|
||||
import WorkSpaceNotAvailable from "@/public/workspace/workspace-not-available.png";
|
||||
|
||||
interface IWorkspaceAuthWrapper {
|
||||
children: ReactNode;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,140 @@
|
|||
import { useRouter as useBProgressRouter } from "@bprogress/next";
|
||||
"use client";
|
||||
|
||||
export function useRouter() {
|
||||
const router = useBProgressRouter();
|
||||
return router;
|
||||
import { useEffect, useRef } from "react";
|
||||
import { BProgress } from "@bprogress/core";
|
||||
import { useNavigation } from "react-router";
|
||||
import "@bprogress/core/css";
|
||||
|
||||
/**
|
||||
* Progress bar configuration options
|
||||
*/
|
||||
interface ProgressConfig {
|
||||
/** Whether to show the loading spinner */
|
||||
showSpinner: boolean;
|
||||
/** Minimum progress percentage (0-1) */
|
||||
minimum: number;
|
||||
/** Animation speed in milliseconds */
|
||||
speed: number;
|
||||
/** Auto-increment speed in milliseconds */
|
||||
trickleSpeed: number;
|
||||
/** CSS easing function */
|
||||
easing: string;
|
||||
/** Enable auto-increment */
|
||||
trickle: boolean;
|
||||
/** Delay before showing progress bar in milliseconds */
|
||||
delay: number;
|
||||
/** Whether to disable the progress bar */
|
||||
isDisabled?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration for the progress bar
|
||||
*/
|
||||
const PROGRESS_CONFIG: Readonly<ProgressConfig> = {
|
||||
showSpinner: false,
|
||||
minimum: 0.1,
|
||||
speed: 400,
|
||||
trickleSpeed: 800,
|
||||
easing: "ease",
|
||||
trickle: true,
|
||||
delay: 0,
|
||||
isDisabled: import.meta.env.PROD, // Disable progress bar in production builds
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Navigation Progress Bar Component
|
||||
*
|
||||
* Automatically displays a progress bar at the top of the page during React Router navigation.
|
||||
* Integrates with React Router's useNavigation hook to monitor route changes.
|
||||
*
|
||||
* Note: Progress bar is disabled in production builds.
|
||||
*
|
||||
* @returns null - This component doesn't render any visible elements
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* function App() {
|
||||
* return (
|
||||
* <>
|
||||
* <AppProgressBar />
|
||||
* <Outlet />
|
||||
* </>
|
||||
* );
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function AppProgressBar(): null {
|
||||
const navigation = useNavigation();
|
||||
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const startedRef = useRef<boolean>(false);
|
||||
|
||||
// Initialize BProgress once on mount
|
||||
useEffect(() => {
|
||||
// Skip initialization in production builds
|
||||
if (PROGRESS_CONFIG.isDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Configure BProgress with our settings
|
||||
BProgress.configure({
|
||||
showSpinner: PROGRESS_CONFIG.showSpinner,
|
||||
minimum: PROGRESS_CONFIG.minimum,
|
||||
speed: PROGRESS_CONFIG.speed,
|
||||
trickleSpeed: PROGRESS_CONFIG.trickleSpeed,
|
||||
easing: PROGRESS_CONFIG.easing,
|
||||
trickle: PROGRESS_CONFIG.trickle,
|
||||
});
|
||||
|
||||
// Render the progress bar element in the DOM
|
||||
BProgress.render(true);
|
||||
|
||||
// Cleanup on unmount
|
||||
return () => {
|
||||
if (BProgress.isStarted()) {
|
||||
BProgress.done();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Handle navigation state changes
|
||||
useEffect(() => {
|
||||
// Skip navigation tracking in production builds
|
||||
if (PROGRESS_CONFIG.isDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (navigation.state === "idle") {
|
||||
// Navigation complete - clear any pending timer
|
||||
if (timerRef.current !== null) {
|
||||
clearTimeout(timerRef.current);
|
||||
timerRef.current = null;
|
||||
}
|
||||
|
||||
// Complete progress if it was started
|
||||
if (startedRef.current) {
|
||||
BProgress.done();
|
||||
startedRef.current = false;
|
||||
}
|
||||
} else {
|
||||
// Navigation in progress (loading or submitting)
|
||||
// Only start if not already started and no timer pending
|
||||
if (timerRef.current === null && !startedRef.current) {
|
||||
timerRef.current = setTimeout((): void => {
|
||||
if (!BProgress.isStarted()) {
|
||||
BProgress.start();
|
||||
startedRef.current = true;
|
||||
}
|
||||
timerRef.current = null;
|
||||
}, PROGRESS_CONFIG.delay);
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (timerRef.current !== null) {
|
||||
clearTimeout(timerRef.current);
|
||||
}
|
||||
};
|
||||
}, [navigation.state]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
"use client";
|
||||
|
||||
import type { FC, ReactNode } from "react";
|
||||
import { useEffect } from "react";
|
||||
import { lazy, Suspense, useEffect } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useParams } from "next/navigation";
|
||||
import posthog from "posthog-js";
|
||||
import { PostHogProvider as PHProvider } from "posthog-js/react";
|
||||
|
|
@ -17,7 +16,7 @@ import { useInstance } from "@/hooks/store/use-instance";
|
|||
import { useWorkspace } from "@/hooks/store/use-workspace";
|
||||
import { useUser, useUserPermissions } from "@/hooks/store/user";
|
||||
// dynamic imports
|
||||
const PostHogPageView = dynamic(() => import("@/lib/posthog-view"), { ssr: false });
|
||||
const PostHogPageView = lazy(() => import("@/lib/posthog-view"));
|
||||
|
||||
export interface IPosthogWrapper {
|
||||
children: ReactNode;
|
||||
|
|
@ -99,7 +98,9 @@ const PostHogProvider: FC<IPosthogWrapper> = observer((props) => {
|
|||
if (is_posthog_enabled)
|
||||
return (
|
||||
<PHProvider client={posthog}>
|
||||
<PostHogPageView />
|
||||
<Suspense>
|
||||
<PostHogPageView />
|
||||
</Suspense>
|
||||
{children}
|
||||
</PHProvider>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -32,7 +32,10 @@ export interface IBaseUserPermissionStore {
|
|||
workspaceInfoBySlug: (workspaceSlug: string) => IWorkspaceMemberMe | undefined;
|
||||
getWorkspaceRoleByWorkspaceSlug: (workspaceSlug: string) => TUserPermissions | EUserWorkspaceRoles | undefined;
|
||||
getProjectRolesByWorkspaceSlug: (workspaceSlug: string) => IUserProjectsRole;
|
||||
getProjectRoleByWorkspaceSlugAndProjectId: (workspaceSlug: string, projectId: string) => EUserPermissions | undefined;
|
||||
getProjectRoleByWorkspaceSlugAndProjectId: (
|
||||
workspaceSlug: string,
|
||||
projectId?: string
|
||||
) => EUserPermissions | undefined;
|
||||
allowPermissions: (
|
||||
allowPermissions: ETempUserRole[],
|
||||
level: TUserPermissionsLevel,
|
||||
|
|
@ -142,7 +145,7 @@ export abstract class BaseUserPermissionStore implements IBaseUserPermissionStor
|
|||
*/
|
||||
abstract getProjectRoleByWorkspaceSlugAndProjectId: (
|
||||
workspaceSlug: string,
|
||||
projectId: string
|
||||
projectId?: string
|
||||
) => EUserPermissions | undefined;
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue