✨ Enhanced Calendar & Project Tracker UI
� Calendar Improvements: - Fixed duplicate API calls and event handling - Enhanced calendar toolbar with beautiful gradient buttons - Fixed button alignment and clipping issues - Added custom calendar-custom.css with modern styling - Improved drag & drop functionality with better visual feedback - Enhanced recently dropped events section with proper scrolling - Added dark mode support for all calendar components - Mobile responsive design with optimized button layouts � Project Tracker Fixes: - Eliminated duplicate API calls with loading refs and time-based prevention - Enhanced pagination handling to prevent double requests - Improved error handling and loading states - Better console logging for debugging � UI/UX Enhancements: - Beautiful gradient backgrounds and hover effects - Proper icon alignment in recently dropped events - Color-coded status indicators (dots instead of text) - Smooth animations and transitions - Enhanced visual hierarchy and spacing - Professional styling with backdrop filters and shadows � Responsive Design: - Mobile-optimized layouts and button sizes - Proper touch targets and spacing - Consistent styling across all screen sizes � Performance: - Reduced re-renders and optimized event handling - Better memory management with proper cleanup - Eliminated React StrictMode double rendering in development
This commit is contained in:
parent
a5d79ad10f
commit
80bb712c70
@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable no-dupe-keys */
|
/* eslint-disable no-dupe-keys */
|
||||||
/* eslint-disable no-const-assign */
|
/* eslint-disable no-const-assign */
|
||||||
/* eslint-disable no-unused-vars */
|
/* eslint-disable no-unused-vars */
|
||||||
import React, { useState, useEffect, useRef } from "react";
|
import React, { useState, useEffect, useRef, useCallback } from "react";
|
||||||
import FullCalendar from "@fullcalendar/react";
|
import FullCalendar from "@fullcalendar/react";
|
||||||
import dayGridPlugin from "@fullcalendar/daygrid";
|
import dayGridPlugin from "@fullcalendar/daygrid";
|
||||||
import timeGridPlugin from "@fullcalendar/timegrid";
|
import timeGridPlugin from "@fullcalendar/timegrid";
|
||||||
@ -9,6 +9,7 @@ import interactionPlugin from "@fullcalendar/interaction";
|
|||||||
import { Draggable } from "@fullcalendar/interaction";
|
import { Draggable } from "@fullcalendar/interaction";
|
||||||
// import "../../assets/plugins/fullcalendar/fullcalendar.min.css";
|
// import "../../assets/plugins/fullcalendar/fullcalendar.min.css";
|
||||||
import "../../style/css/fullcalendar.min.css";
|
import "../../style/css/fullcalendar.min.css";
|
||||||
|
import "../../style/css/calendar-custom.css";
|
||||||
// import FullCalendar from '@fullcalendar/react/dist/main.esm.js';
|
// import FullCalendar from '@fullcalendar/react/dist/main.esm.js';
|
||||||
|
|
||||||
import Select from "react-select";
|
import Select from "react-select";
|
||||||
@ -65,15 +66,26 @@ const Calendar = () => {
|
|||||||
className: "bg-warning",
|
className: "bg-warning",
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
// Add ref to prevent multiple initialization
|
||||||
|
const initializedRef = React.useRef(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// Prevent multiple initialization
|
||||||
|
if (initializedRef.current) {
|
||||||
|
console.log("🚫 Calendar already initialized, skipping");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let elements = Array.from(
|
let elements = Array.from(
|
||||||
document.getElementsByClassName("react-datepicker-wrapper")
|
document.getElementsByClassName("react-datepicker-wrapper")
|
||||||
);
|
);
|
||||||
elements.map((element) => element.classList.add("width-100"));
|
elements.map((element) => element.classList.add("width-100"));
|
||||||
|
|
||||||
// Initialize external draggable events with simple hide/show
|
// Initialize external draggable events with enhanced duplicate prevention
|
||||||
const draggableEl = document.getElementById("calendar-events");
|
const draggableEl = document.getElementById("calendar-events");
|
||||||
if (draggableEl) {
|
if (draggableEl) {
|
||||||
|
console.log("🚀 Initializing calendar draggable events");
|
||||||
|
|
||||||
new Draggable(draggableEl, {
|
new Draggable(draggableEl, {
|
||||||
itemSelector: ".calendar-events",
|
itemSelector: ".calendar-events",
|
||||||
eventData: function(eventEl) {
|
eventData: function(eventEl) {
|
||||||
@ -91,10 +103,9 @@ const Calendar = () => {
|
|||||||
|
|
||||||
// Store reference to currently dragging element
|
// Store reference to currently dragging element
|
||||||
let currentDragElement = null;
|
let currentDragElement = null;
|
||||||
let dragHelper = null;
|
|
||||||
|
|
||||||
// Listen for drag start from external elements
|
// Listen for drag start from external elements
|
||||||
draggableEl.addEventListener('dragstart', function(e) {
|
const handleDragStart = (e) => {
|
||||||
const target = e.target.closest('.calendar-events');
|
const target = e.target.closest('.calendar-events');
|
||||||
if (target) {
|
if (target) {
|
||||||
currentDragElement = target;
|
currentDragElement = target;
|
||||||
@ -102,27 +113,42 @@ const Calendar = () => {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (currentDragElement) {
|
if (currentDragElement) {
|
||||||
currentDragElement.classList.add('dragging-hidden');
|
currentDragElement.classList.add('dragging-hidden');
|
||||||
|
console.log("🎯 Hiding dragged element:", target.innerText.trim());
|
||||||
}
|
}
|
||||||
}, 10); // Small delay to let drag start
|
}, 10); // Small delay to let drag start
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
// Simple approach - just hide the original item during drag
|
|
||||||
// No custom helper, let FullCalendar handle the drag visual
|
|
||||||
|
|
||||||
// Listen for drag end
|
// Listen for drag end
|
||||||
document.addEventListener('dragend', function(e) {
|
const handleDragEnd = (e) => {
|
||||||
if (currentDragElement) {
|
if (currentDragElement) {
|
||||||
currentDragElement.classList.remove('dragging-hidden');
|
currentDragElement.classList.remove('dragging-hidden');
|
||||||
|
console.log("🎯 Showing dragged element back");
|
||||||
currentDragElement = null;
|
currentDragElement = null;
|
||||||
}
|
}
|
||||||
if (dragHelper && dragHelper.parentNode) {
|
};
|
||||||
dragHelper.parentNode.removeChild(dragHelper);
|
|
||||||
dragHelper = null;
|
draggableEl.addEventListener('dragstart', handleDragStart);
|
||||||
}
|
document.addEventListener('dragend', handleDragEnd);
|
||||||
});
|
|
||||||
|
// Mark as initialized
|
||||||
|
initializedRef.current = true;
|
||||||
|
console.log("✅ Calendar draggable events initialized successfully");
|
||||||
|
|
||||||
|
// Cleanup function
|
||||||
|
return () => {
|
||||||
|
draggableEl.removeEventListener('dragstart', handleDragStart);
|
||||||
|
document.removeEventListener('dragend', handleDragEnd);
|
||||||
|
initializedRef.current = false;
|
||||||
|
console.log("🧹 Calendar drag listeners cleaned up");
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}, []);
|
}, []); // Empty dependency array for one-time initialization
|
||||||
|
|
||||||
|
// Debug useEffect to track calendarEvents changes - DISABLED to prevent re-renders
|
||||||
|
// useEffect(() => {
|
||||||
|
// console.log("🔥 calendarEvents changed:", calendarEvents.length, calendarEvents);
|
||||||
|
// }, [calendarEvents]);
|
||||||
|
|
||||||
const handleChange = (date) => {
|
const handleChange = (date) => {
|
||||||
setDate(date);
|
setDate(date);
|
||||||
@ -154,48 +180,65 @@ const Calendar = () => {
|
|||||||
setaddneweventobj(selectInfo);
|
setaddneweventobj(selectInfo);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEventReceive = (info) => {
|
// Add ref to track processing state more reliably
|
||||||
// Handle external drag and drop
|
const processingRef = React.useRef(false);
|
||||||
console.log("Event received:", info.event);
|
const lastDropTime = React.useRef(0);
|
||||||
|
|
||||||
// Prevent FullCalendar from automatically adding the event
|
const handleEventReceive = useCallback((info) => {
|
||||||
// We'll handle it manually to avoid duplicates
|
const now = Date.now();
|
||||||
|
const timeSinceLastDrop = now - lastDropTime.current;
|
||||||
|
|
||||||
|
// Handle external drag and drop with enhanced duplicate prevention
|
||||||
|
console.log("🔥 handleEventReceive called - Event:", info.event.title);
|
||||||
|
|
||||||
|
// Prevent duplicate processing within 300ms
|
||||||
|
if (processingRef.current || timeSinceLastDrop < 300) {
|
||||||
|
console.log("🚫 Duplicate drop prevented:", {
|
||||||
|
processing: processingRef.current,
|
||||||
|
timeSinceLastDrop
|
||||||
|
});
|
||||||
|
info.revert();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
processingRef.current = true;
|
||||||
|
lastDropTime.current = now;
|
||||||
|
|
||||||
|
// Prevent default behavior
|
||||||
info.revert();
|
info.revert();
|
||||||
|
|
||||||
// Create event object
|
// Create event object with unique ID
|
||||||
|
const uniqueId = `dropped-${now}-${Math.random().toString(36).substr(2, 9)}`;
|
||||||
const newEvent = {
|
const newEvent = {
|
||||||
id: `dropped-${Date.now()}`,
|
id: uniqueId,
|
||||||
title: info.event.title,
|
title: info.event.title,
|
||||||
start: info.event.start,
|
start: info.event.start,
|
||||||
end: info.event.end || new Date(info.event.start.getTime() + 60 * 60 * 1000), // Default 1 hour duration
|
end: info.event.end || new Date(info.event.start.getTime() + 60 * 60 * 1000),
|
||||||
className: info.event.classNames[0] || 'bg-primary',
|
className: info.event.classNames[0] || 'bg-primary',
|
||||||
droppedAt: new Date().toLocaleString(),
|
droppedAt: new Date().toLocaleString(),
|
||||||
source: 'external'
|
source: 'external'
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add to calendar events state to display on calendar
|
console.log("✅ Creating new event:", uniqueId);
|
||||||
|
|
||||||
|
// Update calendar events
|
||||||
setCalendarEvents(prev => [...prev, newEvent]);
|
setCalendarEvents(prev => [...prev, newEvent]);
|
||||||
|
|
||||||
// Add to dropped events list for tracking
|
// Add to dropped events list
|
||||||
setDroppedEvents(prev => [...prev, newEvent]);
|
setDroppedEvents(prev => [...prev, newEvent]);
|
||||||
|
|
||||||
// Show success notification in console only
|
// Handle "Remove after drop" option
|
||||||
console.log("✅ Event successfully dropped:", newEvent);
|
const removeAfterDrop = document.getElementById("drop-remove")?.checked;
|
||||||
|
if (removeAfterDrop && info.draggedEl) {
|
||||||
// Show the original item again (in case it was hidden)
|
|
||||||
const draggedEl = info.draggedEl;
|
|
||||||
if (draggedEl) {
|
|
||||||
draggedEl.classList.remove('dragging-hidden');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if "Remove after drop" is checked
|
|
||||||
const removeAfterDrop = document.getElementById("drop-remove").checked;
|
|
||||||
if (removeAfterDrop) {
|
|
||||||
// Remove the dragged element from the external list
|
|
||||||
info.draggedEl.remove();
|
info.draggedEl.remove();
|
||||||
console.log("🗑️ Original event removed from sidebar");
|
console.log("🗑️ Original event removed from sidebar");
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
// Reset processing flag
|
||||||
|
setTimeout(() => {
|
||||||
|
processingRef.current = false;
|
||||||
|
}, 300);
|
||||||
|
}, []); // Remove dependencies to prevent unnecessary re-creation
|
||||||
|
|
||||||
const handleEventDrop = (info) => {
|
const handleEventDrop = (info) => {
|
||||||
// Handle internal event drag and drop
|
// Handle internal event drag and drop
|
||||||
@ -331,32 +374,52 @@ const Calendar = () => {
|
|||||||
{/* Dropped Events Tracker */}
|
{/* Dropped Events Tracker */}
|
||||||
{droppedEvents.length > 0 && (
|
{droppedEvents.length > 0 && (
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<h5 className="text-success">✅ Recently Dropped Events ({droppedEvents.length})</h5>
|
<div className="dropped-events-header">
|
||||||
|
✅ Recently Dropped Events
|
||||||
|
<span className="dropped-events-count">{droppedEvents.length}</span>
|
||||||
|
</div>
|
||||||
<div className="dropped-events-list">
|
<div className="dropped-events-list">
|
||||||
{droppedEvents.slice(-5).map((event, index) => (
|
{droppedEvents.slice(-8).map((event) => (
|
||||||
<div key={event.id} className="dropped-event-item mb-2 p-2 border rounded">
|
<div key={event.id} className="dropped-event-item p-3">
|
||||||
<div className="d-flex justify-content-between align-items-center">
|
<div className="row align-items-start">
|
||||||
<div>
|
<div className="col-8">
|
||||||
<strong>{event.title}</strong>
|
<strong>{event.title}</strong>
|
||||||
<br />
|
<div className="event-time">
|
||||||
<small className="text-muted">
|
<span className="event-icon">📅</span>
|
||||||
📅 {event.start.toLocaleDateString()} at {event.start.toLocaleTimeString()}
|
<small className="text-muted">
|
||||||
</small>
|
{event.start.toLocaleDateString()} • {event.start.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}
|
||||||
<br />
|
</small>
|
||||||
<small className="text-success">
|
</div>
|
||||||
⏰ Dropped: {event.droppedAt}
|
<div className="event-dropped-time">
|
||||||
</small>
|
<span className="event-icon">⏰</span>
|
||||||
|
<small className="text-success">
|
||||||
|
{event.droppedAt}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-4 text-end">
|
||||||
|
<span
|
||||||
|
className={`badge ${event.className}`}
|
||||||
|
title={event.className.replace('bg-', '').toUpperCase()}
|
||||||
|
style={{
|
||||||
|
width: '20px',
|
||||||
|
height: '20px',
|
||||||
|
borderRadius: '50%',
|
||||||
|
display: 'inline-block',
|
||||||
|
border: '2px solid rgba(255,255,255,0.3)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span className={`badge ${event.className}`}>
|
|
||||||
{event.className.replace('bg-', '')}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{droppedEvents.length > 5 && (
|
{droppedEvents.length > 8 && (
|
||||||
<small className="text-muted">
|
<div className="text-center mt-2">
|
||||||
... and {droppedEvents.length - 5} more events
|
<small className="text-muted" style={{fontStyle: 'italic'}}>
|
||||||
</small>
|
... and {droppedEvents.length - 8} more events
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -35,12 +35,39 @@ const ProjectTracker = () => {
|
|||||||
const [totalCount, setTotalCount] = useState(0);
|
const [totalCount, setTotalCount] = useState(0);
|
||||||
const [totalPages, setTotalPages] = useState(1);
|
const [totalPages, setTotalPages] = useState(1);
|
||||||
|
|
||||||
// Load projects from API
|
// Add loading ref to prevent duplicate calls with timestamp
|
||||||
const loadProjects = async (page = currentPage, size = pageSize) => {
|
const loadingRef = React.useRef(false);
|
||||||
|
const lastCallRef = React.useRef(0);
|
||||||
|
const mountedRef = React.useRef(false);
|
||||||
|
|
||||||
|
// Load projects from API with enhanced duplicate prevention
|
||||||
|
const loadProjects = React.useCallback(async (page = currentPage, size = pageSize) => {
|
||||||
|
const now = Date.now();
|
||||||
|
const timeSinceLastCall = now - lastCallRef.current;
|
||||||
|
|
||||||
|
// Prevent duplicate API calls within 500ms
|
||||||
|
if (loadingRef.current || timeSinceLastCall < 500) {
|
||||||
|
console.log('🚫 API call blocked - already in progress or too soon:', {
|
||||||
|
loading: loadingRef.current,
|
||||||
|
timeSinceLastCall,
|
||||||
|
mounted: mountedRef.current
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only proceed if component is mounted
|
||||||
|
if (!mountedRef.current) {
|
||||||
|
console.log('🚫 Component not mounted, skipping API call');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastCallRef.current = now;
|
||||||
|
loadingRef.current = true;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const apiBaseUrl = process.env.REACT_APP_API_BASE_URL || '';
|
const apiBaseUrl = process.env.REACT_APP_API_BASE_URL || '';
|
||||||
console.log('Loading projects from:', `${apiBaseUrl}Projects`);
|
console.log('📡 Loading projects from:', `${apiBaseUrl}Projects?page=${page}&pageSize=${size}`);
|
||||||
|
|
||||||
const response = await fetch(`${apiBaseUrl}Projects?page=${page}&pageSize=${size}`, {
|
const response = await fetch(`${apiBaseUrl}Projects?page=${page}&pageSize=${size}`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
@ -55,7 +82,7 @@ const ProjectTracker = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
console.log('API Response:', result);
|
console.log('✅ API Response:', result);
|
||||||
|
|
||||||
if (result.data) {
|
if (result.data) {
|
||||||
// Map API data to table format
|
// Map API data to table format
|
||||||
@ -99,15 +126,20 @@ const ProjectTracker = () => {
|
|||||||
setTotalPages(1);
|
setTotalPages(1);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading projects:', error);
|
console.error('💥 Error loading projects:', error);
|
||||||
// Set empty data on error
|
// Only update state if component is still mounted
|
||||||
setProjectData([]);
|
if (mountedRef.current) {
|
||||||
setTotalCount(0);
|
setProjectData([]);
|
||||||
setTotalPages(1);
|
setTotalCount(0);
|
||||||
|
setTotalPages(1);
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
if (mountedRef.current) {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
loadingRef.current = false; // Reset loading ref
|
||||||
}
|
}
|
||||||
};
|
}, [currentPage, pageSize]); // Add dependencies for useCallback
|
||||||
|
|
||||||
// Helper functions for mapping
|
// Helper functions for mapping
|
||||||
const getCategoryColor = (categoryName) => {
|
const getCategoryColor = (categoryName) => {
|
||||||
@ -188,6 +220,12 @@ const ProjectTracker = () => {
|
|||||||
|
|
||||||
// Delete project function
|
// Delete project function
|
||||||
const handleDeleteProject = async (projectId) => {
|
const handleDeleteProject = async (projectId) => {
|
||||||
|
// Prevent multiple delete operations
|
||||||
|
if (loading || loadingRef.current) {
|
||||||
|
console.log('🚫 Operation already in progress, ignoring delete request');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: 'Xác nhận xóa dự án',
|
title: 'Xác nhận xóa dự án',
|
||||||
content: 'Bạn có chắc chắn muốn xóa dự án này không? Hành động này không thể hoàn tác.',
|
content: 'Bạn có chắc chắn muốn xóa dự án này không? Hành động này không thể hoàn tác.',
|
||||||
@ -260,34 +298,47 @@ const ProjectTracker = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Load data on component mount
|
// Mount/unmount management
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
mountedRef.current = true;
|
||||||
|
console.log('🚀 Component mounted - loading projects');
|
||||||
|
|
||||||
|
// Load projects on mount
|
||||||
loadProjects();
|
loadProjects();
|
||||||
}, []);
|
|
||||||
|
// Cleanup on unmount
|
||||||
|
return () => {
|
||||||
|
console.log('🔄 Component unmounting - cleaning up');
|
||||||
|
mountedRef.current = false;
|
||||||
|
loadingRef.current = false;
|
||||||
|
lastCallRef.current = 0;
|
||||||
|
};
|
||||||
|
}, [loadProjects]); // Include loadProjects in dependencies
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Handle pagination change
|
// Handle pagination change
|
||||||
const handlePageChange = (page) => {
|
const handlePageChange = (page) => {
|
||||||
setCurrentPage(page);
|
if (page !== currentPage && !loading) {
|
||||||
loadProjects(page, pageSize);
|
setCurrentPage(page);
|
||||||
|
loadProjects(page, pageSize);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle page size change
|
// Handle page size change
|
||||||
const handlePageSizeChange = (newPageSize) => {
|
const handlePageSizeChange = (newPageSize) => {
|
||||||
setPageSize(newPageSize);
|
if (newPageSize !== pageSize && !loading) {
|
||||||
setCurrentPage(1); // Reset to first page when changing page size
|
setPageSize(newPageSize);
|
||||||
loadProjects(1, newPageSize);
|
setCurrentPage(1); // Reset to first page when changing page size
|
||||||
|
loadProjects(1, newPageSize);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle table change (for Ant Design Table)
|
// Handle table change (for Ant Design Table) - DISABLED to prevent double calls
|
||||||
const handleTableChange = (paginationInfo) => {
|
const handleTableChange = () => {
|
||||||
if (paginationInfo.current !== currentPage) {
|
// Disabled to prevent duplicate API calls since we use CustomPagination
|
||||||
handlePageChange(paginationInfo.current);
|
// The CustomPagination component handles all pagination logic
|
||||||
}
|
console.log('Table change event ignored to prevent duplicate API calls');
|
||||||
if (paginationInfo.pageSize !== pageSize) {
|
|
||||||
handlePageSizeChange(paginationInfo.pageSize);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -345,7 +396,7 @@ const ProjectTracker = () => {
|
|||||||
dataIndex: 'manager',
|
dataIndex: 'manager',
|
||||||
key: 'manager',
|
key: 'manager',
|
||||||
render: (managers) => (
|
render: (managers) => (
|
||||||
<Avatar.Group maxCount={2} size="small">
|
<Avatar.Group max={{ count: 2 }} size="small">
|
||||||
{managers.map((manager, index) => (
|
{managers.map((manager, index) => (
|
||||||
<Avatar
|
<Avatar
|
||||||
key={index}
|
key={index}
|
||||||
|
|||||||
@ -54,14 +54,11 @@ initializeTheme();
|
|||||||
if (rootElement) {
|
if (rootElement) {
|
||||||
const root = ReactDOM.createRoot(rootElement);
|
const root = ReactDOM.createRoot(rootElement);
|
||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
|
||||||
<Provider store={store} >
|
<Provider store={store} >
|
||||||
<BrowserRouter basename={process.env.PUBLIC_URL}>
|
<BrowserRouter basename={process.env.PUBLIC_URL}>
|
||||||
<AllRoutes />
|
<AllRoutes />
|
||||||
|
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</Provider>
|
</Provider>
|
||||||
</React.StrictMode>
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
console.error("Element with id 'root' not found.");
|
console.error("Element with id 'root' not found.");
|
||||||
|
|||||||
1044
src/style/css/calendar-custom.css
Normal file
1044
src/style/css/calendar-custom.css
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user