Compare commits

..
Author SHA1 Message Date
NarayanBavisetti c7ed825226 chore: added product tour fields 2025-12-02 20:33:07 +05:30
22 changed files with 175 additions and 272 deletions
-1
View File
@@ -111,4 +111,3 @@ build/
.react-router/
AGENTS.md
temp/
scripts/
+1 -1
View File
@@ -48,7 +48,6 @@
"uuid": "catalog:"
},
"devDependencies": {
"@dotenvx/dotenvx": "catalog:",
"@plane/eslint-config": "workspace:*",
"@plane/tailwind-config": "workspace:*",
"@plane/typescript-config": "workspace:*",
@@ -58,6 +57,7 @@
"@types/node": "catalog:",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"dotenv": "^16.4.5",
"typescript": "catalog:",
"vite": "catalog:",
"vite-tsconfig-paths": "^5.1.4"
+1 -1
View File
@@ -1,6 +1,6 @@
import path from "node:path";
import * as dotenv from "@dotenvx/dotenvx";
import { reactRouter } from "@react-router/dev/vite";
import dotenv from "dotenv";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
import { joinUrlPath } from "@plane/utils";
@@ -0,0 +1,42 @@
# Generated by Django 4.2.25 on 2025-12-02 14:16
from django.db import migrations, models
import plane.db.models.workspace
def get_default_feature_tours():
return {
"work_items": True,
"cycles": True,
"modules": True,
"intake": True,
"pages": True,
}
def populate_feature_tours(apps, _schema_editor):
WorkspaceUserProperties = apps.get_model('db', 'WorkspaceUserProperties')
default_value = get_default_feature_tours()
# Use bulk update for better performance
WorkspaceUserProperties.objects.all().update(feature_tours=default_value)
class Migration(migrations.Migration):
dependencies = [
('db', '0112_auto_20251124_0603'),
]
operations = [
migrations.AddField(
model_name='profile',
name='is_navigation_tour_completed',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='workspaceuserproperties',
name='feature_tours',
field=models.JSONField(default=plane.db.models.workspace.get_default_feature_tours),
),
migrations.RunPython(populate_feature_tours, reverse_code=migrations.RunPython.noop),
]
+3
View File
@@ -214,6 +214,9 @@ class Profile(TimeAuditModel):
goals = models.JSONField(default=dict)
background_color = models.CharField(max_length=255, default=get_random_color)
# navigation tour
is_navigation_tour_completed = models.BooleanField(default=False)
# marketing
has_marketing_email_consent = models.BooleanField(default=False)
+11
View File
@@ -112,6 +112,16 @@ def slug_validator(value):
raise ValidationError("Slug is not valid")
def get_default_feature_tours():
return {
"work_items": False,
"cycles": False,
"modules": False,
"intake": False,
"pages": False,
}
class Workspace(BaseModel):
TIMEZONE_CHOICES = tuple(zip(pytz.common_timezones, pytz.common_timezones))
@@ -325,6 +335,7 @@ class WorkspaceUserProperties(BaseModel):
choices=NavigationControlPreference.choices,
default=NavigationControlPreference.ACCORDION,
)
feature_tours = models.JSONField(default=get_default_feature_tours)
class Meta:
unique_together = ["workspace", "user", "deleted_at"]
+3 -2
View File
@@ -24,7 +24,7 @@
},
"author": "Plane Software Inc.",
"dependencies": {
"@dotenvx/dotenvx": "catalog:",
"@dotenvx/dotenvx": "^1.49.0",
"@hocuspocus/extension-database": "2.15.2",
"@hocuspocus/extension-logger": "2.15.2",
"@hocuspocus/extension-redis": "2.15.2",
@@ -41,7 +41,8 @@
"axios": "catalog:",
"compression": "1.8.1",
"cors": "^2.8.5",
"express": "catalog:",
"dotenv": "^16.4.5",
"express": "^4.21.2",
"express-ws": "^5.0.2",
"helmet": "^7.1.0",
"ioredis": "5.7.0",
+1 -1
View File
@@ -52,7 +52,6 @@
"uuid": "catalog:"
},
"devDependencies": {
"@dotenvx/dotenvx": "catalog:",
"@plane/eslint-config": "workspace:*",
"@plane/tailwind-config": "workspace:*",
"@plane/typescript-config": "workspace:*",
@@ -62,6 +61,7 @@
"@types/node": "catalog:",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"dotenv": "^16.4.5",
"typescript": "catalog:",
"vite": "catalog:",
"vite-tsconfig-paths": "^5.1.4"
+1 -1
View File
@@ -1,6 +1,6 @@
import path from "node:path";
import * as dotenv from "@dotenvx/dotenvx";
import { reactRouter } from "@react-router/dev/vite";
import dotenv from "dotenv";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
import { joinUrlPath } from "@plane/utils";
@@ -1,41 +1,17 @@
// components
import { observer } from "mobx-react";
import { useParams, usePathname } from "next/navigation";
import { cn } from "@plane/utils";
import { TopNavPowerK } from "@/components/navigation";
import { HelpMenuRoot } from "@/components/workspace/sidebar/help-section/root";
import { UserMenuRoot } from "@/components/workspace/sidebar/user-menu-root";
import { WorkspaceMenuRoot } from "@/components/workspace/sidebar/workspace-menu-root";
import { useAppRailPreferences } from "@/hooks/use-navigation-preferences";
import { Tooltip } from "@plane/propel/tooltip";
import { AppSidebarItem } from "@/components/sidebar/sidebar-item";
import { InboxIcon } from "@plane/propel/icons";
import useSWR from "swr";
import { useWorkspaceNotifications } from "@/hooks/store/notifications";
export const TopNavigationRoot = observer(() => {
// router
const { workspaceSlug, projectId, workItem } = useParams();
const pathname = usePathname();
// store hooks
const { unreadNotificationsCount, getUnreadNotificationsCount } = useWorkspaceNotifications();
const { preferences } = useAppRailPreferences();
const showLabel = preferences.displayMode === "icon_with_label";
// Fetch notification count
useSWR(
workspaceSlug ? "WORKSPACE_UNREAD_NOTIFICATION_COUNT" : null,
workspaceSlug ? () => getUnreadNotificationsCount(workspaceSlug.toString()) : null
);
// Calculate notification count
const isMentionsEnabled = unreadNotificationsCount.mention_unread_notifications_count > 0;
const totalNotifications = isMentionsEnabled
? unreadNotificationsCount.mention_unread_notifications_count
: unreadNotificationsCount.total_unread_notifications_count;
return (
<div
className={cn("flex items-center min-h-11 w-full px-3.5 z-[27] transition-all duration-300", {
@@ -52,23 +28,6 @@ export const TopNavigationRoot = observer(() => {
</div>
{/* Additional Actions */}
<div className="shrink-0 flex-1 flex gap-1 items-center justify-end">
<Tooltip tooltipContent="Inbox" position="bottom">
<AppSidebarItem
variant="link"
item={{
href: `/${workspaceSlug?.toString()}/notifications/`,
icon: (
<div className="relative">
<InboxIcon className="size-5" />
{totalNotifications > 0 && (
<span className="absolute -top-0 -right-0 size-2 rounded-full bg-red-500" />
)}
</div>
),
isActive: pathname?.includes("/notifications/"),
}}
/>
</Tooltip>
<HelpMenuRoot />
<div className="flex items-center justify-center size-8 hover:bg-custom-background-80 rounded-md">
<UserMenuRoot size="xs" />
@@ -44,7 +44,7 @@ export const ProjectActionsMenu: FC<Props> = ({
customButton={
<span
ref={actionSectionRef}
className="grid place-items-center p-0.5 text-custom-sidebar-text-400 hover:bg-custom-sidebar-background-90 rounded"
className="grid place-items-center p-0.5 text-custom-sidebar-text-400 hover:bg-custom-sidebar-background-80 rounded"
onClick={() => setIsMenuActive(!isMenuActive)}
>
<MoreHorizontal className="size-4" />
@@ -1,30 +0,0 @@
import type { TPartialProject } from "@/plane-web/types";
// plane propel imports
import { Logo } from "@plane/propel/emoji-icon-picker";
import { ChevronDownIcon } from "@plane/propel/icons";
import { Tooltip } from "@plane/propel/tooltip";
type TProjectHeaderButtonProps = {
project: TPartialProject;
};
export function ProjectHeaderButton({ project }: TProjectHeaderButtonProps) {
return (
<Tooltip tooltipContent={project.name} position="bottom">
<div className="relative flex items-center text-left select-none w-full max-w-48 pr-1">
<div className="size-7 rounded-md bg-custom-background-90 flex items-center justify-center flex-shrink-0">
<Logo logo={project.logo_props} size={16} />
</div>
<div className="relative flex-1 min-w-0">
<p className="truncate text-base font-medium text-custom-sidebar-text-200 px-2">{project.name}</p>
<div className="absolute right-0 top-0 bottom-0 flex items-center justify-end pointer-events-none opacity-0 group-hover:opacity-100 transition-opacity duration-200">
<div className="relative h-full w-8 flex items-center justify-end">
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-custom-background-90 to-custom-background-90 rounded-r" />
<ChevronDownIcon className="relative z-10 size-4 text-custom-text-300" />
</div>
</div>
</div>
</div>
</Tooltip>
);
}
@@ -1,107 +1,20 @@
import { useCallback, useMemo } from "react";
import { observer } from "mobx-react";
// plane ui imports
import type { ICustomSearchSelectOption } from "@plane/types";
import { CustomSearchSelect } from "@plane/ui";
// plane propel imports
import { ProjectIcon } from "@plane/propel/icons";
// hooks
import { useAppRouter } from "@/hooks/use-app-router";
import { useProject } from "@/hooks/store/use-project";
import { useUserPermissions } from "@/hooks/store/user";
import { useNavigationItems } from "@/plane-web/components/navigations";
// local components
import { SwitcherLabel } from "../common/switcher-label";
import { ProjectHeaderButton } from "./project-header-button";
// utils
import { getTabUrl } from "./tab-navigation-utils";
import { useTabPreferences } from "./use-tab-preferences";
import type { FC } from "react";
// plane imports
import { Logo } from "@plane/propel/emoji-icon-picker";
import type { TLogoProps } from "@plane/types";
type TProjectHeaderProps = {
workspaceSlug: string;
projectId: string;
type ProjectHeaderProps = {
project: {
name: string;
logo_props: TLogoProps;
};
};
export const ProjectHeader = observer((props: TProjectHeaderProps) => {
const { workspaceSlug, projectId } = props;
// router
const router = useAppRouter();
// store hooks
const { joinedProjectIds, getPartialProjectById } = useProject();
const { allowPermissions } = useUserPermissions();
// Get current project details
const currentProjectDetails = getPartialProjectById(projectId);
// Get available navigation items for this project
const navigationItems = useNavigationItems({
workspaceSlug: workspaceSlug,
projectId,
project: currentProjectDetails,
allowPermissions,
});
// Get preferences from hook
const { tabPreferences } = useTabPreferences(workspaceSlug, projectId);
// Memoize available tab keys
const availableTabKeys = useMemo(() => navigationItems.map((item) => item.key), [navigationItems]);
// Memoize validated default tab key
const validatedDefaultTabKey = useMemo(
() =>
availableTabKeys.includes(tabPreferences.defaultTab)
? tabPreferences.defaultTab
: availableTabKeys[0] || "work_items",
[availableTabKeys, tabPreferences.defaultTab]
);
// Memoize switcher options to prevent recalculation on every render
const switcherOptions = useMemo<ICustomSearchSelectOption[]>(
() =>
joinedProjectIds
.map((id): ICustomSearchSelectOption | null => {
const project = getPartialProjectById(id);
if (!project) return null;
return {
value: id,
query: project.name,
content: (
<SwitcherLabel
name={project.name}
logo_props={project.logo_props}
LabelIcon={ProjectIcon}
type="material"
/>
),
};
})
.filter((option): option is ICustomSearchSelectOption => option !== null),
[joinedProjectIds, getPartialProjectById]
);
// Memoize onChange handler
const handleProjectChange = useCallback(
(value: string) => {
if (value !== currentProjectDetails?.id) {
router.push(getTabUrl(workspaceSlug, value, validatedDefaultTabKey));
}
},
[currentProjectDetails?.id, router, workspaceSlug, validatedDefaultTabKey]
);
// Early return if no project details
if (!currentProjectDetails) return null;
return (
<CustomSearchSelect
options={switcherOptions}
value={currentProjectDetails.id}
onChange={handleProjectChange}
customButton={currentProjectDetails ? <ProjectHeaderButton project={currentProjectDetails} /> : null}
className="h-full rounded"
customButtonClassName="group flex items-center gap-0.5 rounded hover:bg-custom-background-90 outline-none cursor-pointer h-full"
/>
);
});
export const ProjectHeader: FC<ProjectHeaderProps> = ({ project }) => (
<div className="flex items-center gap-1.5 text-left select-none w-full">
<div className="size-7 rounded-md bg-custom-background-90 flex items-center justify-center flex-shrink-0">
<Logo logo={project.logo_props} size={16} />
</div>
<p className="truncate text-base font-medium text-custom-sidebar-text-200 flex-shrink-0">{project.name}</p>
</div>
);
@@ -115,7 +115,7 @@ export const TabNavigationRoot: FC<TTabNavigationRootProps> = observer((props) =
const hiddenNavigationItems = allNavigationItems.filter((item) => tabPreferences.hiddenTabs.includes(item.key));
// Responsive tab layout hook
const { visibleItems, overflowItems, hasOverflow, itemRefs, containerRef } = useResponsiveTabLayout({
const { visibleItems, overflowItems, hasOverflow, containerRef, itemRefs } = useResponsiveTabLayout({
visibleNavigationItems,
hiddenNavigationItems,
isActive,
@@ -169,7 +169,7 @@ export const TabNavigationRoot: FC<TTabNavigationRootProps> = observer((props) =
{/* container for the tab navigation */}
<div className="flex items-center gap-3 overflow-hidden size-full">
<div className="flex items-center gap-2 shrink-0">
<ProjectHeader workspaceSlug={workspaceSlug} projectId={projectId} />
<ProjectHeader project={project} />
<div className="shrink-0">
<ProjectActionsMenu
workspaceSlug={workspaceSlug}
@@ -1,12 +1,12 @@
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import type { TNavigationItem } from "./tab-navigation-root";
export type TResponsiveTabLayout = {
visibleItems: TNavigationItem[];
overflowItems: TNavigationItem[];
hasOverflow: boolean;
containerRef: React.RefObject<HTMLDivElement>;
itemRefs: React.MutableRefObject<(HTMLDivElement | null)[]>;
containerRef: (node: HTMLDivElement | null) => void;
};
type UseResponsiveTabLayoutProps = {
@@ -30,9 +30,9 @@ export const useResponsiveTabLayout = ({
hiddenNavigationItems,
isActive,
}: UseResponsiveTabLayoutProps): TResponsiveTabLayout => {
// Refs for measuring items
// Refs for measuring space and items
const containerRef = useRef<HTMLDivElement>(null);
const itemRefs = useRef<(HTMLDivElement | null)[]>([]);
const resizeObserverRef = useRef<ResizeObserver | null>(null);
// State for responsive behavior
const [containerWidth, setContainerWidth] = useState<number>(0);
@@ -42,44 +42,24 @@ export const useResponsiveTabLayout = ({
const gap = 4; // gap-1 = 4px
const overflowButtonWidth = 40;
// Callback ref that sets up ResizeObserver when element is attached
const containerRef = useCallback((node: HTMLDivElement | null) => {
// Clean up previous observer if it exists
if (resizeObserverRef.current) {
resizeObserverRef.current.disconnect();
resizeObserverRef.current = null;
}
const container = containerRef?.current;
// If node is null (unmounting), just clean up
if (!node) {
setContainerWidth(0);
return;
}
// ResizeObserver to measure container width
useEffect(() => {
if (!container) return;
// Set initial width immediately
setContainerWidth(node.offsetWidth);
// Create and set up new ResizeObserver
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
setContainerWidth(entry.contentRect.width);
}
});
resizeObserverRef.current = resizeObserver;
resizeObserver.observe(node);
}, []); // Empty deps - callback function remains stable
resizeObserver.observe(container);
// Cleanup effect to disconnect observer on component unmount
useEffect(
() => () => {
if (resizeObserverRef.current) {
resizeObserverRef.current.disconnect();
resizeObserverRef.current = null;
}
},
[]
);
return () => {
resizeObserver.disconnect();
};
}, [container]);
// Calculate how many items can fit
useEffect(() => {
@@ -157,7 +137,7 @@ export const useResponsiveTabLayout = ({
visibleItems,
overflowItems,
hasOverflow,
itemRefs,
containerRef,
itemRefs,
};
};
@@ -38,7 +38,7 @@ export const HelpMenuRoot = observer(function HelpMenuRoot() {
<AppSidebarItem
variant="button"
item={{
icon: <HelpCircle className="size-5" />,
icon: <HelpCircle className="size-4" />,
isActive: isNeedHelpOpen,
}}
/>
@@ -69,8 +69,8 @@ export const SidebarProjectsListItem = observer(function SidebarProjectsListItem
const { isMobile } = usePlatformOS();
const { allowPermissions } = useUserPermissions();
const { getIsProjectListOpen, toggleProjectListOpen } = useCommandPalette();
const { toggleAnySidebarDropdown } = useAppTheme();
const { preferences: projectPreferences } = useProjectNavigationPreferences();
const { isExtendedProjectSidebarOpened, toggleExtendedProjectSidebar, toggleAnySidebarDropdown } = useAppTheme();
// states
const [leaveProjectModalOpen, setLeaveProjectModal] = useState(false);
@@ -259,10 +259,6 @@ export const SidebarProjectsListItem = observer(function SidebarProjectsListItem
} else {
router.push(defaultTabUrl);
}
// close the extended sidebar if it is open
if (isExtendedProjectSidebarOpened) {
toggleExtendedProjectSidebar(false);
}
};
const isAccordionMode = projectPreferences.navigationMode === "accordion";
+1 -1
View File
@@ -76,7 +76,6 @@
"uuid": "catalog:"
},
"devDependencies": {
"@dotenvx/dotenvx": "catalog:",
"@plane/eslint-config": "workspace:*",
"@plane/tailwind-config": "workspace:*",
"@plane/typescript-config": "workspace:*",
@@ -87,6 +86,7 @@
"@types/react": "catalog:",
"@types/react-color": "^3.0.6",
"@types/react-dom": "catalog:",
"dotenv": "^16.4.5",
"prettier": "^3.2.5",
"typescript": "catalog:",
"vite": "catalog:",
+1 -1
View File
@@ -1,6 +1,6 @@
import path from "node:path";
import * as dotenv from "@dotenvx/dotenvx";
import { reactRouter } from "@react-router/dev/vite";
import dotenv from "dotenv";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
+1 -3
View File
@@ -24,8 +24,6 @@
},
"pnpm": {
"overrides": {
"express": "catalog:",
"mdast-util-to-hast": "13.2.1",
"valibot": "1.2.0",
"glob": "11.1.0",
"js-yaml": "4.1.1",
@@ -50,7 +48,7 @@
"unrs-resolver"
]
},
"packageManager": "pnpm@10.24.0+sha512.01ff8ae71b4419903b65c60fb2dc9d34cf8bb6e06d03bde112ef38f7a34d6904c424ba66bea5cdcf12890230bf39f9580473140ed9c946fef328b6e5238a345a",
"packageManager": "pnpm@10.21.0",
"engines": {
"node": ">=22.18.0"
}
+73 -40
View File
@@ -18,9 +18,6 @@ catalogs:
'@bprogress/core':
specifier: ^1.3.4
version: 1.3.4
'@dotenvx/dotenvx':
specifier: 1.51.1
version: 1.51.1
'@react-router/dev':
specifier: 7.9.5
version: 7.9.5
@@ -98,8 +95,6 @@ catalogs:
version: 13.0.0
overrides:
express: 4.22.0
mdast-util-to-hast: 13.2.1
valibot: 1.2.0
glob: 11.1.0
js-yaml: 4.1.1
@@ -217,9 +212,6 @@ importers:
specifier: 'catalog:'
version: 13.0.0
devDependencies:
'@dotenvx/dotenvx':
specifier: 'catalog:'
version: 1.51.1
'@plane/eslint-config':
specifier: workspace:*
version: link:../../packages/eslint-config
@@ -247,6 +239,9 @@ importers:
'@types/react-dom':
specifier: 'catalog:'
version: 18.3.1
dotenv:
specifier: ^16.4.5
version: 16.6.1
typescript:
specifier: 5.8.3
version: 5.8.3
@@ -260,8 +255,8 @@ importers:
apps/live:
dependencies:
'@dotenvx/dotenvx':
specifier: 'catalog:'
version: 1.51.1
specifier: ^1.49.0
version: 1.49.0
'@hocuspocus/extension-database':
specifier: 2.15.2
version: 2.15.2(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27)
@@ -310,12 +305,15 @@ importers:
cors:
specifier: ^2.8.5
version: 2.8.5
dotenv:
specifier: ^16.4.5
version: 16.6.1
express:
specifier: 4.22.0
version: 4.22.0
specifier: ^4.21.2
version: 4.21.2
express-ws:
specifier: ^5.0.2
version: 5.0.2(express@4.22.0)
version: 5.0.2(express@4.21.2)
helmet:
specifier: ^7.1.0
version: 7.2.0
@@ -477,9 +475,6 @@ importers:
specifier: 'catalog:'
version: 13.0.0
devDependencies:
'@dotenvx/dotenvx':
specifier: 'catalog:'
version: 1.51.1
'@plane/eslint-config':
specifier: workspace:*
version: link:../../packages/eslint-config
@@ -507,6 +502,9 @@ importers:
'@types/react-dom':
specifier: 'catalog:'
version: 18.3.1
dotenv:
specifier: ^16.4.5
version: 16.6.1
typescript:
specifier: 5.8.3
version: 5.8.3
@@ -691,9 +689,6 @@ importers:
specifier: 'catalog:'
version: 13.0.0
devDependencies:
'@dotenvx/dotenvx':
specifier: 'catalog:'
version: 1.51.1
'@plane/eslint-config':
specifier: workspace:*
version: link:../../packages/eslint-config
@@ -724,6 +719,9 @@ importers:
'@types/react-dom':
specifier: 'catalog:'
version: 18.3.1
dotenv:
specifier: ^16.4.5
version: 16.6.1
prettier:
specifier: ^3.2.5
version: 3.6.2
@@ -1863,8 +1861,8 @@ packages:
'@date-fns/tz@1.4.1':
resolution: {integrity: sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==}
'@dotenvx/dotenvx@1.51.1':
resolution: {integrity: sha512-fqcQxcxC4LOaUlW8IkyWw8x0yirlLUkbxohz9OnWvVWjf73J5yyw7jxWnkOJaUKXZotcGEScDox9MU6rSkcDgg==}
'@dotenvx/dotenvx@1.49.0':
resolution: {integrity: sha512-M1cyP6YstFQCjih54SAxCqHLMMi8QqV8tenpgGE48RTXWD7vfMYJiw/6xcCDpS2h28AcLpTsFCZA863Ge9yxzA==}
hasBin: true
'@ecies/ciphers@0.2.4':
@@ -2923,7 +2921,7 @@ packages:
resolution: {integrity: sha512-Mg94Tw9JSaRuwkvIC6PaODRzsLs6mo70ppz5qdIK/G3iotSxsH08TDNdzot7CaXXevk/pIiD/+Tbn0H/asHsYA==}
engines: {node: '>=20.0.0'}
peerDependencies:
express: 4.22.0
express: ^4.17.1 || ^5
react-router: 7.9.5
typescript: 5.8.3
peerDependenciesMeta:
@@ -5696,10 +5694,10 @@ packages:
resolution: {integrity: sha512-0uvmuk61O9HXgLhGl3QhNSEtRsQevtmbL94/eILaliEADZBHZOQUAiHFrGPrgsjikohyrmSG5g+sCfASTt0lkQ==}
engines: {node: '>=4.5.0'}
peerDependencies:
express: 4.22.0
express: ^4.0.0 || ^5.0.0-alpha.1
express@4.22.0:
resolution: {integrity: sha512-c2iPh3xp5vvCLgaHK03+mWLFPhox7j1LwyxcZwFVApEv5i0X+IjPpbT50SJJwwLpdBVfp45AkK/v+AFgv/XlfQ==}
express@4.21.2:
resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==}
engines: {node: '>= 0.10.0'}
extend@3.0.2:
@@ -6668,6 +6666,9 @@ packages:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
mdast-util-definitions@5.1.2:
resolution: {integrity: sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==}
mdast-util-find-and-replace@3.0.2:
resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==}
@@ -6698,8 +6699,11 @@ packages:
mdast-util-phrasing@4.1.0:
resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==}
mdast-util-to-hast@13.2.1:
resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==}
mdast-util-to-hast@12.3.0:
resolution: {integrity: sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==}
mdast-util-to-hast@13.2.0:
resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==}
mdast-util-to-markdown@2.1.2:
resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==}
@@ -8716,12 +8720,18 @@ packages:
unist-util-find-after@5.0.0:
resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==}
unist-util-generated@2.0.1:
resolution: {integrity: sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==}
unist-util-is@5.2.1:
resolution: {integrity: sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==}
unist-util-is@6.0.0:
resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==}
unist-util-position@4.0.4:
resolution: {integrity: sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==}
unist-util-position@5.0.0:
resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==}
@@ -9565,7 +9575,7 @@ snapshots:
'@date-fns/tz@1.4.1': {}
'@dotenvx/dotenvx@1.51.1':
'@dotenvx/dotenvx@1.49.0':
dependencies:
commander: 11.1.0
dotenv: 17.2.1
@@ -10705,10 +10715,10 @@ snapshots:
- tsx
- yaml
'@react-router/express@7.9.5(express@4.22.0)(react-router@7.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.8.3)':
'@react-router/express@7.9.5(express@4.21.2)(react-router@7.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.8.3)':
dependencies:
'@react-router/node': 7.9.5(react-router@7.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.8.3)
express: 4.22.0
express: 4.21.2
react-router: 7.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
optionalDependencies:
typescript: 5.8.3
@@ -10723,10 +10733,10 @@ snapshots:
'@react-router/serve@7.9.5(react-router@7.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.8.3)':
dependencies:
'@mjackson/node-fetch-server': 0.2.0
'@react-router/express': 7.9.5(express@4.22.0)(react-router@7.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.8.3)
'@react-router/express': 7.9.5(express@4.21.2)(react-router@7.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.8.3)
'@react-router/node': 7.9.5(react-router@7.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.8.3)
compression: 1.8.1
express: 4.22.0
express: 4.21.2
get-port: 5.1.1
morgan: 1.10.1
react-router: 7.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -13847,15 +13857,15 @@ snapshots:
lodash: 4.17.21
winston: 3.17.0
express-ws@5.0.2(express@4.22.0):
express-ws@5.0.2(express@4.21.2):
dependencies:
express: 4.22.0
express: 4.21.2
ws: 7.5.10
transitivePeerDependencies:
- bufferutil
- utf-8-validate
express@4.22.0:
express@4.21.2:
dependencies:
accepts: 1.3.8
array-flatten: 1.1.1
@@ -13878,7 +13888,7 @@ snapshots:
parseurl: 1.3.3
path-to-regexp: 0.1.12
proxy-addr: 2.0.7
qs: 6.14.0
qs: 6.13.0
range-parser: 1.2.1
safe-buffer: 5.2.1
send: 0.19.0
@@ -14282,7 +14292,7 @@ snapshots:
comma-separated-tokens: 2.0.3
hast-util-whitespace: 3.0.0
html-void-elements: 3.0.0
mdast-util-to-hast: 13.2.1
mdast-util-to-hast: 13.2.0
property-information: 7.1.0
space-separated-tokens: 2.0.2
stringify-entities: 4.0.4
@@ -14298,7 +14308,7 @@ snapshots:
hast-util-to-text: 4.0.2
hast-util-whitespace: 3.0.0
mdast-util-phrasing: 4.1.0
mdast-util-to-hast: 13.2.1
mdast-util-to-hast: 13.2.0
mdast-util-to-string: 4.0.0
rehype-minify-whitespace: 6.0.2
trim-trailing-lines: 2.1.0
@@ -14926,6 +14936,12 @@ snapshots:
math-intrinsics@1.1.0: {}
mdast-util-definitions@5.1.2:
dependencies:
'@types/mdast': 3.0.15
'@types/unist': 2.0.11
unist-util-visit: 4.1.2
mdast-util-find-and-replace@3.0.2:
dependencies:
'@types/mdast': 4.0.4
@@ -15029,7 +15045,18 @@ snapshots:
'@types/mdast': 4.0.4
unist-util-is: 6.0.0
mdast-util-to-hast@13.2.1:
mdast-util-to-hast@12.3.0:
dependencies:
'@types/hast': 2.3.10
'@types/mdast': 3.0.15
mdast-util-definitions: 5.1.2
micromark-util-sanitize-uri: 1.2.0
trim-lines: 3.0.1
unist-util-generated: 2.0.1
unist-util-position: 4.0.4
unist-util-visit: 4.1.2
mdast-util-to-hast@13.2.0:
dependencies:
'@types/hast': 3.0.4
'@types/mdast': 4.0.4
@@ -16559,7 +16586,7 @@ snapshots:
dependencies:
'@types/hast': 2.3.10
'@types/mdast': 3.0.15
mdast-util-to-hast: 13.2.1
mdast-util-to-hast: 12.3.0
unified: 10.1.2
remark-stringify@11.0.0:
@@ -17453,6 +17480,8 @@ snapshots:
'@types/unist': 3.0.3
unist-util-is: 6.0.0
unist-util-generated@2.0.1: {}
unist-util-is@5.2.1:
dependencies:
'@types/unist': 2.0.11
@@ -17461,6 +17490,10 @@ snapshots:
dependencies:
'@types/unist': 3.0.3
unist-util-position@4.0.4:
dependencies:
'@types/unist': 2.0.11
unist-util-position@5.0.0:
dependencies:
'@types/unist': 3.0.3
-2
View File
@@ -9,7 +9,6 @@ catalog:
"@atlaskit/pragmatic-drag-and-drop-hitbox": 1.1.0
"@atlaskit/pragmatic-drag-and-drop": 1.7.4
"@bprogress/core": ^1.3.4
"@dotenvx/dotenvx": 1.51.1
"@react-router/dev": 7.9.5
"@react-router/node": 7.9.5
"@react-router/serve": 7.9.5
@@ -23,7 +22,6 @@ catalog:
"@types/react-dom": 18.3.1
"@types/react": 18.3.11
axios: 1.12.0
express: 4.22.0
lodash-es: 4.17.21
lucide-react: 0.469.0
mobx-react: 9.1.1