wahhhh
This commit is contained in:
@@ -1,10 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect, useRef } from "react";
|
||||||
import Layout from "@/components/layout/layout";
|
import Layout from "@/components/layout/layout";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Plus, Pencil, Trash2, ChevronRight, ChevronDown } from "lucide-react";
|
import { Plus, Pencil, Trash2, ChevronRight, ChevronDown, MoveVertical } from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
@@ -27,17 +27,39 @@ import { apiRequest } from "@/lib/api";
|
|||||||
import type { Category } from "@/models/categories";
|
import type { Category } from "@/models/categories";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
|
||||||
|
// Drag and Drop imports
|
||||||
|
import { DndProvider, useDrag, useDrop, DropTargetMonitor } from 'react-dnd';
|
||||||
|
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||||
|
|
||||||
|
// Define interfaces for drag item
|
||||||
|
interface DragItem {
|
||||||
|
id: string;
|
||||||
|
parentId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Item Types
|
||||||
|
const ItemTypes = {
|
||||||
|
CATEGORY: 'category',
|
||||||
|
};
|
||||||
|
|
||||||
export default function CategoriesPage() {
|
export default function CategoriesPage() {
|
||||||
const [categories, setCategories] = useState<Category[]>([]);
|
const [categories, setCategories] = useState<Category[]>([]);
|
||||||
const [newCategoryName, setNewCategoryName] = useState("");
|
const [newCategoryName, setNewCategoryName] = useState("");
|
||||||
const [selectedParentId, setSelectedParentId] = useState<string>("");
|
const [selectedParentId, setSelectedParentId] = useState<string>("");
|
||||||
const [editingCategory, setEditingCategory] = useState<Category | null>(null);
|
const [editingCategory, setEditingCategory] = useState<Category | null>(null);
|
||||||
const [categoryToDelete, setCategoryToDelete] = useState<Category | null>(null);
|
const [categoryToDelete, setCategoryToDelete] = useState<Category | null>(null);
|
||||||
|
const [expanded, setExpanded] = useState<Record<string, boolean>>({});
|
||||||
|
|
||||||
const rootCategories = categories.filter(cat => !cat.parentId);
|
// Get root categories sorted by order
|
||||||
|
const rootCategories = categories
|
||||||
|
.filter(cat => !cat.parentId)
|
||||||
|
.sort((a, b) => (a.order || 0) - (b.order || 0));
|
||||||
|
|
||||||
|
// Get subcategories sorted by order
|
||||||
const getSubcategories = (parentId: string) =>
|
const getSubcategories = (parentId: string) =>
|
||||||
categories.filter(cat => cat.parentId === parentId);
|
categories
|
||||||
|
.filter(cat => cat.parentId === parentId)
|
||||||
|
.sort((a, b) => (a.order || 0) - (b.order || 0));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchCategories();
|
fetchCategories();
|
||||||
@@ -59,9 +81,18 @@ export default function CategoriesPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Find the highest order in the selected parent group
|
||||||
|
const siblings = selectedParentId
|
||||||
|
? categories.filter(cat => cat.parentId === selectedParentId)
|
||||||
|
: categories.filter(cat => !cat.parentId);
|
||||||
|
|
||||||
|
const maxOrder = siblings.reduce((max, cat) =>
|
||||||
|
Math.max(max, cat.order || 0), 0);
|
||||||
|
|
||||||
const response = await apiRequest("/categories", "POST", {
|
const response = await apiRequest("/categories", "POST", {
|
||||||
name: newCategoryName,
|
name: newCategoryName,
|
||||||
parentId: selectedParentId || undefined,
|
parentId: selectedParentId || undefined,
|
||||||
|
order: maxOrder + 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
setCategories([...categories, response]);
|
setCategories([...categories, response]);
|
||||||
@@ -103,21 +134,142 @@ export default function CategoriesPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderCategoryItem = (category: Category, level: number = 0) => {
|
const moveCategory = async (dragId: string, hoverId: string, parentId?: string) => {
|
||||||
|
// Create new array with updated orders
|
||||||
|
const dragIndex = categories.findIndex(cat => cat._id === dragId);
|
||||||
|
const hoverIndex = categories.findIndex(cat => cat._id === hoverId);
|
||||||
|
|
||||||
|
if (dragIndex === -1 || hoverIndex === -1) return;
|
||||||
|
|
||||||
|
const draggedCategory = categories[dragIndex];
|
||||||
|
|
||||||
|
// Make sure we're only reordering within the same parent group
|
||||||
|
if (draggedCategory.parentId !== parentId) return;
|
||||||
|
|
||||||
|
// Create a copy for reordering
|
||||||
|
const updatedCategories = [...categories];
|
||||||
|
|
||||||
|
// Get only categories with the same parent for reordering
|
||||||
|
const siblingCategories = updatedCategories
|
||||||
|
.filter(cat => cat.parentId === parentId)
|
||||||
|
.sort((a, b) => (a.order || 0) - (b.order || 0));
|
||||||
|
|
||||||
|
// Remove the dragged category from its position
|
||||||
|
const draggedItem = siblingCategories.find(cat => cat._id === dragId);
|
||||||
|
if (!draggedItem) return;
|
||||||
|
|
||||||
|
const filteredSiblings = siblingCategories.filter(cat => cat._id !== dragId);
|
||||||
|
|
||||||
|
// Find where to insert the dragged item
|
||||||
|
const hoverItem = siblingCategories.find(cat => cat._id === hoverId);
|
||||||
|
if (!hoverItem) return;
|
||||||
|
|
||||||
|
const hoverPos = filteredSiblings.findIndex(cat => cat._id === hoverId);
|
||||||
|
|
||||||
|
// Insert the dragged item at the new position
|
||||||
|
filteredSiblings.splice(hoverPos, 0, draggedItem);
|
||||||
|
|
||||||
|
// Update the order for all siblings
|
||||||
|
filteredSiblings.forEach((cat, index) => {
|
||||||
|
cat.order = index;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update the categories state
|
||||||
|
setCategories(updatedCategories.map(cat => {
|
||||||
|
const updatedCat = filteredSiblings.find(c => c._id === cat._id);
|
||||||
|
return updatedCat || cat;
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Save the new order to the server
|
||||||
|
try {
|
||||||
|
await apiRequest(`/categories/${dragId}`, "PUT", {
|
||||||
|
order: filteredSiblings.find(cat => cat._id === dragId)?.order,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
toast.error("Failed to update category order");
|
||||||
|
// Revert to original order
|
||||||
|
fetchCategories();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleExpand = (categoryId: string) => {
|
||||||
|
setExpanded(prev => ({
|
||||||
|
...prev,
|
||||||
|
[categoryId]: !prev[categoryId]
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Category Item Component with Drag & Drop
|
||||||
|
const CategoryItem = ({ category, level = 0 }: { category: Category, level?: number }) => {
|
||||||
const subcategories = getSubcategories(category._id);
|
const subcategories = getSubcategories(category._id);
|
||||||
const hasSubcategories = subcategories.length > 0;
|
const hasSubcategories = subcategories.length > 0;
|
||||||
const isEditing = editingCategory?._id === category._id;
|
const isEditing = editingCategory?._id === category._id;
|
||||||
|
const isExpanded = expanded[category._id];
|
||||||
|
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
// Set up drag
|
||||||
|
const [{ isDragging }, drag] = useDrag({
|
||||||
|
type: ItemTypes.CATEGORY,
|
||||||
|
item: { id: category._id, parentId: category.parentId },
|
||||||
|
collect: (monitor) => ({
|
||||||
|
isDragging: monitor.isDragging(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set up drop
|
||||||
|
const [{ handlerId, isOver }, drop] = useDrop<DragItem, void, { handlerId: string | symbol | null; isOver: boolean }>({
|
||||||
|
accept: ItemTypes.CATEGORY,
|
||||||
|
collect: (monitor) => ({
|
||||||
|
handlerId: monitor.getHandlerId(),
|
||||||
|
isOver: monitor.isOver(),
|
||||||
|
}),
|
||||||
|
hover(item: DragItem, monitor) {
|
||||||
|
if (!ref.current) return;
|
||||||
|
|
||||||
|
const dragId = item.id;
|
||||||
|
const hoverId = category._id;
|
||||||
|
|
||||||
|
// Don't replace items with themselves
|
||||||
|
if (dragId === hoverId) return;
|
||||||
|
|
||||||
|
// Only allow reordering within the same parent
|
||||||
|
if (item.parentId !== category.parentId) return;
|
||||||
|
|
||||||
|
moveCategory(dragId, hoverId, category.parentId);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect the drag and drop refs
|
||||||
|
drag(drop(ref));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={category._id} className="space-y-1">
|
<div key={category._id} className="space-y-1">
|
||||||
<div
|
<div
|
||||||
className={`group flex items-center p-2 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors
|
ref={ref}
|
||||||
${isEditing ? 'bg-gray-100 dark:bg-gray-800' : ''}`}
|
className={`group flex items-center p-2 rounded-md transition-colors
|
||||||
|
${isEditing ? 'bg-gray-100 dark:bg-gray-800' : ''}
|
||||||
|
${isOver ? 'bg-gray-100 dark:bg-gray-800' : 'hover:bg-gray-100 dark:hover:bg-gray-800'}
|
||||||
|
${isDragging ? 'opacity-50' : 'opacity-100'}`}
|
||||||
style={{ marginLeft: `${level * 24}px` }}
|
style={{ marginLeft: `${level * 24}px` }}
|
||||||
|
data-handler-id={handlerId}
|
||||||
>
|
>
|
||||||
|
<div className="cursor-grab mr-2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300">
|
||||||
|
<MoveVertical className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
|
||||||
{hasSubcategories && (
|
{hasSubcategories && (
|
||||||
<ChevronRight className="h-4 w-4 mr-2 text-muted-foreground" />
|
<button
|
||||||
|
onClick={() => toggleExpand(category._id)}
|
||||||
|
className="mr-1 focus:outline-none"
|
||||||
|
>
|
||||||
|
{isExpanded ?
|
||||||
|
<ChevronDown className="h-4 w-4 text-muted-foreground" /> :
|
||||||
|
<ChevronRight className="h-4 w-4 text-muted-foreground" />
|
||||||
|
}
|
||||||
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex-1 flex items-center space-x-2">
|
<div className="flex-1 flex items-center space-x-2">
|
||||||
{isEditing ? (
|
{isEditing ? (
|
||||||
<Input
|
<Input
|
||||||
@@ -179,7 +331,9 @@ export default function CategoriesPage() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{subcategories.map(subcat => renderCategoryItem(subcat, level + 1))}
|
{isExpanded && subcategories.map(subcat =>
|
||||||
|
<CategoryItem key={subcat._id} category={subcat} level={level + 1} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -242,40 +396,40 @@ export default function CategoriesPage() {
|
|||||||
<CardTitle className="text-xl font-semibold">Category List</CardTitle>
|
<CardTitle className="text-xl font-semibold">Category List</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="space-y-1">
|
<DndProvider backend={HTML5Backend}>
|
||||||
{rootCategories.length === 0 ? (
|
<div className="space-y-1">
|
||||||
<p className="text-sm text-muted-foreground text-center py-4">
|
{rootCategories.length === 0 ? (
|
||||||
No categories yet. Add your first category above.
|
<p className="text-sm text-muted-foreground text-center py-4">
|
||||||
</p>
|
No categories yet. Add your first category above.
|
||||||
) : (
|
</p>
|
||||||
rootCategories.map(category => renderCategoryItem(category))
|
) : (
|
||||||
)}
|
rootCategories.map(category => (
|
||||||
</div>
|
<CategoryItem key={category._id} category={category} />
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</DndProvider>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* Delete Confirmation Dialog */}
|
{/* Delete Confirmation Dialog */}
|
||||||
<AlertDialog open={!!categoryToDelete} onOpenChange={() => setCategoryToDelete(null)}>
|
{categoryToDelete && (
|
||||||
<AlertDialogContent>
|
<AlertDialog open={!!categoryToDelete} onOpenChange={() => setCategoryToDelete(null)}>
|
||||||
<AlertDialogHeader>
|
<AlertDialogContent>
|
||||||
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
<AlertDialogHeader>
|
||||||
<AlertDialogDescription>
|
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
|
||||||
This will permanently delete the category "{categoryToDelete?.name}"
|
<AlertDialogDescription>
|
||||||
{getSubcategories(categoryToDelete?._id || "").length > 0 &&
|
This will permanently delete the category "{categoryToDelete.name}".
|
||||||
" and all its subcategories"}. This action cannot be undone.
|
This action cannot be undone.
|
||||||
</AlertDialogDescription>
|
</AlertDialogDescription>
|
||||||
</AlertDialogHeader>
|
</AlertDialogHeader>
|
||||||
<AlertDialogFooter>
|
<AlertDialogFooter>
|
||||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||||
<AlertDialogAction
|
<AlertDialogAction onClick={handleDeleteConfirm}>Delete</AlertDialogAction>
|
||||||
onClick={handleDeleteConfirm}
|
</AlertDialogFooter>
|
||||||
className="bg-red-500 hover:bg-red-600"
|
</AlertDialogContent>
|
||||||
>
|
</AlertDialog>
|
||||||
Delete
|
)}
|
||||||
</AlertDialogAction>
|
|
||||||
</AlertDialogFooter>
|
|
||||||
</AlertDialogContent>
|
|
||||||
</AlertDialog>
|
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
export interface Category {
|
export interface Category {
|
||||||
_id: string;
|
_id: string;
|
||||||
name: string;
|
name: string;
|
||||||
parentId?: string
|
parentId?: string;
|
||||||
|
order?: number;
|
||||||
}
|
}
|
||||||
89
package-lock.json
generated
89
package-lock.json
generated
@@ -55,6 +55,8 @@
|
|||||||
"pusher-js": "^8.4.0",
|
"pusher-js": "^8.4.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-day-picker": "8.10.1",
|
"react-day-picker": "8.10.1",
|
||||||
|
"react-dnd": "^16.0.1",
|
||||||
|
"react-dnd-html5-backend": "^16.0.1",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-hook-form": "^7.54.1",
|
"react-hook-form": "^7.54.1",
|
||||||
"react-markdown": "^10.0.0",
|
"react-markdown": "^10.0.0",
|
||||||
@@ -2421,6 +2423,24 @@
|
|||||||
"integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==",
|
"integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@react-dnd/asap": {
|
||||||
|
"version": "5.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz",
|
||||||
|
"integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@react-dnd/invariant": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@react-dnd/shallowequal": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@react-three/drei": {
|
"node_modules/@react-three/drei": {
|
||||||
"version": "10.0.6",
|
"version": "10.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-10.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-10.0.6.tgz",
|
||||||
@@ -4463,6 +4483,17 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/dnd-core": {
|
||||||
|
"version": "16.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz",
|
||||||
|
"integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@react-dnd/asap": "^5.0.1",
|
||||||
|
"@react-dnd/invariant": "^4.0.1",
|
||||||
|
"redux": "^4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/doctrine": {
|
"node_modules/doctrine": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
|
||||||
@@ -5197,7 +5228,6 @@
|
|||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/fast-equals": {
|
"node_modules/fast-equals": {
|
||||||
@@ -5842,6 +5872,15 @@
|
|||||||
"integrity": "sha512-7GOkcqn0Y9EqU2OJZlzkwxj9Uynuln7URvr7dRjgqNJNZ5UbbjL/v1BjAvQogy57Psdd/ek1u2s6IDEFYlabrA==",
|
"integrity": "sha512-7GOkcqn0Y9EqU2OJZlzkwxj9Uynuln7URvr7dRjgqNJNZ5UbbjL/v1BjAvQogy57Psdd/ek1u2s6IDEFYlabrA==",
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
},
|
},
|
||||||
|
"node_modules/hoist-non-react-statics": {
|
||||||
|
"version": "3.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
||||||
|
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"react-is": "^16.7.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/html-url-attributes": {
|
"node_modules/html-url-attributes": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz",
|
||||||
@@ -8216,6 +8255,45 @@
|
|||||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-dnd": {
|
||||||
|
"version": "16.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz",
|
||||||
|
"integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@react-dnd/invariant": "^4.0.1",
|
||||||
|
"@react-dnd/shallowequal": "^4.0.1",
|
||||||
|
"dnd-core": "^16.0.1",
|
||||||
|
"fast-deep-equal": "^3.1.3",
|
||||||
|
"hoist-non-react-statics": "^3.3.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/hoist-non-react-statics": ">= 3.3.1",
|
||||||
|
"@types/node": ">= 12",
|
||||||
|
"@types/react": ">= 16",
|
||||||
|
"react": ">= 16.14"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/hoist-non-react-statics": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/node": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-dnd-html5-backend": {
|
||||||
|
"version": "16.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz",
|
||||||
|
"integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"dnd-core": "^16.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-dom": {
|
"node_modules/react-dom": {
|
||||||
"version": "19.0.0",
|
"version": "19.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
|
||||||
@@ -8478,6 +8556,15 @@
|
|||||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
|
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/redux": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.9.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/reflect.getprototypeof": {
|
"node_modules/reflect.getprototypeof": {
|
||||||
"version": "1.0.10",
|
"version": "1.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
|
||||||
|
|||||||
@@ -63,6 +63,8 @@
|
|||||||
"pusher-js": "^8.4.0",
|
"pusher-js": "^8.4.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-day-picker": "8.10.1",
|
"react-day-picker": "8.10.1",
|
||||||
|
"react-dnd": "^16.0.1",
|
||||||
|
"react-dnd-html5-backend": "^16.0.1",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-hook-form": "^7.54.1",
|
"react-hook-form": "^7.54.1",
|
||||||
"react-markdown": "^10.0.0",
|
"react-markdown": "^10.0.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user