+ {/* Header */}
+
+
+
+
onView(project)}
+ >
+ {project.name}
+
+
+
+ {/* Actions Dropdown */}
+
+
+ {showMenu && (
+ <>
+
setShowMenu(false)} />
+
+
+
+
+
+ >
+ )}
+
+
+
+ {/* Description */}
+
+ {project.description}
+
+
+ {/* Progress Bar */}
+
+
+ Progress
+ {project.completion}%
+
+
+
+
+ {/* Meta Info */}
+
+
+
+
+ {new Date(project.deadline).toLocaleDateString()}
+
+
+
+
+ {project.team?.length || 0} members
+
+
+
+ {/* Budget */}
+ {project.budget && (
+
+
+
+
+ ${(project.budget / 1000).toFixed(0)}K
+
+
+
+ )}
+
+ {/* Status Badge */}
+
+
+ );
+};
+
+ProjectCard.displayName = 'ProjectCard';
diff --git a/src/components/projects/ProjectFilters.jsx b/src/components/projects/ProjectFilters.jsx
new file mode 100644
index 00000000..a51dfafd
--- /dev/null
+++ b/src/components/projects/ProjectFilters.jsx
@@ -0,0 +1,96 @@
+import React from 'react';
+import {
+ MagnifyingGlassIcon,
+ ChevronUpDownIcon,
+ Squares2X2Icon,
+ ListBulletIcon,
+} from '@heroicons/react/24/outline';
+
+export const ProjectFilters = ({
+ searchTerm,
+ onSearchChange,
+ filterStatus,
+ onStatusChange,
+ filterPriority,
+ onPriorityChange,
+ onSort,
+ sortDirection,
+ viewMode,
+ onViewModeChange,
+}) => {
+ return (
+
+
+ {/* Search */}
+
+
+ onSearchChange(e.target.value)}
+ className="w-full pl-10 sm:pl-12 pr-3 sm:pr-4 py-2.5 sm:py-3 text-sm sm:text-base bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all"
+ />
+
+
+ {/* Filters Row */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+ProjectFilters.displayName = 'ProjectFilters';
diff --git a/src/components/projects/ProjectModal.jsx b/src/components/projects/ProjectModal.jsx
new file mode 100644
index 00000000..002e4b3e
--- /dev/null
+++ b/src/components/projects/ProjectModal.jsx
@@ -0,0 +1,181 @@
+import React from 'react';
+import { XMarkIcon } from '@heroicons/react/24/outline';
+
+export const ProjectModal = ({ isOpen, onClose, project, onSave }) => {
+ const [formData, setFormData] = React.useState({
+ name: '',
+ description: '',
+ status: 'active',
+ priority: 'medium',
+ completion: 0,
+ deadline: '',
+ budget: '',
+ });
+
+ React.useEffect(() => {
+ if (project) {
+ setFormData({
+ name: project.name || '',
+ description: project.description || '',
+ status: project.status || 'active',
+ priority: project.priority || 'medium',
+ completion: project.completion || 0,
+ deadline: project.deadline || '',
+ budget: project.budget || '',
+ });
+ } else {
+ setFormData({
+ name: '',
+ description: '',
+ status: 'active',
+ priority: 'medium',
+ completion: 0,
+ deadline: '',
+ budget: '',
+ });
+ }
+ }, [project, isOpen]);
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ if (!formData.name) return;
+ onSave({ ...project, ...formData, id: project?.id || Date.now() });
+ onClose();
+ };
+
+ if (!isOpen) return null;
+
+ return (
+
+
+
+
+ {project ? 'Edit Project' : 'New Project'}
+
+
+
+
+
+ {/* Project Name */}
+
+
+ setFormData({ ...formData, name: e.target.value })}
+ className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
+ placeholder="Enter project name"
+ />
+
+
+ {/* Description */}
+
+
+
+
+ {/* Status & Priority */}
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Completion & Deadline */}
+
+
+ {/* Budget */}
+
+
+ setFormData({ ...formData, budget: parseInt(e.target.value) || 0 })}
+ className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
+ placeholder="Enter budget amount"
+ />
+
+
+ {/* Actions */}
+
+
+
+
+
+
+
+ );
+};
+
+ProjectModal.displayName = 'ProjectModal';
diff --git a/src/components/projects/ProjectsStats.jsx b/src/components/projects/ProjectsStats.jsx
new file mode 100644
index 00000000..33dc68a1
--- /dev/null
+++ b/src/components/projects/ProjectsStats.jsx
@@ -0,0 +1,68 @@
+import React from 'react';
+import {
+ FolderIcon,
+ RocketLaunchIcon,
+ CheckBadgeIcon,
+ ChartBarSquareIcon,
+} from '@heroicons/react/24/outline';
+
+export const ProjectsStats = ({ projects }) => {
+ const stats = React.useMemo(() => {
+ const total = projects.length;
+ const active = projects.filter((p) => p.status === 'active').length;
+ const completed = projects.filter((p) => p.status === 'completed').length;
+ const avgCompletion =
+ total > 0 ? Math.round(projects.reduce((sum, p) => sum + p.completion, 0) / total) : 0;
+
+ return [
+ {
+ label: 'Total Projects',
+ value: total,
+ icon: FolderIcon,
+ color: 'from-blue-500 to-cyan-500',
+ },
+ {
+ label: 'Active',
+ value: active,
+ icon: RocketLaunchIcon,
+ color: 'from-green-500 to-emerald-500',
+ },
+ {
+ label: 'Completed',
+ value: completed,
+ icon: CheckBadgeIcon,
+ color: 'from-purple-500 to-pink-500',
+ },
+ {
+ label: 'Avg Completion',
+ value: `${avgCompletion}%`,
+ icon: ChartBarSquareIcon,
+ color: 'from-orange-500 to-amber-500',
+ },
+ ];
+ }, [projects]);
+
+ return (
+
+ {stats.map((stat, index) => {
+ const Icon = stat.icon;
+ return (
+
+
+
+
+
{stat.label}
+
{stat.value}
+
+ );
+ })}
+
+ );
+};
+
+ProjectsStats.displayName = 'ProjectsStats';
diff --git a/src/components/projects/index.js b/src/components/projects/index.js
new file mode 100644
index 00000000..b23ed120
--- /dev/null
+++ b/src/components/projects/index.js
@@ -0,0 +1,4 @@
+export { ProjectsStats } from './ProjectsStats';
+export { ProjectCard } from './ProjectCard';
+export { ProjectFilters } from './ProjectFilters';
+export { ProjectModal } from './ProjectModal';
diff --git a/src/components/team/MemberCard.jsx b/src/components/team/MemberCard.jsx
new file mode 100644
index 00000000..8953dbf8
--- /dev/null
+++ b/src/components/team/MemberCard.jsx
@@ -0,0 +1,168 @@
+import React from 'react';
+import { Card, Avatar, Badge, Button } from '../ui';
+import { MEMBER_STATUS_COLORS } from '../../utils/';
+import { Typography, IconButton } from '@material-tailwind/react';
+import {
+ EnvelopeIcon,
+ PhoneIcon,
+ MapPinIcon,
+ PencilIcon,
+ TrashIcon,
+} from '@heroicons/react/24/outline';
+import { StarIcon as StarSolidIcon } from '@heroicons/react/24/solid';
+
+export const MemberCard = React.memo(
+ ({ member, onEdit, onDelete, onViewDetails, compact = false }) => {
+ if (compact) {
+ return (
+
+
+
+
+
+
+ {member.name}
+
+
+ {member.role}
+
+
+
+ {member.status}
+
+
+
+
+ );
+ }
+
+ return (
+
+
+ {/* Header with Actions */}
+
+
+
+
+
+ {member.name}
+
+
+ {member.role}
+
+
+ {member.status}
+
+
+
+
+
+
onEdit?.(member)}>
+
+
+
onDelete?.(member.id)}
+ >
+
+
+
+
+
+ {/* Bio */}
+ {member.bio && (
+
+ {member.bio}
+
+ )}
+
+ {/* Contact Info */}
+
+ {member.email && (
+
+
+
+ {member.email}
+
+
+ )}
+ {member.phone && (
+
+ )}
+ {member.location && (
+
+
+ {member.location}
+
+ )}
+
+
+ {/* Stats */}
+
+
+
+ {member.tasksCompleted || 0}
+
+
+ Tasks
+
+
+
+
+ {member.hoursWorked || 0}
+
+
+ Hours
+
+
+
+
+
+ {member.rating || 0}
+
+
+
+
+ Rating
+
+
+
+
+ {/* Department & Projects */}
+
+
+
+ Department
+
+
+ {member.department}
+
+
+
+
+ Projects
+
+
+ {member.projects?.length || 0}
+
+
+
+
+ {/* View Details Button */}
+ {onViewDetails && (
+
+ )}
+
+
+ );
+ }
+);
+
+MemberCard.displayName = 'MemberCard';
diff --git a/src/components/team/MemberStatusBadge.jsx b/src/components/team/MemberStatusBadge.jsx
new file mode 100644
index 00000000..5697c78a
--- /dev/null
+++ b/src/components/team/MemberStatusBadge.jsx
@@ -0,0 +1,22 @@
+import React from 'react';
+import { Badge } from '../ui';
+import { MEMBER_STATUS_COLORS } from '../../utils';
+
+export const MemberStatusBadge = React.memo(({ status, size = 'sm', className = '' }) => {
+ const statusConfig = {
+ online: { label: 'Online', color: 'green' },
+ offline: { label: 'Offline', color: 'gray' },
+ away: { label: 'Away', color: 'yellow' },
+ busy: { label: 'Busy', color: 'red' },
+ };
+
+ const config = statusConfig[status] || statusConfig.offline;
+
+ return (
+
+ {config.label}
+
+ );
+});
+
+MemberStatusBadge.displayName = 'MemberStatusBadge';
diff --git a/src/components/team/TeamMemberRow.jsx b/src/components/team/TeamMemberRow.jsx
new file mode 100644
index 00000000..113d6a74
--- /dev/null
+++ b/src/components/team/TeamMemberRow.jsx
@@ -0,0 +1,90 @@
+import React from 'react';
+import { Avatar, Badge, Tooltip } from '../ui';
+import { formatDate, MEMBER_STATUS_COLORS } from '../../utils';
+import { PencilIcon, TrashIcon } from '@heroicons/react/24/outline';
+import { Typography, IconButton } from '@material-tailwind/react';
+
+export const TeamMemberRow = React.memo(({ member, onEdit, onDelete }) => {
+ return (
+
+ {/* Member */}
+
+
+
+
+
+ {member.name}
+
+ {member.location && (
+
+ {member.location}
+
+ )}
+
+
+ |
+
+ {/* Email */}
+
+
+ {member.email}
+
+ |
+
+ {/* Role */}
+
+
+ {member.role}
+
+ |
+
+ {/* Department */}
+
+
+ {member.department}
+
+ |
+
+ {/* Status */}
+
+
+ {member.status}
+
+ |
+
+ {/* Join Date */}
+
+
+ {formatDate(member.joinDate, 'short')}
+
+ |
+
+ {/* Actions */}
+
+
+
+ onEdit?.(member)}>
+
+
+
+
+ {
+ if (window.confirm(`Remove ${member.name} from the team?`)) {
+ onDelete?.(member.id);
+ }
+ }}
+ >
+
+
+
+
+ |
+
+ );
+});
+
+TeamMemberRow.displayName = 'TeamMemberRow';
diff --git a/src/components/team/TeamStats.jsx b/src/components/team/TeamStats.jsx
new file mode 100644
index 00000000..95617115
--- /dev/null
+++ b/src/components/team/TeamStats.jsx
@@ -0,0 +1,109 @@
+import React from 'react';
+import { Card } from '../ui';
+import { CompactStatCard } from '../dashboard';
+import { Typography } from '@material-tailwind/react';
+import { UsersIcon, UserPlusIcon, ChartBarIcon, ClockIcon } from '@heroicons/react/24/outline';
+
+export const TeamStats = React.memo(({ statistics }) => {
+ if (!statistics) return null;
+
+ const stats = [
+ {
+ label: 'Total Members',
+ value: statistics.total || 0,
+ icon: UsersIcon,
+ color: 'blue',
+ trend: '+2 this month',
+ },
+ {
+ label: 'Online Now',
+ value: statistics.onlineCount || 0,
+ icon: UserPlusIcon,
+ color: 'green',
+ trend: `${statistics.activePercentage || 0}% active`,
+ },
+ {
+ label: 'Tasks Completed',
+ value: statistics.totalTasksCompleted || 0,
+ icon: ChartBarIcon,
+ color: 'purple',
+ },
+ {
+ label: 'Hours Worked',
+ value: `${Math.round((statistics.totalHoursWorked || 0) / 1000)}k`,
+ icon: ClockIcon,
+ color: 'orange',
+ },
+ ];
+
+ return (
+
+ {/* Quick Stats */}
+
+ {stats.map((stat, idx) => (
+
+ ))}
+
+
+ {/* Department Breakdown */}
+
+
+
+ Department Distribution
+
+
+ {statistics.byDepartment?.map((dept) => (
+
+
+
+
+ {dept.department}
+
+
+
+
+ ))}
+
+
+
+
+ {/* Status Distribution */}
+
+
+
+ Member Status
+
+
+ {statistics.byStatus?.map((status) => (
+
+
+ {status.count}
+
+
+ {status.status}
+
+
+ ))}
+
+
+
+
+ );
+});
+
+TeamStats.displayName = 'TeamStats';
diff --git a/src/components/team/TeamTable.jsx b/src/components/team/TeamTable.jsx
new file mode 100644
index 00000000..94ba05ad
--- /dev/null
+++ b/src/components/team/TeamTable.jsx
@@ -0,0 +1,88 @@
+import React from 'react';
+import { Card, Button, EmptyState } from '../ui';
+import { TeamMemberRow } from './';
+import { PlusIcon, UsersIcon } from '@heroicons/react/24/outline';
+import { Typography } from '@material-tailwind/react';
+
+const TABLE_HEADERS = ['Member', 'Email', 'Role', 'Department', 'Status', 'Join Date', 'Actions'];
+
+export const TeamTable = React.memo(
+ ({ members = [], onAddMember, onEditMember, onDeleteMember, loading = false }) => {
+ if (loading) {
+ return (
+
+
+
+ {[...Array(5)].map((_, i) => (
+
+ ))}
+
+
+
+ );
+ }
+
+ return (
+
+ {/* Header */}
+
+
+
+
+ Team Members
+
+
+ Manage your team and their roles
+
+
+
+
+
+
+ {/* Table */}
+
+ {members.length === 0 ? (
+
+ Add First Member
+
+ }
+ />
+ ) : (
+
+
+
+ {TABLE_HEADERS.map((header) => (
+ |
+
+ {header}
+
+ |
+ ))}
+
+
+
+ {members.map((member) => (
+
+ ))}
+
+
+ )}
+
+
+ );
+ }
+);
+
+TeamTable.displayName = 'TeamTable';
diff --git a/src/components/team/index.js b/src/components/team/index.js
new file mode 100644
index 00000000..323036ca
--- /dev/null
+++ b/src/components/team/index.js
@@ -0,0 +1,5 @@
+export { TeamStats } from './TeamStats';
+export { MemberCard } from './MemberCard';
+export { MemberStatusBadge } from './MemberStatusBadge';
+export { TeamMemberRow } from './TeamMemberRow';
+export { TeamTable } from './TeamTable';
diff --git a/src/components/ui/Avatar.jsx b/src/components/ui/Avatar.jsx
new file mode 100644
index 00000000..76988097
--- /dev/null
+++ b/src/components/ui/Avatar.jsx
@@ -0,0 +1,47 @@
+import React from 'react';
+import { Avatar as MTAvatar } from '@material-tailwind/react';
+
+export const Avatar = React.forwardRef(
+ (
+ { src, alt = '', size = 'md', variant = 'circular', className = '', fallback, ...props },
+ ref
+ ) => {
+ const [imgError, setImgError] = React.useState(false);
+
+ const sizeClasses = {
+ xs: 'h-6 w-6 text-xs',
+ sm: 'h-9 w-9 text-sm',
+ md: 'h-12 w-12 text-base',
+ lg: 'h-16 w-16 text-lg',
+ xl: 'h-20 w-20 text-xl',
+ xxl: 'h-32 w-32 text-2xl',
+ };
+
+ if (imgError || !src) {
+ return (
+
+ {fallback || alt.charAt(0).toUpperCase()}
+
+ );
+ }
+
+ return (
+
setImgError(true)}
+ {...props}
+ />
+ );
+ }
+);
+
+Avatar.displayName = 'Avatar';
diff --git a/src/components/ui/Badge.jsx b/src/components/ui/Badge.jsx
new file mode 100644
index 00000000..c4083d66
--- /dev/null
+++ b/src/components/ui/Badge.jsx
@@ -0,0 +1,28 @@
+import React from 'react';
+import { Chip } from '@material-tailwind/react';
+
+export const Badge = React.memo(
+ ({
+ children,
+ variant = 'filled',
+ color = 'blue',
+ size = 'sm',
+ icon: Icon,
+ className = '',
+ ...props
+ }) => {
+ return (
+ }
+ className={`capitalize ${className}`}
+ {...props}
+ />
+ );
+ }
+);
+
+Badge.displayName = 'Badge';
diff --git a/src/components/ui/Button.jsx b/src/components/ui/Button.jsx
new file mode 100644
index 00000000..38791af4
--- /dev/null
+++ b/src/components/ui/Button.jsx
@@ -0,0 +1,45 @@
+import React from 'react';
+import { Button as MTButton } from '@material-tailwind/react';
+
+export const Button = React.memo(
+ ({
+ children,
+ variant = 'filled',
+ color = 'blue',
+ size = 'md',
+ icon: Icon,
+ iconPosition = 'left',
+ loading = false,
+ disabled = false,
+ fullWidth = false,
+ className = '',
+ onClick,
+ ...props
+ }) => {
+ return (
+
+ {loading ? (
+
+ ) : (
+ <>
+ {Icon && iconPosition === 'left' && }
+ {children}
+ {Icon && iconPosition === 'right' && }
+ >
+ )}
+
+ );
+ }
+);
+
+Button.displayName = 'Button';
diff --git a/src/components/ui/Card.jsx b/src/components/ui/Card.jsx
new file mode 100644
index 00000000..07c1ebf0
--- /dev/null
+++ b/src/components/ui/Card.jsx
@@ -0,0 +1,28 @@
+import React from 'react';
+import { Card as MTCard, CardHeader, CardBody, CardFooter } from '@material-tailwind/react';
+
+export const Card = React.memo(
+ ({
+ children,
+ header,
+ footer,
+ className = '',
+ bodyClassName = '',
+ variant = 'filled',
+ ...props
+ }) => {
+ return (
+
+ {header && (
+
+ {header}
+
+ )}
+ {children}
+ {footer && {footer}}
+
+ );
+ }
+);
+
+Card.displayName = 'Card';
diff --git a/src/components/ui/EmptyState.jsx b/src/components/ui/EmptyState.jsx
new file mode 100644
index 00000000..bca8055f
--- /dev/null
+++ b/src/components/ui/EmptyState.jsx
@@ -0,0 +1,25 @@
+import React from 'react';
+import { Typography } from '@material-tailwind/react';
+
+export const EmptyState = React.memo(
+ ({ icon: Icon, title, description, action, className = '' }) => {
+ return (
+
+ {Icon && }
+ {title && (
+
+ {title}
+
+ )}
+ {description && (
+
+ {description}
+
+ )}
+ {action}
+
+ );
+ }
+);
+
+EmptyState.displayName = 'EmptyState';
diff --git a/src/components/ui/Input.jsx b/src/components/ui/Input.jsx
new file mode 100644
index 00000000..8d2d1555
--- /dev/null
+++ b/src/components/ui/Input.jsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import { Input as MTInput } from '@material-tailwind/react';
+
+export const Input = React.memo(
+ ({
+ label,
+ type = 'text',
+ value,
+ onChange,
+ placeholder,
+ error,
+ helperText,
+ icon: Icon,
+ disabled = false,
+ required = false,
+ className = '',
+ ...props
+ }) => {
+ return (
+
+
}
+ disabled={disabled}
+ error={error}
+ required={required}
+ className={className}
+ {...props}
+ />
+ {helperText && (
+
{helperText}
+ )}
+
+ );
+ }
+);
+
+Input.displayName = 'Input';
diff --git a/src/components/ui/Spinner.jsx b/src/components/ui/Spinner.jsx
new file mode 100644
index 00000000..b339f445
--- /dev/null
+++ b/src/components/ui/Spinner.jsx
@@ -0,0 +1,26 @@
+import React from 'react';
+
+export const Spinner = React.memo(({ size = 'md', color = 'blue', className = '' }) => {
+ const sizeClasses = {
+ sm: 'h-4 w-4 border-2',
+ md: 'h-8 w-8 border-3',
+ lg: 'h-12 w-12 border-4',
+ };
+
+ const colorClasses = {
+ blue: 'border-blue-600 border-t-transparent',
+ green: 'border-green-600 border-t-transparent',
+ red: 'border-red-600 border-t-transparent',
+ white: 'border-white border-t-transparent',
+ };
+
+ return (
+
+ );
+});
+
+Spinner.displayName = 'Spinner';
diff --git a/src/components/ui/Tooltip.jsx b/src/components/ui/Tooltip.jsx
new file mode 100644
index 00000000..7ec9dd10
--- /dev/null
+++ b/src/components/ui/Tooltip.jsx
@@ -0,0 +1,16 @@
+import React from 'react';
+import { Tooltip as MTTooltip } from '@material-tailwind/react';
+
+export const Tooltip = React.memo(
+ ({ children, content, placement = 'top', className = '', ...props }) => {
+ if (!content) return children;
+
+ return (
+
+ {children}
+
+ );
+ }
+);
+
+Tooltip.displayName = 'Tooltip';
diff --git a/src/components/ui/index.js b/src/components/ui/index.js
new file mode 100644
index 00000000..4a0043e9
--- /dev/null
+++ b/src/components/ui/index.js
@@ -0,0 +1,9 @@
+// src/components/ui/index.js
+export { Button } from './Button';
+export { Input } from './Input';
+export { Card } from './Card';
+export { Avatar } from './Avatar';
+export { Badge } from './Badge';
+export { Tooltip } from './Tooltip';
+export { Spinner } from './Spinner';
+export { EmptyState } from './EmptyState';
diff --git a/src/configs/charts-config.js b/src/configs/charts-config.js
deleted file mode 100644
index 7c2676dd..00000000
--- a/src/configs/charts-config.js
+++ /dev/null
@@ -1,61 +0,0 @@
-export const chartsConfig = {
- chart: {
- toolbar: {
- show: false,
- },
- },
- title: {
- show: "",
- },
- dataLabels: {
- enabled: false,
- },
- xaxis: {
- axisTicks: {
- show: false,
- },
- axisBorder: {
- show: false,
- },
- labels: {
- style: {
- colors: "#37474f",
- fontSize: "13px",
- fontFamily: "inherit",
- fontWeight: 300,
- },
- },
- },
- yaxis: {
- labels: {
- style: {
- colors: "#37474f",
- fontSize: "13px",
- fontFamily: "inherit",
- fontWeight: 300,
- },
- },
- },
- grid: {
- show: true,
- borderColor: "#dddddd",
- strokeDashArray: 5,
- xaxis: {
- lines: {
- show: true,
- },
- },
- padding: {
- top: 5,
- right: 20,
- },
- },
- fill: {
- opacity: 0.8,
- },
- tooltip: {
- theme: "dark",
- },
-};
-
-export default chartsConfig;
diff --git a/src/configs/index.js b/src/configs/index.js
deleted file mode 100644
index 5bd9296e..00000000
--- a/src/configs/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export * from "@/configs/charts-config";
diff --git a/src/context/AuthContext.jsx b/src/context/AuthContext.jsx
new file mode 100644
index 00000000..1b5dd08c
--- /dev/null
+++ b/src/context/AuthContext.jsx
@@ -0,0 +1,72 @@
+import React from 'react';
+import { useLocalStorage } from '../hooks';
+import {
+ loginWithEmail,
+ signupWithEmail,
+ loginWithGoogle,
+ loginWithGithub,
+ logoutUser,
+} from '../lib/authService';
+
+const AuthContext = React.createContext(null);
+
+export const AuthProvider = ({ children }) => {
+ const [user, setUser] = useLocalStorage('dashboard_user', null);
+ const [loading, setLoading] = React.useState(false);
+
+ const login = async ({ email, password }) => {
+ setLoading(true);
+ try {
+ const res = await loginWithEmail({ email, password });
+ setUser(res.user);
+ return { success: true, user: res.user };
+ } catch (e) {
+ return { success: false, error: e.message };
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const signup = async ({ email, password }) => {
+ setLoading(true);
+ try {
+ const res = await signupWithEmail({ email, password });
+ setUser(res.user);
+ return { success: true, user: res.user };
+ } catch (e) {
+ return { success: false, error: e.message };
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const googleLogin = async () => {
+ const res = await loginWithGoogle();
+ setUser(res.user);
+ };
+
+ const githubLogin = async () => {
+ const res = await loginWithGithub();
+ setUser(res.user);
+ };
+
+ const logout = async () => {
+ await logoutUser();
+ setUser(null);
+ };
+
+ const value = {
+ user,
+ loading,
+ isAuthenticated: !!user,
+ login,
+ signup,
+ googleLogin,
+ githubLogin,
+ logout,
+ };
+
+ return {children};
+};
+
+export const useAuth = () => React.useContext(AuthContext);
diff --git a/src/context/DashborardContext.jsx b/src/context/DashborardContext.jsx
new file mode 100644
index 00000000..4c7f74f8
--- /dev/null
+++ b/src/context/DashborardContext.jsx
@@ -0,0 +1,43 @@
+import React from 'react';
+
+const DashboardContext = React.createContext(null);
+
+export const DashboardProvider = ({ children }) => {
+ const [globalState, setGlobalState] = React.useState({
+ sidebarOpen: true,
+ theme: 'light',
+ lastUpdated: new Date(),
+ });
+
+ const updateGlobalState = React.useCallback((updates) => {
+ setGlobalState((prev) => ({ ...prev, ...updates }));
+ }, []);
+
+ const toggleSidebar = React.useCallback(() => {
+ setGlobalState((prev) => ({ ...prev, sidebarOpen: !prev.sidebarOpen }));
+ }, []);
+
+ const setTheme = React.useCallback((theme) => {
+ setGlobalState((prev) => ({ ...prev, theme }));
+ }, []);
+
+ const value = React.useMemo(
+ () => ({
+ ...globalState,
+ updateGlobalState,
+ toggleSidebar,
+ setTheme,
+ }),
+ [globalState, updateGlobalState, toggleSidebar, setTheme]
+ );
+
+ return {children};
+};
+
+export const useDashboard = () => {
+ const context = React.useContext(DashboardContext);
+ if (!context) {
+ throw new Error('useDashboard must be used within DashboardProvider');
+ }
+ return context;
+};
diff --git a/src/context/ThemeContext.jsx b/src/context/ThemeContext.jsx
new file mode 100644
index 00000000..db0232e5
--- /dev/null
+++ b/src/context/ThemeContext.jsx
@@ -0,0 +1,102 @@
+import React from 'react';
+import { useLocalStorage } from '../hooks';
+
+const ThemeContext = React.createContext(null);
+
+export const ThemeProvider = ({ children }) => {
+ const [theme, setTheme] = useLocalStorage('dashboard_theme', 'light');
+ const [colorScheme, setColorScheme] = useLocalStorage('dashboard_color_scheme', 'blue');
+
+ // Apply theme to document
+ React.useEffect(() => {
+ const root = document.documentElement;
+
+ if (theme === 'dark') {
+ root.classList.add('dark');
+ } else {
+ root.classList.remove('dark');
+ }
+ }, [theme]);
+
+ // Toggle theme
+ const toggleTheme = React.useCallback(() => {
+ setTheme((prev) => (prev === 'light' ? 'dark' : 'light'));
+ }, [setTheme]);
+
+ // Set specific theme
+ const setThemeMode = React.useCallback(
+ (mode) => {
+ if (['light', 'dark', 'auto'].includes(mode)) {
+ setTheme(mode);
+ }
+ },
+ [setTheme]
+ );
+
+ // Change color scheme
+ const changeColorScheme = React.useCallback(
+ (scheme) => {
+ setColorScheme(scheme);
+ },
+ [setColorScheme]
+ );
+
+ // Get system preference
+ const getSystemTheme = React.useCallback(() => {
+ if (typeof window !== 'undefined' && window.matchMedia) {
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
+ }
+ return 'light';
+ }, []);
+
+ // Auto theme based on system
+ React.useEffect(() => {
+ if (theme === 'auto') {
+ const systemTheme = getSystemTheme();
+ const root = document.documentElement;
+
+ if (systemTheme === 'dark') {
+ root.classList.add('dark');
+ } else {
+ root.classList.remove('dark');
+ }
+
+ // Listen for system theme changes
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
+ const handleChange = (e) => {
+ if (theme === 'auto') {
+ if (e.matches) {
+ root.classList.add('dark');
+ } else {
+ root.classList.remove('dark');
+ }
+ }
+ };
+
+ mediaQuery.addEventListener('change', handleChange);
+ return () => mediaQuery.removeEventListener('change', handleChange);
+ }
+ }, [theme, getSystemTheme]);
+
+ const value = React.useMemo(
+ () => ({
+ theme,
+ colorScheme,
+ toggleTheme,
+ setThemeMode,
+ changeColorScheme,
+ isDark: theme === 'dark' || (theme === 'auto' && getSystemTheme() === 'dark'),
+ }),
+ [theme, colorScheme, toggleTheme, setThemeMode, changeColorScheme, getSystemTheme]
+ );
+
+ return {children};
+};
+
+export const useTheme = () => {
+ const context = React.useContext(ThemeContext);
+ if (!context) {
+ throw new Error('useTheme must be used within ThemeProvider');
+ }
+ return context;
+};
diff --git a/src/context/index.js b/src/context/index.js
new file mode 100644
index 00000000..a1dc2460
--- /dev/null
+++ b/src/context/index.js
@@ -0,0 +1,3 @@
+export { AuthProvider, useAuth } from './AuthContext';
+export { DashboardProvider, useDashboard } from './DashborardContext';
+export { ThemeProvider, useTheme } from './ThemeContext';
diff --git a/src/context/index.jsx b/src/context/index.jsx
deleted file mode 100644
index 653a362d..00000000
--- a/src/context/index.jsx
+++ /dev/null
@@ -1,85 +0,0 @@
-import React from "react";
-import PropTypes from "prop-types";
-
-export const MaterialTailwind = React.createContext(null);
-MaterialTailwind.displayName = "MaterialTailwindContext";
-
-export function reducer(state, action) {
- switch (action.type) {
- case "OPEN_SIDENAV": {
- return { ...state, openSidenav: action.value };
- }
- case "SIDENAV_TYPE": {
- return { ...state, sidenavType: action.value };
- }
- case "SIDENAV_COLOR": {
- return { ...state, sidenavColor: action.value };
- }
- case "TRANSPARENT_NAVBAR": {
- return { ...state, transparentNavbar: action.value };
- }
- case "FIXED_NAVBAR": {
- return { ...state, fixedNavbar: action.value };
- }
- case "OPEN_CONFIGURATOR": {
- return { ...state, openConfigurator: action.value };
- }
- default: {
- throw new Error(`Unhandled action type: ${action.type}`);
- }
- }
-}
-
-export function MaterialTailwindControllerProvider({ children }) {
- const initialState = {
- openSidenav: false,
- sidenavColor: "dark",
- sidenavType: "white",
- transparentNavbar: true,
- fixedNavbar: false,
- openConfigurator: false,
- };
-
- const [controller, dispatch] = React.useReducer(reducer, initialState);
- const value = React.useMemo(
- () => [controller, dispatch],
- [controller, dispatch]
- );
-
- return (
-
- {children}
-
- );
-}
-
-export function useMaterialTailwindController() {
- const context = React.useContext(MaterialTailwind);
-
- if (!context) {
- throw new Error(
- "useMaterialTailwindController should be used inside the MaterialTailwindControllerProvider."
- );
- }
-
- return context;
-}
-
-MaterialTailwindControllerProvider.displayName = "/src/context/index.jsx";
-
-MaterialTailwindControllerProvider.propTypes = {
- children: PropTypes.node.isRequired,
-};
-
-export const setOpenSidenav = (dispatch, value) =>
- dispatch({ type: "OPEN_SIDENAV", value });
-export const setSidenavType = (dispatch, value) =>
- dispatch({ type: "SIDENAV_TYPE", value });
-export const setSidenavColor = (dispatch, value) =>
- dispatch({ type: "SIDENAV_COLOR", value });
-export const setTransparentNavbar = (dispatch, value) =>
- dispatch({ type: "TRANSPARENT_NAVBAR", value });
-export const setFixedNavbar = (dispatch, value) =>
- dispatch({ type: "FIXED_NAVBAR", value });
-export const setOpenConfigurator = (dispatch, value) =>
- dispatch({ type: "OPEN_CONFIGURATOR", value });
diff --git a/src/data/authors-table-data.js b/src/data/authors-table-data.js
deleted file mode 100644
index 7ae9d9cb..00000000
--- a/src/data/authors-table-data.js
+++ /dev/null
@@ -1,52 +0,0 @@
-export const authorsTableData = [
- {
- img: "/img/team-2.jpeg",
- name: "John Michael",
- email: "john@creative-tim.com",
- job: ["Manager", "Organization"],
- online: true,
- date: "23/04/18",
- },
- {
- img: "/img/team-1.jpeg",
- name: "Alexa Liras",
- email: "alexa@creative-tim.com",
- job: ["Programator", "Developer"],
- online: false,
- date: "11/01/19",
- },
- {
- img: "/img/team-4.jpeg",
- name: "Laurent Perrier",
- email: "laurent@creative-tim.com",
- job: ["Executive", "Projects"],
- online: true,
- date: "19/09/17",
- },
- {
- img: "/img/team-3.jpeg",
- name: "Michael Levi",
- email: "michael@creative-tim.com",
- job: ["Programator", "Developer"],
- online: true,
- date: "24/12/08",
- },
- {
- img: "/img/bruce-mars.jpeg",
- name: "Bruce Mars",
- email: "bruce@creative-tim.com",
- job: ["Manager", "Executive"],
- online: false,
- date: "04/10/21",
- },
- {
- img: "/img/team-2.jpeg",
- name: "Alexander",
- email: "alexander@creative-tim.com",
- job: ["Programator", "Developer"],
- online: false,
- date: "14/09/20",
- },
-];
-
-export default authorsTableData;
diff --git a/src/data/conversations-data.js b/src/data/conversations-data.js
deleted file mode 100644
index 2b488644..00000000
--- a/src/data/conversations-data.js
+++ /dev/null
@@ -1,29 +0,0 @@
-export const conversationsData = [
- {
- img: "/img/team-1.jpeg",
- name: "Sophie B.",
- message: "Hi! I need more information...",
- },
- {
- img: "/img/team-2.jpeg",
- name: "Alexander",
- message: "Awesome work, can you...",
- },
- {
- img: "/img/team-3.jpeg",
- name: "Ivanna",
- message: "About files I can...",
- },
- {
- img: "/img/team-4.jpeg",
- name: "Peterson",
- message: "Have a great afternoon...",
- },
- {
- img: "/img/bruce-mars.jpeg",
- name: "Bruce Mars",
- message: "Hi! I need more information...",
- },
-];
-
-export default conversationsData;
diff --git a/src/data/index.js b/src/data/index.js
index 7ca52a08..981b3a60 100644
--- a/src/data/index.js
+++ b/src/data/index.js
@@ -1,8 +1,37 @@
-export * from "@/data/statistics-cards-data";
-export * from "@/data/statistics-charts-data";
-export * from "@/data/projects-table-data";
-export * from "@/data/orders-overview-data";
-export * from "@/data/platform-settings-data";
-export * from "@/data/conversations-data";
-export * from "@/data/projects-data";
-export * from "@/data/authors-table-data";
+export {
+ initialProjects,
+ getProjectById,
+ getProjectsByStatus,
+ getProjectsByPriority,
+} from './initialProjects';
+export {
+ initialTeamMembers,
+ getTeamMemberById,
+ getTeamMembersByDepartment,
+ getTeamMembersByStatus,
+ getOnlineMembers,
+} from './initialTeamMembers';
+export {
+ initialNotifications,
+ getNotificationById,
+ getUnreadNotifications,
+ getNotificationsByType,
+ getNotificationsByCategory,
+ getRecentNotifications,
+} from './initialNotifications';
+export {
+ dashboardStatistics,
+ projectsByPriority,
+ projectsByStatus,
+ teamByDepartment,
+ budgetAllocation,
+ monthlyRevenue,
+ projectTimeline,
+ taskStatistics,
+ teamPerformance,
+ clientSatisfaction,
+ getTotalRevenue,
+ getTotalProfit,
+ getAverageClientSatisfaction,
+ getTaskCompletionRate,
+} from './statistics';
diff --git a/src/data/initialNotifications.js b/src/data/initialNotifications.js
new file mode 100644
index 00000000..0474f188
--- /dev/null
+++ b/src/data/initialNotifications.js
@@ -0,0 +1,230 @@
+export const initialNotifications = [
+ {
+ id: 1,
+ type: 'success',
+ title: 'Project Milestone Completed',
+ message:
+ 'CRM System Phase 2 has been completed successfully. All deliverables met quality standards.',
+ timestamp: new Date(Date.now() - 5 * 60 * 1000).toISOString(), // 5 minutes ago
+ read: false,
+ priority: 'high',
+ actionUrl: '/projects/1',
+ actionLabel: 'View Project',
+ category: 'project',
+ relatedEntity: {
+ type: 'project',
+ id: 1,
+ name: 'Enterprise CRM System',
+ },
+ },
+ {
+ id: 2,
+ type: 'warning',
+ title: 'Deadline Approaching',
+ message: 'E-commerce Platform deadline is in 7 days. Current completion: 90%',
+ timestamp: new Date(Date.now() - 60 * 60 * 1000).toISOString(), // 1 hour ago
+ read: false,
+ priority: 'high',
+ actionUrl: '/projects/3',
+ actionLabel: 'View Details',
+ category: 'deadline',
+ relatedEntity: {
+ type: 'project',
+ id: 3,
+ name: 'E-commerce Platform',
+ },
+ },
+ {
+ id: 3,
+ type: 'info',
+ title: 'New Team Member',
+ message: 'John Smith has joined the Engineering team',
+ timestamp: new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString(), // 2 hours ago
+ read: true,
+ priority: 'medium',
+ actionUrl: '/team',
+ actionLabel: 'View Team',
+ category: 'team',
+ relatedEntity: {
+ type: 'team',
+ id: 13,
+ name: 'John Smith',
+ },
+ },
+ {
+ id: 4,
+ type: 'error',
+ title: 'Budget Alert',
+ message: 'Mobile Banking App has exceeded 80% of allocated budget',
+ timestamp: new Date(Date.now() - 3 * 60 * 60 * 1000).toISOString(), // 3 hours ago
+ read: false,
+ priority: 'critical',
+ actionUrl: '/projects/2',
+ actionLabel: 'Review Budget',
+ category: 'budget',
+ relatedEntity: {
+ type: 'project',
+ id: 2,
+ name: 'Mobile Banking App',
+ },
+ },
+ {
+ id: 5,
+ type: 'success',
+ title: 'Task Completed',
+ message: 'Security audit for Healthcare Portal has been completed',
+ timestamp: new Date(Date.now() - 5 * 60 * 60 * 1000).toISOString(), // 5 hours ago
+ read: true,
+ priority: 'medium',
+ actionUrl: '/projects/5',
+ actionLabel: 'View Task',
+ category: 'task',
+ relatedEntity: {
+ type: 'project',
+ id: 5,
+ name: 'Healthcare Portal',
+ },
+ },
+ {
+ id: 6,
+ type: 'info',
+ title: 'Meeting Reminder',
+ message: 'Weekly team standup meeting starts in 30 minutes',
+ timestamp: new Date(Date.now() - 6 * 60 * 60 * 1000).toISOString(), // 6 hours ago
+ read: false,
+ priority: 'medium',
+ actionUrl: '/calendar',
+ actionLabel: 'View Calendar',
+ category: 'meeting',
+ relatedEntity: {
+ type: 'meeting',
+ id: 101,
+ name: 'Weekly Standup',
+ },
+ },
+ {
+ id: 7,
+ type: 'warning',
+ title: 'Resource Allocation',
+ message: 'AI Analytics Dashboard requires additional backend resources',
+ timestamp: new Date(Date.now() - 8 * 60 * 60 * 1000).toISOString(), // 8 hours ago
+ read: true,
+ priority: 'high',
+ actionUrl: '/projects/4',
+ actionLabel: 'Review Resources',
+ category: 'resource',
+ relatedEntity: {
+ type: 'project',
+ id: 4,
+ name: 'AI Analytics Dashboard',
+ },
+ },
+ {
+ id: 8,
+ type: 'success',
+ title: 'Code Review Approved',
+ message: 'Your pull request #234 has been approved and merged',
+ timestamp: new Date(Date.now() - 12 * 60 * 60 * 1000).toISOString(), // 12 hours ago
+ read: true,
+ priority: 'low',
+ actionUrl: '/code-reviews',
+ actionLabel: 'View PR',
+ category: 'code',
+ relatedEntity: {
+ type: 'pull_request',
+ id: 234,
+ name: 'Feature: User Authentication',
+ },
+ },
+ {
+ id: 9,
+ type: 'info',
+ title: 'System Maintenance',
+ message: 'Scheduled maintenance window this Saturday 2:00 AM - 4:00 AM UTC',
+ timestamp: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(), // 1 day ago
+ read: false,
+ priority: 'medium',
+ actionUrl: '/settings',
+ actionLabel: 'View Details',
+ category: 'system',
+ relatedEntity: {
+ type: 'maintenance',
+ id: 501,
+ name: 'System Maintenance',
+ },
+ },
+ {
+ id: 10,
+ type: 'warning',
+ title: 'License Expiring',
+ message: 'Your Figma team license will expire in 15 days',
+ timestamp: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString(), // 2 days ago
+ read: true,
+ priority: 'high',
+ actionUrl: '/settings/billing',
+ actionLabel: 'Renew License',
+ category: 'billing',
+ relatedEntity: {
+ type: 'license',
+ id: 301,
+ name: 'Figma Team License',
+ },
+ },
+ {
+ id: 11,
+ type: 'success',
+ title: 'Performance Bonus',
+ message: "Congratulations! You've earned a performance bonus for Q4 2025",
+ timestamp: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000).toISOString(), // 3 days ago
+ read: false,
+ priority: 'medium',
+ actionUrl: '/profile',
+ actionLabel: 'View Details',
+ category: 'hr',
+ relatedEntity: {
+ type: 'bonus',
+ id: 401,
+ name: 'Q4 Performance Bonus',
+ },
+ },
+ {
+ id: 12,
+ type: 'info',
+ title: 'New Feature Request',
+ message: 'Client has requested a new feature for the CRM System',
+ timestamp: new Date(Date.now() - 4 * 24 * 60 * 60 * 1000).toISOString(), // 4 days ago
+ read: true,
+ priority: 'low',
+ actionUrl: '/projects/1',
+ actionLabel: 'View Request',
+ category: 'feature',
+ relatedEntity: {
+ type: 'feature_request',
+ id: 601,
+ name: 'Advanced Reporting Module',
+ },
+ },
+];
+
+// Helper functions
+export const getNotificationById = (id) => {
+ return initialNotifications.find((notif) => notif.id === id);
+};
+
+export const getUnreadNotifications = () => {
+ return initialNotifications.filter((notif) => !notif.read);
+};
+
+export const getNotificationsByType = (type) => {
+ return initialNotifications.filter((notif) => notif.type === type);
+};
+
+export const getNotificationsByCategory = (category) => {
+ return initialNotifications.filter((notif) => notif.category === category);
+};
+
+export const getRecentNotifications = (limit = 5) => {
+ return initialNotifications
+ .sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))
+ .slice(0, limit);
+};
diff --git a/src/data/initialProfile.js b/src/data/initialProfile.js
new file mode 100644
index 00000000..e69de29b
diff --git a/src/data/initialProjects.js b/src/data/initialProjects.js
new file mode 100644
index 00000000..9883c78b
--- /dev/null
+++ b/src/data/initialProjects.js
@@ -0,0 +1,294 @@
+export const initialProjects = [
+ {
+ id: 1,
+ name: 'Enterprise CRM System',
+ description:
+ 'Complete customer relationship management system with advanced analytics and reporting',
+ budget: 45000,
+ completion: 75,
+ priority: 'high',
+ status: 'active',
+ deadline: '2026-02-15',
+ startDate: '2025-09-01',
+ category: 'Software Development',
+ tags: ['CRM', 'Enterprise', 'Analytics'],
+ client: 'TechCorp Industries',
+ members: [
+ {
+ id: 1,
+ name: 'John Doe',
+ avatar: 'https://i.pravatar.cc/150?img=1',
+ role: 'Project Lead',
+ },
+ {
+ id: 2,
+ name: 'Jane Smith',
+ avatar: 'https://i.pravatar.cc/150?img=2',
+ role: 'Senior Developer',
+ },
+ {
+ id: 3,
+ name: 'Mike Johnson',
+ avatar: 'https://i.pravatar.cc/150?img=3',
+ role: 'UI/UX Designer',
+ },
+ ],
+ milestones: [
+ { id: 1, title: 'Requirements Gathering', completed: true, date: '2025-09-15' },
+ { id: 2, title: 'Design Phase', completed: true, date: '2025-10-20' },
+ { id: 3, title: 'Development Phase', completed: false, date: '2026-01-15' },
+ { id: 4, title: 'Testing & QA', completed: false, date: '2026-02-01' },
+ { id: 5, title: 'Deployment', completed: false, date: '2026-02-15' },
+ ],
+ tasks: {
+ total: 48,
+ completed: 36,
+ inProgress: 8,
+ pending: 4,
+ },
+ },
+ {
+ id: 2,
+ name: 'Mobile Banking App',
+ description: 'Secure mobile banking application with biometric authentication',
+ budget: 32000,
+ completion: 45,
+ priority: 'medium',
+ status: 'active',
+ deadline: '2026-03-20',
+ startDate: '2025-10-15',
+ category: 'Mobile Development',
+ tags: ['Mobile', 'Banking', 'Security'],
+ client: 'National Bank',
+ members: [
+ {
+ id: 4,
+ name: 'Sarah Williams',
+ avatar: 'https://i.pravatar.cc/150?img=4',
+ role: 'Mobile Developer',
+ },
+ {
+ id: 5,
+ name: 'Tom Brown',
+ avatar: 'https://i.pravatar.cc/150?img=5',
+ role: 'Security Specialist',
+ },
+ ],
+ milestones: [
+ { id: 1, title: 'Planning & Design', completed: true, date: '2025-11-01' },
+ { id: 2, title: 'Backend Development', completed: true, date: '2025-12-15' },
+ { id: 3, title: 'Mobile App Development', completed: false, date: '2026-02-15' },
+ { id: 4, title: 'Security Audit', completed: false, date: '2026-03-01' },
+ { id: 5, title: 'Launch', completed: false, date: '2026-03-20' },
+ ],
+ tasks: {
+ total: 35,
+ completed: 16,
+ inProgress: 12,
+ pending: 7,
+ },
+ },
+ {
+ id: 3,
+ name: 'E-commerce Platform',
+ description: 'Full-featured e-commerce platform with inventory management',
+ budget: 58000,
+ completion: 90,
+ priority: 'high',
+ status: 'active',
+ deadline: '2026-01-30',
+ startDate: '2025-07-01',
+ category: 'Web Development',
+ tags: ['E-commerce', 'Web', 'Payment'],
+ client: 'RetailMax',
+ members: [
+ {
+ id: 6,
+ name: 'Emily Davis',
+ avatar: 'https://i.pravatar.cc/150?img=6',
+ role: 'Full Stack Developer',
+ },
+ {
+ id: 7,
+ name: 'David Wilson',
+ avatar: 'https://i.pravatar.cc/150?img=7',
+ role: 'Frontend Developer',
+ },
+ {
+ id: 8,
+ name: 'Lisa Anderson',
+ avatar: 'https://i.pravatar.cc/150?img=8',
+ role: 'Backend Developer',
+ },
+ {
+ id: 9,
+ name: 'Chris Martin',
+ avatar: 'https://i.pravatar.cc/150?img=9',
+ role: 'QA Engineer',
+ },
+ ],
+ milestones: [
+ { id: 1, title: 'Platform Architecture', completed: true, date: '2025-08-01' },
+ { id: 2, title: 'Core Features', completed: true, date: '2025-10-15' },
+ { id: 3, title: 'Payment Integration', completed: true, date: '2025-12-01' },
+ { id: 4, title: 'Testing Phase', completed: false, date: '2026-01-20' },
+ { id: 5, title: 'Go Live', completed: false, date: '2026-01-30' },
+ ],
+ tasks: {
+ total: 62,
+ completed: 56,
+ inProgress: 4,
+ pending: 2,
+ },
+ },
+ {
+ id: 4,
+ name: 'AI Analytics Dashboard',
+ description: 'Advanced analytics dashboard with machine learning capabilities',
+ budget: 67000,
+ completion: 30,
+ priority: 'high',
+ status: 'active',
+ deadline: '2026-04-30',
+ startDate: '2025-11-01',
+ category: 'AI & Analytics',
+ tags: ['AI', 'Analytics', 'Machine Learning'],
+ client: 'DataInsights Corp',
+ members: [
+ {
+ id: 10,
+ name: 'Robert Chen',
+ avatar: 'https://i.pravatar.cc/150?img=10',
+ role: 'AI Engineer',
+ },
+ {
+ id: 11,
+ name: 'Maria Garcia',
+ avatar: 'https://i.pravatar.cc/150?img=11',
+ role: 'Data Scientist',
+ },
+ {
+ id: 12,
+ name: 'James Lee',
+ avatar: 'https://i.pravatar.cc/150?img=12',
+ role: 'Backend Developer',
+ },
+ ],
+ milestones: [
+ { id: 1, title: 'Data Pipeline Setup', completed: true, date: '2025-11-20' },
+ { id: 2, title: 'ML Model Development', completed: false, date: '2026-02-15' },
+ { id: 3, title: 'Dashboard Development', completed: false, date: '2026-03-20' },
+ { id: 4, title: 'Integration & Testing', completed: false, date: '2026-04-15' },
+ { id: 5, title: 'Deployment', completed: false, date: '2026-04-30' },
+ ],
+ tasks: {
+ total: 42,
+ completed: 13,
+ inProgress: 15,
+ pending: 14,
+ },
+ },
+ {
+ id: 5,
+ name: 'Healthcare Portal',
+ description: 'Patient management portal with telemedicine features',
+ budget: 52000,
+ completion: 60,
+ priority: 'critical',
+ status: 'active',
+ deadline: '2026-02-28',
+ startDate: '2025-08-15',
+ category: 'Healthcare',
+ tags: ['Healthcare', 'Portal', 'Telemedicine'],
+ client: 'MediCare Plus',
+ members: [
+ {
+ id: 13,
+ name: 'Dr. Amanda White',
+ avatar: 'https://i.pravatar.cc/150?img=13',
+ role: 'Medical Consultant',
+ },
+ {
+ id: 14,
+ name: 'Kevin Park',
+ avatar: 'https://i.pravatar.cc/150?img=14',
+ role: 'Full Stack Developer',
+ },
+ {
+ id: 15,
+ name: 'Nancy Rodriguez',
+ avatar: 'https://i.pravatar.cc/150?img=15',
+ role: 'UX Designer',
+ },
+ ],
+ milestones: [
+ { id: 1, title: 'HIPAA Compliance Review', completed: true, date: '2025-09-10' },
+ { id: 2, title: 'Portal Development', completed: true, date: '2025-11-30' },
+ { id: 3, title: 'Telemedicine Integration', completed: false, date: '2026-01-30' },
+ { id: 4, title: 'Security Testing', completed: false, date: '2026-02-15' },
+ { id: 5, title: 'Launch', completed: false, date: '2026-02-28' },
+ ],
+ tasks: {
+ total: 55,
+ completed: 33,
+ inProgress: 14,
+ pending: 8,
+ },
+ },
+ {
+ id: 6,
+ name: 'Social Media Management Tool',
+ description: 'Multi-platform social media management and analytics tool',
+ budget: 28000,
+ completion: 15,
+ priority: 'low',
+ status: 'active',
+ deadline: '2026-05-15',
+ startDate: '2025-12-01',
+ category: 'SaaS',
+ tags: ['Social Media', 'Analytics', 'Automation'],
+ client: 'SocialPro Agency',
+ members: [
+ {
+ id: 16,
+ name: 'Alex Turner',
+ avatar: 'https://i.pravatar.cc/150?img=16',
+ role: 'Full Stack Developer',
+ },
+ {
+ id: 17,
+ name: 'Sophie Miller',
+ avatar: 'https://i.pravatar.cc/150?img=17',
+ role: 'Frontend Developer',
+ },
+ ],
+ milestones: [
+ { id: 1, title: 'Platform Research', completed: true, date: '2025-12-15' },
+ { id: 2, title: 'API Integrations', completed: false, date: '2026-02-01' },
+ { id: 3, title: 'Dashboard Development', completed: false, date: '2026-03-15' },
+ { id: 4, title: 'Analytics Module', completed: false, date: '2026-04-15' },
+ { id: 5, title: 'Beta Launch', completed: false, date: '2026-05-15' },
+ ],
+ tasks: {
+ total: 38,
+ completed: 6,
+ inProgress: 10,
+ pending: 22,
+ },
+ },
+];
+
+// Helper function to get project by ID
+export const getProjectById = (id) => {
+ return initialProjects.find((project) => project.id === id);
+};
+
+// Helper function to get projects by status
+export const getProjectsByStatus = (status) => {
+ return initialProjects.filter((project) => project.status === status);
+};
+
+// Helper function to get projects by priority
+export const getProjectsByPriority = (priority) => {
+ return initialProjects.filter((project) => project.priority === priority);
+};
diff --git a/src/data/initialTeamMembers.js b/src/data/initialTeamMembers.js
new file mode 100644
index 00000000..d3b22737
--- /dev/null
+++ b/src/data/initialTeamMembers.js
@@ -0,0 +1,271 @@
+export const initialTeamMembers = [
+ {
+ id: 1,
+ name: 'Alex Thompson',
+ email: 'alex.thompson@company.com',
+ phone: '+1 (555) 123-4567',
+ role: 'Senior Developer',
+ department: 'engineering',
+ status: 'online',
+ joinDate: '2023-06-15',
+ avatar: 'https://i.pravatar.cc/150?img=10',
+ bio: 'Experienced full-stack developer with expertise in React and Node.js',
+ skills: ['React', 'Node.js', 'TypeScript', 'MongoDB', 'AWS'],
+ location: 'San Francisco, CA',
+ timezone: 'PST',
+ projects: [1, 3, 4],
+ tasksCompleted: 127,
+ hoursWorked: 1840,
+ rating: 4.8,
+ salary: 120000,
+ birthday: '1990-03-15',
+ },
+ {
+ id: 2,
+ name: 'Maria Garcia',
+ email: 'maria.garcia@company.com',
+ phone: '+1 (555) 234-5678',
+ role: 'Product Manager',
+ department: 'product',
+ status: 'offline',
+ joinDate: '2022-11-20',
+ avatar: 'https://i.pravatar.cc/150?img=11',
+ bio: 'Strategic product manager with a track record of successful product launches',
+ skills: ['Product Strategy', 'Agile', 'User Research', 'Roadmapping'],
+ location: 'New York, NY',
+ timezone: 'EST',
+ projects: [1, 2, 5],
+ tasksCompleted: 89,
+ hoursWorked: 2156,
+ rating: 4.9,
+ salary: 135000,
+ birthday: '1988-07-22',
+ },
+ {
+ id: 3,
+ name: 'James Wilson',
+ email: 'james.wilson@company.com',
+ phone: '+1 (555) 345-6789',
+ role: 'UI/UX Designer',
+ department: 'design',
+ status: 'online',
+ joinDate: '2024-01-10',
+ avatar: 'https://i.pravatar.cc/150?img=12',
+ bio: 'Creative designer focused on user-centered design and modern interfaces',
+ skills: ['Figma', 'Adobe XD', 'UI Design', 'Prototyping', 'Design Systems'],
+ location: 'Austin, TX',
+ timezone: 'CST',
+ projects: [1, 3, 6],
+ tasksCompleted: 56,
+ hoursWorked: 892,
+ rating: 4.7,
+ salary: 95000,
+ birthday: '1992-11-08',
+ },
+ {
+ id: 4,
+ name: 'Sarah Williams',
+ email: 'sarah.williams@company.com',
+ phone: '+1 (555) 456-7890',
+ role: 'Mobile Developer',
+ department: 'engineering',
+ status: 'away',
+ joinDate: '2023-03-05',
+ avatar: 'https://i.pravatar.cc/150?img=4',
+ bio: 'Mobile development specialist with focus on iOS and Android platforms',
+ skills: ['React Native', 'Swift', 'Kotlin', 'Firebase', 'Mobile UI'],
+ location: 'Seattle, WA',
+ timezone: 'PST',
+ projects: [2, 5],
+ tasksCompleted: 78,
+ hoursWorked: 1423,
+ rating: 4.6,
+ salary: 115000,
+ birthday: '1991-05-18',
+ },
+ {
+ id: 5,
+ name: 'Tom Brown',
+ email: 'tom.brown@company.com',
+ phone: '+1 (555) 567-8901',
+ role: 'Security Specialist',
+ department: 'engineering',
+ status: 'online',
+ joinDate: '2022-08-12',
+ avatar: 'https://i.pravatar.cc/150?img=5',
+ bio: 'Cybersecurity expert ensuring application and infrastructure security',
+ skills: ['Security Auditing', 'Penetration Testing', 'OWASP', 'Encryption'],
+ location: 'Boston, MA',
+ timezone: 'EST',
+ projects: [2, 5],
+ tasksCompleted: 94,
+ hoursWorked: 1678,
+ rating: 4.9,
+ salary: 140000,
+ birthday: '1987-09-30',
+ },
+ {
+ id: 6,
+ name: 'Emily Davis',
+ email: 'emily.davis@company.com',
+ phone: '+1 (555) 678-9012',
+ role: 'Full Stack Developer',
+ department: 'engineering',
+ status: 'online',
+ joinDate: '2023-02-18',
+ avatar: 'https://i.pravatar.cc/150?img=6',
+ bio: 'Versatile developer comfortable with both frontend and backend technologies',
+ skills: ['React', 'Python', 'Django', 'PostgreSQL', 'Docker'],
+ location: 'Denver, CO',
+ timezone: 'MST',
+ projects: [3],
+ tasksCompleted: 112,
+ hoursWorked: 1567,
+ rating: 4.8,
+ salary: 125000,
+ birthday: '1993-02-14',
+ },
+ {
+ id: 7,
+ name: 'David Wilson',
+ email: 'david.wilson@company.com',
+ phone: '+1 (555) 789-0123',
+ role: 'Frontend Developer',
+ department: 'engineering',
+ status: 'busy',
+ joinDate: '2023-09-22',
+ avatar: 'https://i.pravatar.cc/150?img=7',
+ bio: 'Frontend specialist passionate about creating beautiful user interfaces',
+ skills: ['React', 'Vue.js', 'CSS/SASS', 'JavaScript', 'Webpack'],
+ location: 'Miami, FL',
+ timezone: 'EST',
+ projects: [3, 6],
+ tasksCompleted: 87,
+ hoursWorked: 1234,
+ rating: 4.5,
+ salary: 105000,
+ birthday: '1994-06-25',
+ },
+ {
+ id: 8,
+ name: 'Lisa Anderson',
+ email: 'lisa.anderson@company.com',
+ phone: '+1 (555) 890-1234',
+ role: 'Backend Developer',
+ department: 'engineering',
+ status: 'online',
+ joinDate: '2022-05-30',
+ avatar: 'https://i.pravatar.cc/150?img=8',
+ bio: 'Backend engineer specializing in scalable microservices architecture',
+ skills: ['Node.js', 'Java', 'Microservices', 'Redis', 'Kubernetes'],
+ location: 'Chicago, IL',
+ timezone: 'CST',
+ projects: [3, 4],
+ tasksCompleted: 145,
+ hoursWorked: 2089,
+ rating: 4.9,
+ salary: 130000,
+ birthday: '1989-12-03',
+ },
+ {
+ id: 9,
+ name: 'Chris Martin',
+ email: 'chris.martin@company.com',
+ phone: '+1 (555) 901-2345',
+ role: 'QA Engineer',
+ department: 'engineering',
+ status: 'offline',
+ joinDate: '2023-07-14',
+ avatar: 'https://i.pravatar.cc/150?img=9',
+ bio: 'Quality assurance expert ensuring bug-free software delivery',
+ skills: ['Test Automation', 'Selenium', 'Jest', 'Cypress', 'Manual Testing'],
+ location: 'Portland, OR',
+ timezone: 'PST',
+ projects: [3, 5],
+ tasksCompleted: 203,
+ hoursWorked: 1456,
+ rating: 4.7,
+ salary: 95000,
+ birthday: '1995-04-19',
+ },
+ {
+ id: 10,
+ name: 'Robert Chen',
+ email: 'robert.chen@company.com',
+ phone: '+1 (555) 012-3456',
+ role: 'AI Engineer',
+ department: 'engineering',
+ status: 'online',
+ joinDate: '2023-10-08',
+ avatar: 'https://i.pravatar.cc/150?img=20',
+ bio: 'AI/ML engineer building intelligent systems and predictive models',
+ skills: ['Python', 'TensorFlow', 'PyTorch', 'Machine Learning', 'Deep Learning'],
+ location: 'San Jose, CA',
+ timezone: 'PST',
+ projects: [4],
+ tasksCompleted: 67,
+ hoursWorked: 1123,
+ rating: 4.8,
+ salary: 145000,
+ birthday: '1990-08-27',
+ },
+ {
+ id: 11,
+ name: 'Jennifer Lee',
+ email: 'jennifer.lee@company.com',
+ phone: '+1 (555) 123-4560',
+ role: 'Marketing Manager',
+ department: 'marketing',
+ status: 'online',
+ joinDate: '2022-09-15',
+ avatar: 'https://i.pravatar.cc/150?img=21',
+ bio: 'Digital marketing strategist with expertise in growth hacking',
+ skills: ['SEO', 'Content Marketing', 'Social Media', 'Analytics', 'Campaign Management'],
+ location: 'Los Angeles, CA',
+ timezone: 'PST',
+ projects: [],
+ tasksCompleted: 156,
+ hoursWorked: 1890,
+ rating: 4.6,
+ salary: 110000,
+ birthday: '1991-01-12',
+ },
+ {
+ id: 12,
+ name: 'Michael Rodriguez',
+ email: 'michael.rodriguez@company.com',
+ phone: '+1 (555) 234-5601',
+ role: 'Sales Director',
+ department: 'sales',
+ status: 'away',
+ joinDate: '2021-04-20',
+ avatar: 'https://i.pravatar.cc/150?img=22',
+ bio: 'Results-driven sales leader with consistent track record of exceeding targets',
+ skills: ['Sales Strategy', 'CRM', 'Negotiation', 'Team Leadership', 'Account Management'],
+ location: 'Dallas, TX',
+ timezone: 'CST',
+ projects: [],
+ tasksCompleted: 234,
+ hoursWorked: 2567,
+ rating: 4.9,
+ salary: 150000,
+ birthday: '1985-10-05',
+ },
+];
+
+// Helper functions
+export const getTeamMemberById = (id) => {
+ return initialTeamMembers.find((member) => member.id === id);
+};
+
+export const getTeamMembersByDepartment = (department) => {
+ return initialTeamMembers.filter((member) => member.department === department);
+};
+
+export const getTeamMembersByStatus = (status) => {
+ return initialTeamMembers.filter((member) => member.status === status);
+};
+
+export const getOnlineMembers = () => {
+ return initialTeamMembers.filter((member) => member.status === 'online');
+};
diff --git a/src/data/orders-overview-data.js b/src/data/orders-overview-data.js
deleted file mode 100644
index d1ba49d9..00000000
--- a/src/data/orders-overview-data.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import {
- BellIcon,
- PlusCircleIcon,
- ShoppingCartIcon,
- CreditCardIcon,
- LockOpenIcon,
- BanknotesIcon,
-} from "@heroicons/react/24/solid";
-
-export const ordersOverviewData = [
- {
- icon: BellIcon,
- color: "text-blue-gray-300",
- title: "$2400, Design changes",
- description: "22 DEC 7:20 PM",
- },
- {
- icon: PlusCircleIcon,
- color: "text-blue-gray-300",
- title: "New order #1832412",
- description: "21 DEC 11 PM",
- },
- {
- icon: ShoppingCartIcon,
- color: "text-blue-gray-300",
- title: "Server payments for April",
- description: "21 DEC 9:34 PM",
- },
- {
- icon: CreditCardIcon,
- color: "text-blue-gray-300",
- title: "New card added for order #4395133",
- description: "20 DEC 2:20 AM",
- },
- {
- icon: LockOpenIcon,
- color: "text-blue-gray-300",
- title: "Unlock packages for development",
- description: "18 DEC 4:54 AM",
- },
- {
- icon: BanknotesIcon,
- color: "text-blue-gray-300",
- title: "New order #9583120",
- description: "17 DEC",
- },
-];
-
-export default ordersOverviewData;
diff --git a/src/data/platform-settings-data.js b/src/data/platform-settings-data.js
deleted file mode 100644
index 140c5dfd..00000000
--- a/src/data/platform-settings-data.js
+++ /dev/null
@@ -1,38 +0,0 @@
-export const platformSettingsData = [
- {
- title: "account",
- options: [
- {
- checked: true,
- label: "Email me when someone follows me",
- },
- {
- checked: false,
- label: "Email me when someone answers on my post",
- },
- {
- checked: true,
- label: "Email me when someone mentions me",
- },
- ],
- },
- {
- title: "application",
- options: [
- {
- checked: false,
- label: "New launches and projects",
- },
- {
- checked: true,
- label: "Monthly product updates",
- },
- {
- checked: false,
- label: "Subscribe to newsletter",
- },
- ],
- },
-];
-
-export default platformSettingsData;
diff --git a/src/data/projects-data.js b/src/data/projects-data.js
deleted file mode 100644
index 3c83bc08..00000000
--- a/src/data/projects-data.js
+++ /dev/null
@@ -1,60 +0,0 @@
-export const projectsData = [
- {
- img: "/img/home-decor-1.jpeg",
- title: "Modern",
- tag: "Project #1",
- description:
- "As Uber works through a huge amount of internal management turmoil.",
- route: "/dashboard/profile",
- members: [
- { img: "/img/team-1.jpeg", name: "Romina Hadid" },
- { img: "/img/team-2.jpeg", name: "Ryan Tompson" },
- { img: "/img/team-3.jpeg", name: "Jessica Doe" },
- { img: "/img/team-4.jpeg", name: "Alexander Smith" },
- ],
- },
- {
- img: "/img/home-decor-2.jpeg",
- title: "Scandinavian",
- tag: "Project #2",
- description:
- "Music is something that every person has his or her own specific opinion about.",
- route: "/dashboard/profile",
- members: [
- { img: "/img/team-4.jpeg", name: "Alexander Smith" },
- { img: "/img/team-3.jpeg", name: "Jessica Doe" },
- { img: "/img/team-2.jpeg", name: "Ryan Tompson" },
- { img: "/img/team-1.jpeg", name: "Romina Hadid" },
- ],
- },
- {
- img: "/img/home-decor-3.jpeg",
- title: "Minimalist",
- tag: "Project #3",
- description:
- "Different people have different taste, and various types of music.",
- route: "/dashboard/profile",
- members: [
- { img: "/img/team-1.jpeg", name: "Romina Hadid" },
- { img: "/img/team-2.jpeg", name: "Ryan Tompson" },
- { img: "/img/team-3.jpeg", name: "Jessica Doe" },
- { img: "/img/team-4.jpeg", name: "Alexander Smith" },
- ],
- },
- {
- img: "/img/home-decor-4.jpeg",
- title: "Gothic",
- tag: "Project #4",
- description:
- "Why would anyone pick blue over pink? Pink is obviously a better color.",
- route: "/dashboard/profile",
- members: [
- { img: "/img/team-4.jpeg", name: "Alexander Smith" },
- { img: "/img/team-3.jpeg", name: "Jessica Doe" },
- { img: "/img/team-2.jpeg", name: "Ryan Tompson" },
- { img: "/img/team-1.jpeg", name: "Romina Hadid" },
- ],
- },
-];
-
-export default projectsData;
diff --git a/src/data/projects-table-data.js b/src/data/projects-table-data.js
deleted file mode 100644
index d37e08ba..00000000
--- a/src/data/projects-table-data.js
+++ /dev/null
@@ -1,65 +0,0 @@
-export const projectsTableData = [
- {
- img: "/img/logo-xd.svg",
- name: "Material XD Version",
- members: [
- { img: "/img/team-1.jpeg", name: "Romina Hadid" },
- { img: "/img/team-2.jpeg", name: "Ryan Tompson" },
- { img: "/img/team-3.jpeg", name: "Jessica Doe" },
- { img: "/img/team-4.jpeg", name: "Alexander Smith" },
- ],
- budget: "$14,000",
- completion: 60,
- },
- {
- img: "/img/logo-atlassian.svg",
- name: "Add Progress Track",
- members: [
- { img: "/img/team-2.jpeg", name: "Ryan Tompson" },
- { img: "/img/team-4.jpeg", name: "Alexander Smith" },
- ],
- budget: "$3,000",
- completion: 10,
- },
- {
- img: "/img/logo-slack.svg",
- name: "Fix Platform Errors",
- members: [
- { img: "/img/team-3.jpeg", name: "Jessica Doe" },
- { img: "/img/team-1.jpeg", name: "Romina Hadid" },
- ],
- budget: "Not set",
- completion: 100,
- },
- {
- img: "/img/logo-spotify.svg",
- name: "Launch our Mobile App",
- members: [
- { img: "/img/team-4.jpeg", name: "Alexander Smith" },
- { img: "/img/team-3.jpeg", name: "Jessica Doe" },
- { img: "/img/team-2.jpeg", name: "Ryan Tompson" },
- { img: "/img/team-1.jpeg", name: "Romina Hadid" },
- ],
- budget: "$20,500",
- completion: 100,
- },
- {
- img: "/img/logo-jira.svg",
- name: "Add the New Pricing Page",
- members: [{ img: "/img/team-4.jpeg", name: "Alexander Smith" }],
- budget: "$500",
- completion: 25,
- },
- {
- img: "/img/logo-invision.svg",
- name: "Redesign New Online Shop",
- members: [
- { img: "/img/team-1.jpeg", name: "Romina Hadid" },
- { img: "/img/team-4.jpeg", name: "Alexander Smith" },
- ],
- budget: "$2,000",
- completion: 40,
- },
-];
-
-export default projectsTableData;
diff --git a/src/data/statistics-cards-data.js b/src/data/statistics-cards-data.js
deleted file mode 100644
index d470e708..00000000
--- a/src/data/statistics-cards-data.js
+++ /dev/null
@@ -1,55 +0,0 @@
-import {
- BanknotesIcon,
- UserPlusIcon,
- UsersIcon,
- ChartBarIcon,
-} from "@heroicons/react/24/solid";
-
-export const statisticsCardsData = [
- {
- color: "gray",
- icon: BanknotesIcon,
- title: "Today's Money",
- value: "$53k",
- footer: {
- color: "text-green-500",
- value: "+55%",
- label: "than last week",
- },
- },
- {
- color: "gray",
- icon: UsersIcon,
- title: "Today's Users",
- value: "2,300",
- footer: {
- color: "text-green-500",
- value: "+3%",
- label: "than last month",
- },
- },
- {
- color: "gray",
- icon: UserPlusIcon,
- title: "New Clients",
- value: "3,462",
- footer: {
- color: "text-red-500",
- value: "-2%",
- label: "than yesterday",
- },
- },
- {
- color: "gray",
- icon: ChartBarIcon,
- title: "Sales",
- value: "$103,430",
- footer: {
- color: "text-green-500",
- value: "+5%",
- label: "than yesterday",
- },
- },
-];
-
-export default statisticsCardsData;
diff --git a/src/data/statistics-charts-data.js b/src/data/statistics-charts-data.js
deleted file mode 100644
index 15a36464..00000000
--- a/src/data/statistics-charts-data.js
+++ /dev/null
@@ -1,131 +0,0 @@
-import { chartsConfig } from "@/configs";
-
-const websiteViewsChart = {
- type: "bar",
- height: 220,
- series: [
- {
- name: "Views",
- data: [50, 20, 10, 22, 50, 10, 40],
- },
- ],
- options: {
- ...chartsConfig,
- colors: "#388e3c",
- plotOptions: {
- bar: {
- columnWidth: "16%",
- borderRadius: 5,
- },
- },
- xaxis: {
- ...chartsConfig.xaxis,
- categories: ["M", "T", "W", "T", "F", "S", "S"],
- },
- },
-};
-
-const dailySalesChart = {
- type: "line",
- height: 220,
- series: [
- {
- name: "Sales",
- data: [50, 40, 300, 320, 500, 350, 200, 230, 500],
- },
- ],
- options: {
- ...chartsConfig,
- colors: ["#0288d1"],
- stroke: {
- lineCap: "round",
- },
- markers: {
- size: 5,
- },
- xaxis: {
- ...chartsConfig.xaxis,
- categories: [
- "Apr",
- "May",
- "Jun",
- "Jul",
- "Aug",
- "Sep",
- "Oct",
- "Nov",
- "Dec",
- ],
- },
- },
-};
-
-const completedTaskChart = {
- type: "line",
- height: 220,
- series: [
- {
- name: "Sales",
- data: [50, 40, 300, 320, 500, 350, 200, 230, 500],
- },
- ],
- options: {
- ...chartsConfig,
- colors: ["#388e3c"],
- stroke: {
- lineCap: "round",
- },
- markers: {
- size: 5,
- },
- xaxis: {
- ...chartsConfig.xaxis,
- categories: [
- "Apr",
- "May",
- "Jun",
- "Jul",
- "Aug",
- "Sep",
- "Oct",
- "Nov",
- "Dec",
- ],
- },
- },
-};
-const completedTasksChart = {
- ...completedTaskChart,
- series: [
- {
- name: "Tasks",
- data: [50, 40, 300, 220, 500, 250, 400, 230, 500],
- },
- ],
-};
-
-export const statisticsChartsData = [
- {
- color: "white",
- title: "Website View",
- description: "Last Campaign Performance",
- footer: "campaign sent 2 days ago",
- chart: websiteViewsChart,
- },
- {
- color: "white",
- title: "Daily Sales",
- description: "15% increase in today sales",
- footer: "updated 4 min ago",
- chart: dailySalesChart,
- },
- {
- color: "white",
- title: "Completed Tasks",
- description: "Last Campaign Performance",
- footer: "just updated",
- chart: completedTasksChart,
- },
-];
-
-export default statisticsChartsData;
diff --git a/src/data/statistics.js b/src/data/statistics.js
new file mode 100644
index 00000000..51963819
--- /dev/null
+++ b/src/data/statistics.js
@@ -0,0 +1,231 @@
+import {
+ CurrencyDollarIcon,
+ ChartBarIcon,
+ UsersIcon,
+ CheckCircleIcon,
+ ClockIcon,
+ ExclamationTriangleIcon,
+} from '@heroicons/react/24/outline';
+
+export const dashboardStatistics = [
+ {
+ id: 1,
+ title: 'Total Revenue',
+ value: '$282,460',
+ rawValue: 282460,
+ change: '+12.5%',
+ changeValue: 12.5,
+ trend: 'up',
+ icon: CurrencyDollarIcon,
+ color: 'green',
+ description: 'Total revenue from all projects',
+ period: 'This Quarter',
+ details: {
+ lastMonth: 245000,
+ thisMonth: 282460,
+ growth: 15.3,
+ },
+ },
+ {
+ id: 2,
+ title: 'Active Projects',
+ value: '6',
+ rawValue: 6,
+ change: '+2 new',
+ changeValue: 2,
+ trend: 'up',
+ icon: ChartBarIcon,
+ color: 'blue',
+ description: 'Currently active projects',
+ period: 'This Month',
+ details: {
+ total: 6,
+ onTrack: 4,
+ delayed: 1,
+ atRisk: 1,
+ },
+ },
+ {
+ id: 3,
+ title: 'Team Members',
+ value: '12',
+ rawValue: 12,
+ change: '+2 this month',
+ changeValue: 2,
+ trend: 'up',
+ icon: UsersIcon,
+ color: 'purple',
+ description: 'Total team members',
+ period: 'Current',
+ details: {
+ online: 7,
+ offline: 3,
+ away: 2,
+ },
+ },
+ {
+ id: 4,
+ title: 'Tasks Completed',
+ value: '89',
+ rawValue: 89,
+ change: '-5%',
+ changeValue: -5,
+ trend: 'down',
+ icon: CheckCircleIcon,
+ color: 'orange',
+ description: 'Tasks completed this month',
+ period: 'This Month',
+ details: {
+ total: 145,
+ completed: 89,
+ pending: 56,
+ },
+ },
+ {
+ id: 5,
+ title: 'Avg. Project Time',
+ value: '4.2 months',
+ rawValue: 4.2,
+ change: '+0.5 months',
+ changeValue: 0.5,
+ trend: 'up',
+ icon: ClockIcon,
+ color: 'indigo',
+ description: 'Average project completion time',
+ period: 'Last 6 Months',
+ details: {
+ shortest: 2.5,
+ longest: 6.8,
+ average: 4.2,
+ },
+ },
+ {
+ id: 6,
+ title: 'Issues',
+ value: '3',
+ rawValue: 3,
+ change: '-2 from last week',
+ changeValue: -2,
+ trend: 'down',
+ icon: ExclamationTriangleIcon,
+ color: 'red',
+ description: 'Active issues requiring attention',
+ period: 'This Week',
+ details: {
+ critical: 1,
+ high: 1,
+ medium: 1,
+ low: 0,
+ },
+ },
+];
+
+// Project statistics by priority
+export const projectsByPriority = {
+ critical: 1,
+ high: 2,
+ medium: 2,
+ low: 1,
+};
+
+// Project statistics by status
+export const projectsByStatus = {
+ active: 6,
+ paused: 0,
+ completed: 8,
+ cancelled: 1,
+};
+
+// Team statistics by department
+export const teamByDepartment = {
+ engineering: 8,
+ product: 1,
+ design: 1,
+ marketing: 1,
+ sales: 1,
+};
+
+// Budget allocation statistics
+export const budgetAllocation = [
+ { category: 'Development', value: 145000, percentage: 51 },
+ { category: 'Design', value: 42000, percentage: 15 },
+ { category: 'Marketing', value: 35000, percentage: 12 },
+ { category: 'Operations', value: 28000, percentage: 10 },
+ { category: 'Other', value: 32460, percentage: 12 },
+];
+
+// Monthly revenue data
+export const monthlyRevenue = [
+ { month: 'Jan', revenue: 35000, expenses: 28000, profit: 7000 },
+ { month: 'Feb', revenue: 42000, expenses: 31000, profit: 11000 },
+ { month: 'Mar', revenue: 38000, expenses: 29000, profit: 9000 },
+ { month: 'Apr', revenue: 51000, expenses: 35000, profit: 16000 },
+ { month: 'May', revenue: 48000, expenses: 33000, profit: 15000 },
+ { month: 'Jun', revenue: 55000, expenses: 38000, profit: 17000 },
+ { month: 'Jul', revenue: 62000, expenses: 42000, profit: 20000 },
+ { month: 'Aug', revenue: 58000, expenses: 40000, profit: 18000 },
+ { month: 'Sep', revenue: 67000, expenses: 45000, profit: 22000 },
+ { month: 'Oct', revenue: 71000, expenses: 48000, profit: 23000 },
+ { month: 'Nov', revenue: 68000, expenses: 46000, profit: 22000 },
+ { month: 'Dec', revenue: 75000, expenses: 50000, profit: 25000 },
+];
+
+// Project completion timeline
+export const projectTimeline = [
+ { month: 'Aug', completed: 2, started: 3 },
+ { month: 'Sep', completed: 1, started: 2 },
+ { month: 'Oct', completed: 3, started: 1 },
+ { month: 'Nov', completed: 2, started: 4 },
+ { month: 'Dec', completed: 4, started: 2 },
+ { month: 'Jan', completed: 3, started: 3 },
+];
+
+// Task statistics
+export const taskStatistics = {
+ total: 312,
+ completed: 189,
+ inProgress: 78,
+ pending: 45,
+ completionRate: 60.6,
+ avgCompletionTime: 3.5, // days
+};
+
+// Team performance metrics
+export const teamPerformance = [
+ { member: 'Alex Thompson', tasks: 127, rating: 4.8, efficiency: 92 },
+ { member: 'Maria Garcia', tasks: 89, rating: 4.9, efficiency: 94 },
+ { member: 'James Wilson', tasks: 56, rating: 4.7, efficiency: 88 },
+ { member: 'Sarah Williams', tasks: 78, rating: 4.6, efficiency: 85 },
+ { member: 'Tom Brown', tasks: 94, rating: 4.9, efficiency: 96 },
+ { member: 'Emily Davis', tasks: 112, rating: 4.8, efficiency: 91 },
+ { member: 'David Wilson', tasks: 87, rating: 4.5, efficiency: 82 },
+ { member: 'Lisa Anderson', tasks: 145, rating: 4.9, efficiency: 97 },
+];
+
+// Client satisfaction ratings
+export const clientSatisfaction = [
+ { client: 'TechCorp Industries', rating: 4.8, projects: 3 },
+ { client: 'National Bank', rating: 4.6, projects: 2 },
+ { client: 'RetailMax', rating: 4.9, projects: 1 },
+ { client: 'DataInsights Corp', rating: 4.7, projects: 1 },
+ { client: 'MediCare Plus', rating: 4.9, projects: 2 },
+ { client: 'SocialPro Agency', rating: 4.5, projects: 1 },
+];
+
+// Helper functions
+export const getTotalRevenue = () => {
+ return monthlyRevenue.reduce((sum, month) => sum + month.revenue, 0);
+};
+
+export const getTotalProfit = () => {
+ return monthlyRevenue.reduce((sum, month) => sum + month.profit, 0);
+};
+
+export const getAverageClientSatisfaction = () => {
+ const total = clientSatisfaction.reduce((sum, client) => sum + client.rating, 0);
+ return (total / clientSatisfaction.length).toFixed(1);
+};
+
+export const getTaskCompletionRate = () => {
+ return ((taskStatistics.completed / taskStatistics.total) * 100).toFixed(1);
+};
diff --git a/src/hooks/index.js b/src/hooks/index.js
new file mode 100644
index 00000000..03208fdc
--- /dev/null
+++ b/src/hooks/index.js
@@ -0,0 +1,6 @@
+export { useLocalStorage } from './useLocalStorage';
+export { useProjects } from './useProjects';
+export { useTeamMembers } from './useTeamMembers';
+export { useNotifications } from './useNotifications';
+export { useProfile } from './useProfile';
+export { useAuth } from './useAuth';
diff --git a/src/hooks/useAuth.js b/src/hooks/useAuth.js
new file mode 100644
index 00000000..09930d02
--- /dev/null
+++ b/src/hooks/useAuth.js
@@ -0,0 +1,184 @@
+// ============================================
+// hooks/useAuth.js
+// ============================================
+
+import React from 'react';
+import { useLocalStorage } from './useLocalStorage';
+import { setAuthToken, removeAuthToken, isTokenExpired } from '../utils/auth';
+
+export const useAuth = () => {
+ const [user, setUser] = useLocalStorage('dashboard_user', null);
+ const [token, setToken] = useLocalStorage('dashboard_auth_token', null);
+ const [loading, setLoading] = React.useState(false);
+ const [error, setError] = React.useState(null);
+
+ // Check if user is authenticated
+ const isAuthenticated = React.useMemo(() => {
+ if (!user || !token) return false;
+ if (isTokenExpired(token)) {
+ // Token expired, clear auth data
+ setUser(null);
+ setToken(null);
+ removeAuthToken();
+ return false;
+ }
+ return true;
+ }, [user, token, setUser, setToken]);
+
+ // Login function
+ const login = React.useCallback(
+ async (credentials) => {
+ setLoading(true);
+ setError(null);
+
+ try {
+ // Simulate API call
+ await new Promise((resolve) => setTimeout(resolve, 1000));
+
+ // Mock response
+ const mockUser = {
+ id: Date.now(),
+ name: credentials.name || 'User',
+ email: credentials.email,
+ role: 'admin',
+ avatar: `https://i.pravatar.cc/150?img=${Math.floor(Math.random() * 70)}`,
+ };
+
+ const mockToken = `mock-jwt-token-${Date.now()}`;
+
+ // Save to state and storage
+ setUser(mockUser);
+ setToken(mockToken);
+ setAuthToken(mockToken);
+
+ return { success: true, user: mockUser };
+ } catch (err) {
+ setError(err.message);
+ return { success: false, error: err.message };
+ } finally {
+ setLoading(false);
+ }
+ },
+ [setUser, setToken]
+ );
+
+ // Signup function
+ const signup = React.useCallback(
+ async (userData) => {
+ setLoading(true);
+ setError(null);
+
+ try {
+ // Simulate API call
+ await new Promise((resolve) => setTimeout(resolve, 1000));
+
+ // Mock response
+ const mockUser = {
+ id: Date.now(),
+ name: userData.name,
+ email: userData.email,
+ phone: userData.phone,
+ role: 'user',
+ avatar: `https://i.pravatar.cc/150?img=${Math.floor(Math.random() * 70)}`,
+ };
+
+ const mockToken = `mock-jwt-token-${Date.now()}`;
+
+ // Save to state and storage
+ setUser(mockUser);
+ setToken(mockToken);
+ setAuthToken(mockToken);
+
+ return { success: true, user: mockUser };
+ } catch (err) {
+ setError(err.message);
+ return { success: false, error: err.message };
+ } finally {
+ setLoading(false);
+ }
+ },
+ [setUser, setToken]
+ );
+
+ // Logout function
+ const logout = React.useCallback(() => {
+ setUser(null);
+ setToken(null);
+ removeAuthToken();
+ setError(null);
+ }, [setUser, setToken]);
+
+ // Forgot password function
+ const forgotPassword = React.useCallback(async (email) => {
+ setLoading(true);
+ setError(null);
+
+ try {
+ // Simulate API call
+ await new Promise((resolve) => setTimeout(resolve, 1000));
+ return { success: true, message: 'Password reset link sent to your email' };
+ } catch (err) {
+ setError(err.message);
+ return { success: false, error: err.message };
+ } finally {
+ setLoading(false);
+ }
+ }, []);
+
+ // Update user profile
+ const updateUser = React.useCallback(
+ (updates) => {
+ setUser((prev) => (prev ? { ...prev, ...updates } : null));
+ },
+ [setUser]
+ );
+
+ // Refresh token
+ const refreshToken = React.useCallback(async () => {
+ if (!token) return { success: false };
+
+ setLoading(true);
+ try {
+ // Simulate API call to refresh token
+ await new Promise((resolve) => setTimeout(resolve, 500));
+
+ const newToken = `refreshed-token-${Date.now()}`;
+ setToken(newToken);
+ setAuthToken(newToken);
+
+ return { success: true, token: newToken };
+ } catch (err) {
+ setError(err.message);
+ return { success: false, error: err.message };
+ } finally {
+ setLoading(false);
+ }
+ }, [token, setToken]);
+
+ // Check token validity on mount
+ React.useEffect(() => {
+ if (token && isTokenExpired(token)) {
+ logout();
+ }
+ }, [token, logout]);
+
+ return {
+ // State
+ user,
+ token,
+ loading,
+ error,
+ isAuthenticated,
+
+ // Actions
+ login,
+ signup,
+ logout,
+ forgotPassword,
+ updateUser,
+ refreshToken,
+
+ // Clear error
+ clearError: () => setError(null),
+ };
+};
diff --git a/src/hooks/useLocalStorage.js b/src/hooks/useLocalStorage.js
new file mode 100644
index 00000000..110bc7e2
--- /dev/null
+++ b/src/hooks/useLocalStorage.js
@@ -0,0 +1,73 @@
+// ============================================
+// hooks/useLocalStorage.js
+// ============================================
+
+import { useState, useEffect, useCallback } from 'react';
+import { safeJsonParse } from '../utils/helpers';
+
+/**
+ * Custom hook for localStorage with automatic JSON parsing/stringifying
+ * @param {string} key - LocalStorage key
+ * @param {any} initialValue - Initial value if key doesn't exist
+ * @returns {[any, Function, Function]} [value, setValue, removeValue]
+ */
+export const useLocalStorage = (key, initialValue) => {
+ // State to store our value
+ const [storedValue, setStoredValue] = useState(() => {
+ if (typeof window === 'undefined') {
+ return initialValue;
+ }
+
+ try {
+ const item = window.localStorage.getItem(key);
+ return item ? safeJsonParse(item, initialValue) : initialValue;
+ } catch (error) {
+ console.error(`Error loading localStorage key "${key}":`, error);
+ return initialValue;
+ }
+ });
+
+ // Return a wrapped version of useState's setter function that
+ // persists the new value to localStorage
+ const setValue = useCallback((value) => {
+ try {
+ // Allow value to be a function so we have same API as useState
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
+
+ setStoredValue(valueToStore);
+
+ if (typeof window !== 'undefined') {
+ window.localStorage.setItem(key, JSON.stringify(valueToStore));
+ }
+ } catch (error) {
+ console.error(`Error setting localStorage key "${key}":`, error);
+ }
+ }, [key, storedValue]);
+
+ // Function to remove the key from localStorage
+ const removeValue = useCallback(() => {
+ try {
+ setStoredValue(initialValue);
+
+ if (typeof window !== 'undefined') {
+ window.localStorage.removeItem(key);
+ }
+ } catch (error) {
+ console.error(`Error removing localStorage key "${key}":`, error);
+ }
+ }, [key, initialValue]);
+
+ // Sync with other tabs/windows
+ useEffect(() => {
+ const handleStorageChange = (e) => {
+ if (e.key === key && e.newValue !== null) {
+ setStoredValue(safeJsonParse(e.newValue, initialValue));
+ }
+ };
+
+ window.addEventListener('storage', handleStorageChange);
+ return () => window.removeEventListener('storage', handleStorageChange);
+ }, [key, initialValue]);
+
+ return [storedValue, setValue, removeValue];
+};
\ No newline at end of file
diff --git a/src/hooks/useNotifications.js b/src/hooks/useNotifications.js
new file mode 100644
index 00000000..61775031
--- /dev/null
+++ b/src/hooks/useNotifications.js
@@ -0,0 +1,273 @@
+// ============================================
+// hooks/useNotifications.js
+// ============================================
+
+import { useState, useCallback, useMemo } from 'react';
+import { initialNotifications } from '../data/initialNotifications';
+import { useLocalStorage } from './useLocalStorage';
+import { sortBy, groupBy } from '../utils/helpers';
+
+export const useNotifications = () => {
+ const [notifications, setNotifications] = useLocalStorage('dashboard_notifications', initialNotifications);
+ const [filterType, setFilterType] = useState('all');
+ const [filterCategory, setFilterCategory] = useState('all');
+ const [showOnlyUnread, setShowOnlyUnread] = useState(false);
+ const [loading, setLoading] = useState(false);
+
+ // Filtered notifications
+ const filteredNotifications = useMemo(() => {
+ let result = [...notifications];
+
+ // Filter by type
+ if (filterType !== 'all') {
+ result = result.filter(n => n.type === filterType);
+ }
+
+ // Filter by category
+ if (filterCategory !== 'all') {
+ result = result.filter(n => n.category === filterCategory);
+ }
+
+ // Filter by read status
+ if (showOnlyUnread) {
+ result = result.filter(n => !n.read);
+ }
+
+ // Sort by timestamp (newest first)
+ result = sortBy(result, 'timestamp', 'desc');
+
+ return result;
+ }, [notifications, filterType, filterCategory, showOnlyUnread]);
+
+ // Add new notification
+ const addNotification = useCallback((notificationData) => {
+ setLoading(true);
+
+ try {
+ const newNotification = {
+ ...notificationData,
+ id: Date.now(),
+ timestamp: new Date().toISOString(),
+ read: false,
+ priority: notificationData.priority || 'medium',
+ };
+
+ setNotifications(prev => [newNotification, ...prev]);
+ return { success: true, notification: newNotification };
+ } catch (error) {
+ console.error('Error adding notification:', error);
+ return { success: false, error: error.message };
+ } finally {
+ setLoading(false);
+ }
+ }, [setNotifications]);
+
+ // Mark notification as read
+ const markAsRead = useCallback((id) => {
+ setLoading(true);
+
+ try {
+ setNotifications(prev =>
+ prev.map(notif =>
+ notif.id === id ? { ...notif, read: true } : notif
+ )
+ );
+ return { success: true };
+ } catch (error) {
+ console.error('Error marking notification as read:', error);
+ return { success: false, error: error.message };
+ } finally {
+ setLoading(false);
+ }
+ }, [setNotifications]);
+
+ // Mark notification as unread
+ const markAsUnread = useCallback((id) => {
+ setLoading(true);
+
+ try {
+ setNotifications(prev =>
+ prev.map(notif =>
+ notif.id === id ? { ...notif, read: false } : notif
+ )
+ );
+ return { success: true };
+ } catch (error) {
+ console.error('Error marking notification as unread:', error);
+ return { success: false, error: error.message };
+ } finally {
+ setLoading(false);
+ }
+ }, [setNotifications]);
+
+ // Mark all as read
+ const markAllAsRead = useCallback(() => {
+ setLoading(true);
+
+ try {
+ setNotifications(prev =>
+ prev.map(notif => ({ ...notif, read: true }))
+ );
+ return { success: true };
+ } catch (error) {
+ console.error('Error marking all as read:', error);
+ return { success: false, error: error.message };
+ } finally {
+ setLoading(false);
+ }
+ }, [setNotifications]);
+
+ // Delete notification
+ const deleteNotification = useCallback((id) => {
+ setLoading(true);
+
+ try {
+ setNotifications(prev => prev.filter(notif => notif.id !== id));
+ return { success: true };
+ } catch (error) {
+ console.error('Error deleting notification:', error);
+ return { success: false, error: error.message };
+ } finally {
+ setLoading(false);
+ }
+ }, [setNotifications]);
+
+ // Delete all read notifications
+ const deleteAllRead = useCallback(() => {
+ setLoading(true);
+
+ try {
+ setNotifications(prev => prev.filter(notif => !notif.read));
+ return { success: true };
+ } catch (error) {
+ console.error('Error deleting read notifications:', error);
+ return { success: false, error: error.message };
+ } finally {
+ setLoading(false);
+ }
+ }, [setNotifications]);
+
+ // Clear all notifications
+ const clearAll = useCallback(() => {
+ setLoading(true);
+
+ try {
+ setNotifications([]);
+ return { success: true };
+ } catch (error) {
+ console.error('Error clearing notifications:', error);
+ return { success: false, error: error.message };
+ } finally {
+ setLoading(false);
+ }
+ }, [setNotifications]);
+
+ // Get notification by ID
+ const getNotificationById = useCallback((id) => {
+ return notifications.find(notif => notif.id === id);
+ }, [notifications]);
+
+ // Statistics
+ const statistics = useMemo(() => {
+ const unreadCount = notifications.filter(n => !n.read).length;
+ const byType = groupBy(notifications, 'type');
+ const byCategory = groupBy(notifications, 'category');
+ const byPriority = groupBy(notifications, 'priority');
+
+ // Get counts for each type
+ const typeCounts = {
+ success: byType.success?.length || 0,
+ warning: byType.warning?.length || 0,
+ error: byType.error?.length || 0,
+ info: byType.info?.length || 0,
+ };
+
+ // Get recent notifications (last 24 hours)
+ const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
+ const recentCount = notifications.filter(n =>
+ new Date(n.timestamp) > oneDayAgo
+ ).length;
+
+ return {
+ total: notifications.length,
+ unreadCount,
+ readCount: notifications.length - unreadCount,
+ recentCount,
+ byType: Object.keys(byType).map(type => ({
+ type,
+ count: byType[type].length
+ })),
+ byCategory: Object.keys(byCategory).map(category => ({
+ category,
+ count: byCategory[category].length
+ })),
+ byPriority: Object.keys(byPriority).map(priority => ({
+ priority,
+ count: byPriority[priority].length
+ })),
+ typeCounts,
+ };
+ }, [notifications]);
+
+ // Get unread notifications
+ const getUnreadNotifications = useCallback(() => {
+ return notifications.filter(n => !n.read);
+ }, [notifications]);
+
+ // Get recent notifications (limit)
+ const getRecentNotifications = useCallback((limit = 5) => {
+ return sortBy(notifications, 'timestamp', 'desc').slice(0, limit);
+ }, [notifications]);
+
+ // Get notifications by type
+ const getNotificationsByType = useCallback((type) => {
+ return notifications.filter(n => n.type === type);
+ }, [notifications]);
+
+ // Get notifications by category
+ const getNotificationsByCategory = useCallback((category) => {
+ return notifications.filter(n => n.category === category);
+ }, [notifications]);
+
+ // Reset filters
+ const resetFilters = useCallback(() => {
+ setFilterType('all');
+ setFilterCategory('all');
+ setShowOnlyUnread(false);
+ }, []);
+
+ return {
+ // Data
+ notifications: filteredNotifications,
+ allNotifications: notifications,
+ loading,
+
+ // Filters
+ filterType,
+ setFilterType,
+ filterCategory,
+ setFilterCategory,
+ showOnlyUnread,
+ setShowOnlyUnread,
+
+ // Actions
+ addNotification,
+ markAsRead,
+ markAsUnread,
+ markAllAsRead,
+ deleteNotification,
+ deleteAllRead,
+ clearAll,
+ getNotificationById,
+ resetFilters,
+
+ // Helpers
+ getUnreadNotifications,
+ getRecentNotifications,
+ getNotificationsByType,
+ getNotificationsByCategory,
+
+ // Statistics
+ statistics,
+ };
+};
\ No newline at end of file
diff --git a/src/hooks/useProfile.js b/src/hooks/useProfile.js
new file mode 100644
index 00000000..44526613
--- /dev/null
+++ b/src/hooks/useProfile.js
@@ -0,0 +1,222 @@
+// ============================================
+// hooks/useProfile.js
+// ============================================
+
+import { useState, useCallback } from 'react';
+import { useLocalStorage } from './useLocalStorage';
+import { validateEmail, validatePhone, validateRequired, validateLength } from '../utils/validators';
+import { VALIDATION_RULES } from '../utils/constants';
+
+const initialProfileData = {
+ name: "Richard Davis",
+ title: "Chief Executive Officer",
+ email: "richard.davis@company.com",
+ phone: "+1 (555) 123-4567",
+ location: "San Francisco, CA",
+ bio: "Experienced technology executive with over 15 years of leadership in enterprise software development. Focused on building high-performing teams and delivering innovative solutions.",
+ avatar: "https://i.pravatar.cc/300?img=33",
+ company: "TechCorp Industries",
+ website: "https://techcorp.com",
+ timezone: "PST",
+ language: "English",
+ socialLinks: {
+ linkedin: "",
+ twitter: "",
+ github: "",
+ }
+};
+
+export const useProfile = () => {
+ const [profileData, setProfileData] = useLocalStorage('dashboard_profile', initialProfileData);
+ const [isEditing, setIsEditing] = useState(false);
+ const [tempProfile, setTempProfile] = useState(profileData);
+ const [loading, setLoading] = useState(false);
+ const [errors, setErrors] = useState({});
+
+ // Start editing
+ const startEditing = useCallback(() => {
+ setTempProfile(profileData);
+ setIsEditing(true);
+ setErrors({});
+ }, [profileData]);
+
+ // Cancel editing
+ const cancelEditing = useCallback(() => {
+ setTempProfile(profileData);
+ setIsEditing(false);
+ setErrors({});
+ }, [profileData]);
+
+ // Update temp profile field
+ const updateField = useCallback((field, value) => {
+ setTempProfile(prev => ({
+ ...prev,
+ [field]: value
+ }));
+
+ // Clear error for this field
+ if (errors[field]) {
+ setErrors(prev => {
+ const newErrors = { ...prev };
+ delete newErrors[field];
+ return newErrors;
+ });
+ }
+ }, [errors]);
+
+ // Update social link
+ const updateSocialLink = useCallback((platform, url) => {
+ setTempProfile(prev => ({
+ ...prev,
+ socialLinks: {
+ ...prev.socialLinks,
+ [platform]: url
+ }
+ }));
+ }, []);
+
+ // Validate profile data
+ const validateProfile = useCallback((data) => {
+ const newErrors = {};
+
+ // Validate name
+ const nameValidation = validateRequired(data.name, 'Name');
+ if (!nameValidation.valid) {
+ newErrors.name = nameValidation.message;
+ } else {
+ const lengthValidation = validateLength(
+ data.name,
+ 2,
+ VALIDATION_RULES.MAX_NAME_LENGTH,
+ 'Name'
+ );
+ if (!lengthValidation.valid) {
+ newErrors.name = lengthValidation.message;
+ }
+ }
+
+ // Validate email
+ const emailValidation = validateEmail(data.email);
+ if (!emailValidation.valid) {
+ newErrors.email = emailValidation.message;
+ }
+
+ // Validate phone (if provided)
+ if (data.phone) {
+ const phoneValidation = validatePhone(data.phone);
+ if (!phoneValidation.valid) {
+ newErrors.phone = phoneValidation.message;
+ }
+ }
+
+ // Validate bio length (if provided)
+ if (data.bio) {
+ const bioValidation = validateLength(
+ data.bio,
+ 0,
+ VALIDATION_RULES.MAX_BIO_LENGTH,
+ 'Bio'
+ );
+ if (!bioValidation.valid) {
+ newErrors.bio = bioValidation.message;
+ }
+ }
+
+ return {
+ isValid: Object.keys(newErrors).length === 0,
+ errors: newErrors
+ };
+ }, []);
+
+ // Save profile
+ const saveProfile = useCallback(() => {
+ setLoading(true);
+
+ try {
+ // Validate profile data
+ const validation = validateProfile(tempProfile);
+
+ if (!validation.isValid) {
+ setErrors(validation.errors);
+ setLoading(false);
+ return { success: false, errors: validation.errors };
+ }
+
+ // Save profile
+ setProfileData(tempProfile);
+ setIsEditing(false);
+ setErrors({});
+
+ return { success: true };
+ } catch (error) {
+ console.error('Error saving profile:', error);
+ return { success: false, error: error.message };
+ } finally {
+ setLoading(false);
+ }
+ }, [tempProfile, validateProfile, setProfileData]);
+
+ // Update avatar
+ const updateAvatar = useCallback((avatarUrl) => {
+ setLoading(true);
+
+ try {
+ setProfileData(prev => ({
+ ...prev,
+ avatar: avatarUrl
+ }));
+
+ if (isEditing) {
+ setTempProfile(prev => ({
+ ...prev,
+ avatar: avatarUrl
+ }));
+ }
+
+ return { success: true };
+ } catch (error) {
+ console.error('Error updating avatar:', error);
+ return { success: false, error: error.message };
+ } finally {
+ setLoading(false);
+ }
+ }, [setProfileData, isEditing]);
+
+ // Reset profile to defaults
+ const resetProfile = useCallback(() => {
+ setProfileData(initialProfileData);
+ setTempProfile(initialProfileData);
+ setIsEditing(false);
+ setErrors({});
+ }, [setProfileData]);
+
+ // Quick update (without validation)
+ const quickUpdate = useCallback((field, value) => {
+ setProfileData(prev => ({
+ ...prev,
+ [field]: value
+ }));
+ }, [setProfileData]);
+
+ return {
+ // Data
+ profile: profileData,
+ tempProfile,
+ isEditing,
+ loading,
+ errors,
+
+ // Actions
+ startEditing,
+ cancelEditing,
+ saveProfile,
+ updateField,
+ updateSocialLink,
+ updateAvatar,
+ resetProfile,
+ quickUpdate,
+
+ // Validation
+ validateProfile,
+ };
+};
\ No newline at end of file
diff --git a/src/hooks/useProjects.js b/src/hooks/useProjects.js
new file mode 100644
index 00000000..a2f45b5b
--- /dev/null
+++ b/src/hooks/useProjects.js
@@ -0,0 +1,249 @@
+// ============================================
+// hooks/useProjects.js
+// ============================================
+
+import { useState, useCallback, useMemo } from 'react';
+import { initialProjects } from '../data/initialProjects';
+import { useLocalStorage } from './useLocalStorage';
+import { searchFilter, sortBy } from '../utils/helpers';
+import { validateProjectName, validateBudget, validateDate } from '../utils/validators';
+
+export const useProjects = () => {
+ const [projects, setProjects] = useLocalStorage('dashboard_projects', initialProjects);
+ const [searchTerm, setSearchTerm] = useState('');
+ const [filterStatus, setFilterStatus] = useState('all');
+ const [filterPriority, setFilterPriority] = useState('all');
+ const [sortConfig, setSortConfig] = useState({ key: 'id', order: 'asc' });
+ const [loading, setLoading] = useState(false);
+
+ // Filtered and sorted projects with useMemo for performance
+ const filteredProjects = useMemo(() => {
+ let result = [...projects];
+
+ // Apply search filter
+ if (searchTerm) {
+ result = searchFilter(result, searchTerm, ['name', 'description', 'client']);
+ }
+
+ // Apply status filter
+ if (filterStatus !== 'all') {
+ result = result.filter(p => p.status === filterStatus);
+ }
+
+ // Apply priority filter
+ if (filterPriority !== 'all') {
+ result = result.filter(p => p.priority === filterPriority);
+ }
+
+ // Apply sorting
+ if (sortConfig.key) {
+ result = sortBy(result, sortConfig.key, sortConfig.order);
+ }
+
+ return result;
+ }, [projects, searchTerm, filterStatus, filterPriority, sortConfig]);
+
+ // Add new project
+ const addProject = useCallback((projectData) => {
+ setLoading(true);
+
+ try {
+ // Validate project data
+ const nameValidation = validateProjectName(projectData.name);
+ if (!nameValidation.valid) {
+ throw new Error(nameValidation.message);
+ }
+
+ const budgetValidation = validateBudget(projectData.budget);
+ if (!budgetValidation.valid) {
+ throw new Error(budgetValidation.message);
+ }
+
+ const deadlineValidation = validateDate(projectData.deadline, true);
+ if (!deadlineValidation.valid) {
+ throw new Error(deadlineValidation.message);
+ }
+
+ const newProject = {
+ ...projectData,
+ id: Date.now(),
+ completion: projectData.completion || 0,
+ status: projectData.status || 'active',
+ members: projectData.members || [],
+ tasks: projectData.tasks || { total: 0, completed: 0, inProgress: 0, pending: 0 },
+ startDate: projectData.startDate || new Date().toISOString().split('T')[0],
+ };
+
+ setProjects(prev => [...prev, newProject]);
+ return { success: true, project: newProject };
+ } catch (error) {
+ console.error('Error adding project:', error);
+ return { success: false, error: error.message };
+ } finally {
+ setLoading(false);
+ }
+ }, [setProjects]);
+
+ // Update existing project
+ const updateProject = useCallback((id, updates) => {
+ setLoading(true);
+
+ try {
+ // Validate updates if they contain critical fields
+ if (updates.name) {
+ const nameValidation = validateProjectName(updates.name);
+ if (!nameValidation.valid) {
+ throw new Error(nameValidation.message);
+ }
+ }
+
+ if (updates.budget) {
+ const budgetValidation = validateBudget(updates.budget);
+ if (!budgetValidation.valid) {
+ throw new Error(budgetValidation.message);
+ }
+ }
+
+ if (updates.deadline) {
+ const deadlineValidation = validateDate(updates.deadline, true);
+ if (!deadlineValidation.valid) {
+ throw new Error(deadlineValidation.message);
+ }
+ }
+
+ setProjects(prev =>
+ prev.map(project =>
+ project.id === id ? { ...project, ...updates } : project
+ )
+ );
+
+ return { success: true };
+ } catch (error) {
+ console.error('Error updating project:', error);
+ return { success: false, error: error.message };
+ } finally {
+ setLoading(false);
+ }
+ }, [setProjects]);
+
+ // Delete project
+ const deleteProject = useCallback((id) => {
+ setLoading(true);
+
+ try {
+ setProjects(prev => prev.filter(project => project.id !== id));
+ return { success: true };
+ } catch (error) {
+ console.error('Error deleting project:', error);
+ return { success: false, error: error.message };
+ } finally {
+ setLoading(false);
+ }
+ }, [setProjects]);
+
+ // Get project by ID
+ const getProjectById = useCallback((id) => {
+ return projects.find(project => project.id === id);
+ }, [projects]);
+
+ // Update project completion
+ const updateCompletion = useCallback((id, completion) => {
+ return updateProject(id, { completion });
+ }, [updateProject]);
+
+ // Add member to project
+ const addMemberToProject = useCallback((projectId, member) => {
+ const project = getProjectById(projectId);
+ if (!project) return { success: false, error: 'Project not found' };
+
+ const updatedMembers = [...project.members, member];
+ return updateProject(projectId, { members: updatedMembers });
+ }, [getProjectById, updateProject]);
+
+ // Remove member from project
+ const removeMemberFromProject = useCallback((projectId, memberId) => {
+ const project = getProjectById(projectId);
+ if (!project) return { success: false, error: 'Project not found' };
+
+ const updatedMembers = project.members.filter(m => m.id !== memberId);
+ return updateProject(projectId, { members: updatedMembers });
+ }, [getProjectById, updateProject]);
+
+ // Update project status
+ const updateStatus = useCallback((id, status) => {
+ return updateProject(id, { status });
+ }, [updateProject]);
+
+ // Statistics
+ const statistics = useMemo(() => {
+ const totalBudget = projects.reduce((sum, p) => sum + (p.budget || 0), 0);
+ const avgCompletion = projects.length > 0
+ ? projects.reduce((sum, p) => sum + p.completion, 0) / projects.length
+ : 0;
+
+ const statusCounts = projects.reduce((acc, p) => {
+ acc[p.status] = (acc[p.status] || 0) + 1;
+ return acc;
+ }, {});
+
+ const priorityCounts = projects.reduce((acc, p) => {
+ acc[p.priority] = (acc[p.priority] || 0) + 1;
+ return acc;
+ }, {});
+
+ return {
+ total: projects.length,
+ totalBudget,
+ avgCompletion: Math.round(avgCompletion),
+ byStatus: statusCounts,
+ byPriority: priorityCounts,
+ };
+ }, [projects]);
+
+ // Reset filters
+ const resetFilters = useCallback(() => {
+ setSearchTerm('');
+ setFilterStatus('all');
+ setFilterPriority('all');
+ setSortConfig({ key: 'id', order: 'asc' });
+ }, []);
+
+ // Sort projects
+ const sortProjects = useCallback((key) => {
+ setSortConfig(prev => ({
+ key,
+ order: prev.key === key && prev.order === 'asc' ? 'desc' : 'asc'
+ }));
+ }, []);
+
+ return {
+ // Data
+ projects: filteredProjects,
+ allProjects: projects,
+ loading,
+
+ // Filters
+ searchTerm,
+ setSearchTerm,
+ filterStatus,
+ setFilterStatus,
+ filterPriority,
+ setFilterPriority,
+ sortConfig,
+
+ // Actions
+ addProject,
+ updateProject,
+ deleteProject,
+ getProjectById,
+ updateCompletion,
+ addMemberToProject,
+ removeMemberFromProject,
+ updateStatus,
+ sortProjects,
+ resetFilters,
+
+ // Statistics
+ statistics,
+ };
+};
\ No newline at end of file
diff --git a/src/hooks/useTeamMembers.js b/src/hooks/useTeamMembers.js
new file mode 100644
index 00000000..9f14fc9d
--- /dev/null
+++ b/src/hooks/useTeamMembers.js
@@ -0,0 +1,289 @@
+// ============================================
+// hooks/useTeamMembers.js
+// ============================================
+
+import { useState, useCallback, useMemo } from 'react';
+import { initialTeamMembers } from '../data/initialTeamMembers';
+import { useLocalStorage } from './useLocalStorage';
+import { searchFilter, sortBy, groupBy } from '../utils/helpers';
+import { validateEmail, validatePhone, validateRequired } from '../utils/validators';
+
+export const useTeamMembers = () => {
+ const [teamMembers, setTeamMembers] = useLocalStorage('dashboard_team', initialTeamMembers);
+ const [searchTerm, setSearchTerm] = useState('');
+ const [filterDepartment, setFilterDepartment] = useState('all');
+ const [filterStatus, setFilterStatus] = useState('all');
+ const [sortConfig, setSortConfig] = useState({ key: 'name', order: 'asc' });
+ const [loading, setLoading] = useState(false);
+
+ // Filtered and sorted team members
+ const filteredMembers = useMemo(() => {
+ let result = [...teamMembers];
+
+ // Apply search filter
+ if (searchTerm) {
+ result = searchFilter(result, searchTerm, ['name', 'email', 'role', 'department']);
+ }
+
+ // Apply department filter
+ if (filterDepartment !== 'all') {
+ result = result.filter(m => m.department === filterDepartment);
+ }
+
+ // Apply status filter
+ if (filterStatus !== 'all') {
+ result = result.filter(m => m.status === filterStatus);
+ }
+
+ // Apply sorting
+ if (sortConfig.key) {
+ result = sortBy(result, sortConfig.key, sortConfig.order);
+ }
+
+ return result;
+ }, [teamMembers, searchTerm, filterDepartment, filterStatus, sortConfig]);
+
+ // Add new team member
+ const addMember = useCallback((memberData) => {
+ setLoading(true);
+
+ try {
+ // Validate member data
+ const nameValidation = validateRequired(memberData.name, 'Name');
+ if (!nameValidation.valid) {
+ throw new Error(nameValidation.message);
+ }
+
+ const emailValidation = validateEmail(memberData.email);
+ if (!emailValidation.valid) {
+ throw new Error(emailValidation.message);
+ }
+
+ if (memberData.phone) {
+ const phoneValidation = validatePhone(memberData.phone);
+ if (!phoneValidation.valid) {
+ throw new Error(phoneValidation.message);
+ }
+ }
+
+ // Check for duplicate email
+ const emailExists = teamMembers.some(m => m.email === memberData.email);
+ if (emailExists) {
+ throw new Error('A team member with this email already exists');
+ }
+
+ const newMember = {
+ ...memberData,
+ id: Date.now(),
+ status: memberData.status || 'offline',
+ joinDate: memberData.joinDate || new Date().toISOString().split('T')[0],
+ avatar: memberData.avatar || `https://i.pravatar.cc/150?img=${Math.floor(Math.random() * 70)}`,
+ projects: memberData.projects || [],
+ tasksCompleted: 0,
+ hoursWorked: 0,
+ rating: 0,
+ };
+
+ setTeamMembers(prev => [...prev, newMember]);
+ return { success: true, member: newMember };
+ } catch (error) {
+ console.error('Error adding team member:', error);
+ return { success: false, error: error.message };
+ } finally {
+ setLoading(false);
+ }
+ }, [teamMembers, setTeamMembers]);
+
+ // Update existing team member
+ const updateMember = useCallback((id, updates) => {
+ setLoading(true);
+
+ try {
+ // Validate updates if they contain critical fields
+ if (updates.email) {
+ const emailValidation = validateEmail(updates.email);
+ if (!emailValidation.valid) {
+ throw new Error(emailValidation.message);
+ }
+
+ // Check for duplicate email (excluding current member)
+ const emailExists = teamMembers.some(m => m.id !== id && m.email === updates.email);
+ if (emailExists) {
+ throw new Error('A team member with this email already exists');
+ }
+ }
+
+ if (updates.phone) {
+ const phoneValidation = validatePhone(updates.phone);
+ if (!phoneValidation.valid) {
+ throw new Error(phoneValidation.message);
+ }
+ }
+
+ setTeamMembers(prev =>
+ prev.map(member =>
+ member.id === id ? { ...member, ...updates } : member
+ )
+ );
+
+ return { success: true };
+ } catch (error) {
+ console.error('Error updating team member:', error);
+ return { success: false, error: error.message };
+ } finally {
+ setLoading(false);
+ }
+ }, [teamMembers, setTeamMembers]);
+
+ // Delete team member
+ const deleteMember = useCallback((id) => {
+ setLoading(true);
+
+ try {
+ setTeamMembers(prev => prev.filter(member => member.id !== id));
+ return { success: true };
+ } catch (error) {
+ console.error('Error deleting team member:', error);
+ return { success: false, error: error.message };
+ } finally {
+ setLoading(false);
+ }
+ }, [setTeamMembers]);
+
+ // Get member by ID
+ const getMemberById = useCallback((id) => {
+ return teamMembers.find(member => member.id === id);
+ }, [teamMembers]);
+
+ // Update member status
+ const updateStatus = useCallback((id, status) => {
+ return updateMember(id, { status });
+ }, [updateMember]);
+
+ // Assign member to project
+ const assignToProject = useCallback((memberId, projectId) => {
+ const member = getMemberById(memberId);
+ if (!member) return { success: false, error: 'Member not found' };
+
+ const updatedProjects = [...(member.projects || [])];
+ if (!updatedProjects.includes(projectId)) {
+ updatedProjects.push(projectId);
+ }
+
+ return updateMember(memberId, { projects: updatedProjects });
+ }, [getMemberById, updateMember]);
+
+ // Remove member from project
+ const removeFromProject = useCallback((memberId, projectId) => {
+ const member = getMemberById(memberId);
+ if (!member) return { success: false, error: 'Member not found' };
+
+ const updatedProjects = (member.projects || []).filter(pid => pid !== projectId);
+ return updateMember(memberId, { projects: updatedProjects });
+ }, [getMemberById, updateMember]);
+
+ // Update member rating
+ const updateRating = useCallback((id, rating) => {
+ if (rating < 0 || rating > 5) {
+ return { success: false, error: 'Rating must be between 0 and 5' };
+ }
+ return updateMember(id, { rating });
+ }, [updateMember]);
+
+ // Statistics
+ const statistics = useMemo(() => {
+ const byDepartment = groupBy(teamMembers, 'department');
+ const byStatus = groupBy(teamMembers, 'status');
+
+ const avgRating = teamMembers.length > 0
+ ? teamMembers.reduce((sum, m) => sum + (m.rating || 0), 0) / teamMembers.length
+ : 0;
+
+ const totalTasksCompleted = teamMembers.reduce((sum, m) => sum + (m.tasksCompleted || 0), 0);
+ const totalHoursWorked = teamMembers.reduce((sum, m) => sum + (m.hoursWorked || 0), 0);
+
+ const onlineCount = teamMembers.filter(m => m.status === 'online').length;
+ const offlineCount = teamMembers.filter(m => m.status === 'offline').length;
+
+ return {
+ total: teamMembers.length,
+ byDepartment: Object.keys(byDepartment).map(dept => ({
+ department: dept,
+ count: byDepartment[dept].length
+ })),
+ byStatus: Object.keys(byStatus).map(status => ({
+ status,
+ count: byStatus[status].length
+ })),
+ avgRating: avgRating.toFixed(1),
+ totalTasksCompleted,
+ totalHoursWorked,
+ onlineCount,
+ offlineCount,
+ activePercentage: teamMembers.length > 0
+ ? Math.round((onlineCount / teamMembers.length) * 100)
+ : 0
+ };
+ }, [teamMembers]);
+
+ // Get members by department
+ const getMembersByDepartment = useCallback((department) => {
+ return teamMembers.filter(m => m.department === department);
+ }, [teamMembers]);
+
+ // Get online members
+ const getOnlineMembers = useCallback(() => {
+ return teamMembers.filter(m => m.status === 'online');
+ }, [teamMembers]);
+
+ // Reset filters
+ const resetFilters = useCallback(() => {
+ setSearchTerm('');
+ setFilterDepartment('all');
+ setFilterStatus('all');
+ setSortConfig({ key: 'name', order: 'asc' });
+ }, []);
+
+ // Sort members
+ const sortMembers = useCallback((key) => {
+ setSortConfig(prev => ({
+ key,
+ order: prev.key === key && prev.order === 'asc' ? 'desc' : 'asc'
+ }));
+ }, []);
+
+ return {
+ // Data
+ members: filteredMembers,
+ allMembers: teamMembers,
+ loading,
+
+ // Filters
+ searchTerm,
+ setSearchTerm,
+ filterDepartment,
+ setFilterDepartment,
+ filterStatus,
+ setFilterStatus,
+ sortConfig,
+
+ // Actions
+ addMember,
+ updateMember,
+ deleteMember,
+ getMemberById,
+ updateStatus,
+ assignToProject,
+ removeFromProject,
+ updateRating,
+ sortMembers,
+ resetFilters,
+
+ // Helpers
+ getMembersByDepartment,
+ getOnlineMembers,
+
+ // Statistics
+ statistics,
+ };
+};
\ No newline at end of file
diff --git a/src/index.css b/src/index.css
new file mode 100644
index 00000000..0a70d518
--- /dev/null
+++ b/src/index.css
@@ -0,0 +1,102 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+/* Custom Scrollbar */
+::-webkit-scrollbar {
+ width: 8px;
+ height: 8px;
+}
+
+::-webkit-scrollbar-track {
+ background: #f1f5f9;
+ border-radius: 10px;
+}
+
+::-webkit-scrollbar-thumb {
+ background: linear-gradient(180deg, #3b82f6, #8b5cf6);
+ border-radius: 10px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background: linear-gradient(180deg, #2563eb, #7c3aed);
+}
+
+/* Animations */
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+.animate-fade-in {
+ animation: fadeIn 0.5s ease-out forwards;
+ opacity: 0;
+}
+
+/* Custom Range Input */
+input[type="range"] {
+ -webkit-appearance: none;
+ appearance: none;
+ outline: none;
+}
+
+input[type="range"]::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ background: linear-gradient(135deg, #3b82f6, #8b5cf6);
+ cursor: pointer;
+ box-shadow: 0 2px 8px rgba(59, 130, 246, 0.5);
+ transition: all 0.2s;
+}
+
+input[type="range"]::-webkit-slider-thumb:hover {
+ transform: scale(1.2);
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.7);
+}
+
+input[type="range"]::-moz-range-thumb {
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ background: linear-gradient(135deg, #3b82f6, #8b5cf6);
+ cursor: pointer;
+ border: none;
+ box-shadow: 0 2px 8px rgba(59, 130, 246, 0.5);
+ transition: all 0.2s;
+}
+
+input[type="range"]::-moz-range-thumb:hover {
+ transform: scale(1.2);
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.7);
+}
+
+/* Smooth transitions */
+* {
+ transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform;
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+ transition-duration: 150ms;
+}
+
+/* Glass effect */
+.glass {
+ background: rgba(255, 255, 255, 0.1);
+ backdrop-filter: blur(10px);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+}
+
+/* Gradient text */
+.gradient-text {
+ background: linear-gradient(135deg, #3b82f6, #8b5cf6);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
\ No newline at end of file
diff --git a/src/layouts/auth.jsx b/src/layouts/auth.jsx
deleted file mode 100644
index c7a21165..00000000
--- a/src/layouts/auth.jsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import { Routes, Route } from "react-router-dom";
-import {
- ChartPieIcon,
- UserIcon,
- UserPlusIcon,
- ArrowRightOnRectangleIcon,
-} from "@heroicons/react/24/solid";
-import { Navbar, Footer } from "@/widgets/layout";
-import routes from "@/routes";
-
-export function Auth() {
- const navbarRoutes = [
- {
- name: "dashboard",
- path: "/dashboard/home",
- icon: ChartPieIcon,
- },
- {
- name: "profile",
- path: "/dashboard/home",
- icon: UserIcon,
- },
- {
- name: "sign up",
- path: "/auth/sign-up",
- icon: UserPlusIcon,
- },
- {
- name: "sign in",
- path: "/auth/sign-in",
- icon: ArrowRightOnRectangleIcon,
- },
- ];
-
- return (
-
-
- {routes.map(
- ({ layout, pages }) =>
- layout === "auth" &&
- pages.map(({ path, element }) => (
-
- ))
- )}
-
-
- );
-}
-
-Auth.displayName = "/src/layout/Auth.jsx";
-
-export default Auth;
diff --git a/src/layouts/dashboard.jsx b/src/layouts/dashboard.jsx
deleted file mode 100644
index 888a627a..00000000
--- a/src/layouts/dashboard.jsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import { Routes, Route } from "react-router-dom";
-import { Cog6ToothIcon } from "@heroicons/react/24/solid";
-import { IconButton } from "@material-tailwind/react";
-import {
- Sidenav,
- DashboardNavbar,
- Configurator,
- Footer,
-} from "@/widgets/layout";
-import routes from "@/routes";
-import { useMaterialTailwindController, setOpenConfigurator } from "@/context";
-
-export function Dashboard() {
- const [controller, dispatch] = useMaterialTailwindController();
- const { sidenavType } = controller;
-
- return (
-
-
-
-
-
-
setOpenConfigurator(dispatch, true)}
- >
-
-
-
- {routes.map(
- ({ layout, pages }) =>
- layout === "dashboard" &&
- pages.map(({ path, element }) => (
-
- ))
- )}
-
-
-
-
-
-
- );
-}
-
-Dashboard.displayName = "/src/layout/dashboard.jsx";
-
-export default Dashboard;
diff --git a/src/layouts/index.js b/src/layouts/index.js
deleted file mode 100644
index b58c0568..00000000
--- a/src/layouts/index.js
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from "@/layouts/dashboard";
-export * from "@/layouts/auth";
diff --git a/src/lib/authProviders.js b/src/lib/authProviders.js
new file mode 100644
index 00000000..a660fe54
--- /dev/null
+++ b/src/lib/authProviders.js
@@ -0,0 +1,4 @@
+import { GoogleAuthProvider, GithubAuthProvider } from 'firebase/auth';
+
+export const googleProvider = new GoogleAuthProvider();
+export const githubProvider = new GithubAuthProvider();
diff --git a/src/lib/authService.js b/src/lib/authService.js
new file mode 100644
index 00000000..9c90ad80
--- /dev/null
+++ b/src/lib/authService.js
@@ -0,0 +1,25 @@
+import {
+ signInWithEmailAndPassword,
+ createUserWithEmailAndPassword,
+ signInWithPopup,
+ GoogleAuthProvider,
+ GithubAuthProvider,
+ signOut,
+} from 'firebase/auth';
+
+import { auth } from './firebase';
+
+const googleProvider = new GoogleAuthProvider();
+const githubProvider = new GithubAuthProvider();
+
+export const loginWithEmail = ({ email, password }) =>
+ signInWithEmailAndPassword(auth, email, password);
+
+export const signupWithEmail = ({ email, password }) =>
+ createUserWithEmailAndPassword(auth, email, password);
+
+export const loginWithGoogle = () => signInWithPopup(auth, googleProvider);
+
+export const loginWithGithub = () => signInWithPopup(auth, githubProvider);
+
+export const logoutUser = () => signOut(auth);
diff --git a/src/lib/firebase.js b/src/lib/firebase.js
new file mode 100644
index 00000000..9338fb5f
--- /dev/null
+++ b/src/lib/firebase.js
@@ -0,0 +1,16 @@
+import { initializeApp } from 'firebase/app';
+import { getAuth } from 'firebase/auth';
+
+const firebaseConfig = {
+ apiKey: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
+ authDomain: 'XXXXXXXXXXXXXX.firebaseapp.com',
+ projectId: 'XXXXXXXXXXXXXX',
+ storageBucket: 'XXXXXXXXXXXXXX.firebasestorage.app',
+ messagingSenderId: 'XXXXXXXXXXXXXXXXX',
+ appId: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
+ measurementId: 'XXXXXXXXXXXXXXXXXXXXXX',
+};
+
+// Initialize Firebase
+const app = initializeApp(firebaseConfig);
+export const auth = getAuth(app);
diff --git a/src/main.jsx b/src/main.jsx
index a28f5a60..97423125 100644
--- a/src/main.jsx
+++ b/src/main.jsx
@@ -1,30 +1,15 @@
-/**
-=========================================================
-* Material Tailwind Dashboard React - v2.1.0
-=========================================================
-* Product Page: https://www.creative-tim.com/product/material-tailwind-dashboard-react
-* Copyright 2023 Creative Tim (https://www.creative-tim.com)
-* Licensed under MIT (https://github.com/creativetimofficial/material-tailwind-dashboard-react/blob/main/LICENSE.md)
-* Coded by Creative Tim
-=========================================================
-* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-*/
-import React from "react";
-import ReactDOM from "react-dom/client";
-import App from "./App";
-import { BrowserRouter } from "react-router-dom";
-import { ThemeProvider } from "@material-tailwind/react";
-import { MaterialTailwindControllerProvider } from "@/context";
-import "../public/css/tailwind.css";
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+import { AuthProvider, ThemeProvider, DashboardProvider } from './context';
+import App from './App';
+import './index.css';
-ReactDOM.createRoot(document.getElementById("root")).render(
-
-
-
-
-
-
-
-
-
+ReactDOM.createRoot(document.getElementById('root')).render(
+
+
+
+
+
+
+
);
diff --git a/src/pages/HomePage.jsx b/src/pages/HomePage.jsx
new file mode 100644
index 00000000..2e65999b
--- /dev/null
+++ b/src/pages/HomePage.jsx
@@ -0,0 +1,45 @@
+import React from 'react';
+import { StatCard } from '../components/dashboard';
+import { dashboardStatistics } from '../data';
+
+export const HomePage = React.memo(({ projects, teamMembersCount }) => {
+ // Calculate dynamic statistics
+ const statistics = React.useMemo(() => {
+ const stats = [...dashboardStatistics];
+
+ // Update projects count
+ const projectsIndex = stats.findIndex((s) => s.title === 'Active Projects');
+ if (projectsIndex !== -1) {
+ stats[projectsIndex] = {
+ ...stats[projectsIndex],
+ value: projects.length.toString(),
+ rawValue: projects.length,
+ };
+ }
+
+ // Update team members count
+ const teamIndex = stats.findIndex((s) => s.title === 'Team Members');
+ if (teamIndex !== -1) {
+ stats[teamIndex] = {
+ ...stats[teamIndex],
+ value: teamMembersCount.toString(),
+ rawValue: teamMembersCount,
+ };
+ }
+
+ return stats;
+ }, [projects.length, teamMembersCount]);
+
+ return (
+
+ {/* Statistics Grid */}
+
+ {statistics.map((stat) => (
+
+ ))}
+
+
+ );
+});
+
+HomePage.displayName = 'HomePage';
diff --git a/src/pages/NotificationsPage.jsx b/src/pages/NotificationsPage.jsx
new file mode 100644
index 00000000..cdce1e72
--- /dev/null
+++ b/src/pages/NotificationsPage.jsx
@@ -0,0 +1,237 @@
+import React from 'react';
+import { Card, Button, Badge, EmptyState } from '../components/ui';
+import { Typography, IconButton } from '@material-tailwind/react';
+import { formatRelativeTime } from '../utils';
+import {
+ BellIcon,
+ CheckIcon,
+ XMarkIcon,
+ ClockIcon,
+ CheckCircleIcon,
+ ExclamationTriangleIcon,
+ InformationCircleIcon,
+ XCircleIcon,
+} from '@heroicons/react/24/outline';
+
+export const NotificationsPage = React.memo(
+ ({
+ notifications = [],
+ unreadCount = 0,
+ onMarkRead,
+ onMarkAllRead,
+ onDelete,
+ loading = false,
+ }) => {
+ const getNotificationIcon = (type) => {
+ const iconClasses = 'h-5 w-5';
+ switch (type) {
+ case 'success':
+ return ;
+ case 'warning':
+ return ;
+ case 'error':
+ return ;
+ default:
+ return ;
+ }
+ };
+
+ const getNotificationBgColor = (type, read) => {
+ if (read) return 'bg-white';
+ switch (type) {
+ case 'success':
+ return 'bg-green-50';
+ case 'warning':
+ return 'bg-orange-50';
+ case 'error':
+ return 'bg-red-50';
+ default:
+ return 'bg-blue-50';
+ }
+ };
+
+ if (loading) {
+ return (
+
+
+
+ {[...Array(5)].map((_, i) => (
+
+ ))}
+
+
+
+ );
+ }
+
+ return (
+
+ {/* Stats */}
+
+
+
+
+ Total Notifications
+
+
+ {notifications.length}
+
+
+
+
+
+
+
+ Unread
+
+
+ {unreadCount}
+
+
+
+
+
+
+
+ Read
+
+
+ {notifications.length - unreadCount}
+
+
+
+
+
+ {/* Notifications List */}
+
+ {/* Header */}
+
+
+
+
+ Notifications
+
+
+ {unreadCount} unread notification{unreadCount !== 1 ? 's' : ''}
+
+
+ {unreadCount > 0 && (
+
+ )}
+
+
+
+ {/* List */}
+
+ {notifications.length === 0 ? (
+
+ ) : (
+ notifications.map((notification) => (
+
+
+ {/* Icon */}
+
+ {getNotificationIcon(notification.type)}
+
+
+ {/* Content */}
+
+
+
+
+ {notification.title}
+
+ {!notification.read && (
+
+ )}
+
+
+ {/* Priority Badge */}
+ {notification.priority && notification.priority !== 'medium' && (
+
+ {notification.priority}
+
+ )}
+
+
+
+ {notification.message}
+
+
+ {/* Time & Category */}
+
+
+
+
+ {formatRelativeTime(new Date(notification.timestamp))}
+
+
+
+ {notification.category && (
+
+ {notification.category}
+
+ )}
+
+
+
+ {/* Actions */}
+
+ {!notification.read && (
+ onMarkRead?.(notification.id)}
+ title="Mark as read"
+ >
+
+
+ )}
+ onDelete?.(notification.id)}
+ title="Delete"
+ >
+
+
+
+
+
+ ))
+ )}
+
+
+
+ );
+ }
+);
+
+NotificationsPage.displayName = 'NotificationsPage';
diff --git a/src/pages/ProfilePage.jsx b/src/pages/ProfilePage.jsx
new file mode 100644
index 00000000..7eedcaea
--- /dev/null
+++ b/src/pages/ProfilePage.jsx
@@ -0,0 +1,221 @@
+import React from 'react';
+import { Card, Avatar, Button } from '../components/ui';
+import { Typography, Input, Textarea } from '@material-tailwind/react';
+import {
+ PencilIcon,
+ CheckIcon,
+ XMarkIcon,
+ EnvelopeIcon,
+ PhoneIcon,
+ MapPinIcon,
+} from '@heroicons/react/24/outline';
+
+export const ProfilePage = React.memo(
+ ({ profile, isEditing, tempProfile, errors, onStartEdit, onSave, onCancel, onUpdateField }) => {
+ if (!profile) {
+ return (
+
+
+
+ Loading profile...
+
+
+
+ );
+ }
+
+ return (
+
+ {/* Main Profile Card */}
+
+
+
+ {/* Left Side - Avatar & Basic Info */}
+
+
+
+
+ {isEditing ? (
+
+ onUpdateField('name', e.target.value)}
+ error={!!errors?.name}
+ />
+ {errors?.name && (
+
+ {errors.name}
+
+ )}
+ onUpdateField('title', e.target.value)}
+ />
+
+ ) : (
+ <>
+
+ {profile.name}
+
+
+ {profile.title}
+
+
+ {/* Contact Info */}
+
+
+
+ {profile.email}
+
+ {profile.phone && (
+
+ )}
+ {profile.location && (
+
+
+ {profile.location}
+
+ )}
+
+ >
+ )}
+
+
+
+ {/* Right Side - Actions */}
+
+ {isEditing ? (
+ <>
+
+
+ >
+ ) : (
+
+ )}
+
+
+
+ {/* Divider */}
+
+
+ {/* About Section */}
+
+
+ About
+
+ {isEditing ? (
+
+
+ ) : (
+
+ {profile.bio || 'No bio available'}
+
+ )}
+
+
+ {/* Additional Details */}
+ {isEditing && (
+ <>
+
+
+ >
+ )}
+
+
+
+ {/* Additional Info Cards */}
+
+
+
+
+ 24
+
+
+ Projects Completed
+
+
+
+
+
+
+
+ 4.8
+
+
+ Average Rating
+
+
+
+
+
+
+
+ 156
+
+
+ Tasks Completed
+
+
+
+
+
+ );
+ }
+);
+
+ProfilePage.displayName = 'ProfilePage';
diff --git a/src/pages/ProjectsPage.jsx b/src/pages/ProjectsPage.jsx
new file mode 100644
index 00000000..6d2e793c
--- /dev/null
+++ b/src/pages/ProjectsPage.jsx
@@ -0,0 +1,211 @@
+import React from 'react';
+import { ProjectCard, ProjectFilters, ProjectModal, ProjectsStats } from '../components/projects';
+import { PlusIcon, FolderIcon } from '@heroicons/react/24/outline';
+
+export const ProjectsPage = () => {
+ const [projects, setProjects] = React.useState([
+ {
+ id: 1,
+ name: 'E-Commerce Platform',
+ description: 'Building a modern e-commerce platform with React and Node.js',
+ status: 'active',
+ priority: 'high',
+ completion: 75,
+ deadline: '2026-03-15',
+ budget: 50000,
+ team: [1, 2, 3, 4],
+ },
+ {
+ id: 2,
+ name: 'Mobile Banking App',
+ description: 'Secure mobile banking application for iOS and Android',
+ status: 'active',
+ priority: 'critical',
+ completion: 60,
+ deadline: '2026-02-28',
+ budget: 120000,
+ team: [1, 2, 3, 4, 5, 6],
+ },
+ {
+ id: 3,
+ name: 'AI Chatbot Integration',
+ description: 'Integrate AI-powered chatbot for customer support',
+ status: 'paused',
+ priority: 'medium',
+ completion: 40,
+ deadline: '2026-04-10',
+ budget: 35000,
+ team: [1, 2, 3],
+ },
+ {
+ id: 4,
+ name: 'Analytics Dashboard',
+ description: 'Real-time analytics dashboard with data visualization',
+ status: 'completed',
+ priority: 'high',
+ completion: 100,
+ deadline: '2026-01-05',
+ budget: 45000,
+ team: [1, 2, 3, 4, 5],
+ },
+ ]);
+
+ const [searchTerm, setSearchTerm] = React.useState('');
+ const [filterStatus, setFilterStatus] = React.useState('all');
+ const [filterPriority, setFilterPriority] = React.useState('all');
+ const [viewMode, setViewMode] = React.useState('grid');
+ const [modalOpen, setModalOpen] = React.useState(false);
+ const [selectedProject, setSelectedProject] = React.useState(null);
+ const [sortKey, setSortKey] = React.useState('name');
+ const [sortDirection, setSortDirection] = React.useState('asc');
+
+ const filteredProjects = React.useMemo(() => {
+ let filtered = projects.filter((project) => {
+ const matchesSearch =
+ project.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
+ project.description?.toLowerCase().includes(searchTerm.toLowerCase());
+ const matchesStatus = filterStatus === 'all' || project.status === filterStatus;
+ const matchesPriority = filterPriority === 'all' || project.priority === filterPriority;
+
+ return matchesSearch && matchesStatus && matchesPriority;
+ });
+
+ filtered.sort((a, b) => {
+ let aVal = a[sortKey];
+ let bVal = b[sortKey];
+
+ if (sortKey === 'deadline') {
+ aVal = new Date(aVal).getTime();
+ bVal = new Date(bVal).getTime();
+ }
+
+ if (aVal < bVal) return sortDirection === 'asc' ? -1 : 1;
+ if (aVal > bVal) return sortDirection === 'asc' ? 1 : -1;
+ return 0;
+ });
+
+ return filtered;
+ }, [projects, searchTerm, filterStatus, filterPriority, sortKey, sortDirection]);
+
+ const handleAddProject = () => {
+ setSelectedProject(null);
+ setModalOpen(true);
+ };
+
+ const handleEditProject = (project) => {
+ setSelectedProject(project);
+ setModalOpen(true);
+ };
+
+ const handleDeleteProject = (id) => {
+ if (confirm('Are you sure you want to delete this project?')) {
+ setProjects(projects.filter((p) => p.id !== id));
+ }
+ };
+
+ const handleViewProject = (project) => {
+ alert(`Viewing project: ${project.name}\n\nThis would open a detailed view.`);
+ };
+
+ const handleSaveProject = (projectData) => {
+ if (selectedProject) {
+ setProjects(projects.map((p) => (p.id === projectData.id ? projectData : p)));
+ } else {
+ setProjects([...projects, { ...projectData, team: [] }]);
+ }
+ };
+
+ const handleSort = () => {
+ if (sortKey === 'name') {
+ setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc');
+ } else {
+ setSortKey('name');
+ setSortDirection('asc');
+ }
+ };
+
+ return (
+
+
+ {/* Page Header */}
+
+
+
Projects
+
+ Manage and track your projects
+
+
+
+
+
+ {/* Statistics */}
+
+
+ {/* Filters & Search */}
+
+
+ {/* Projects Grid */}
+ {filteredProjects.length === 0 ? (
+
+
+
No projects found
+
+ Get started by creating your first project
+
+
+
+ ) : (
+
+ {filteredProjects.map((project) => (
+
+ ))}
+
+ )}
+
+
+ {/* Modal */}
+
setModalOpen(false)}
+ project={selectedProject}
+ onSave={handleSaveProject}
+ />
+
+ );
+};
+
+ProjectsPage.displayName = 'ProjectsPage';
diff --git a/src/pages/TeamPage.jsx b/src/pages/TeamPage.jsx
new file mode 100644
index 00000000..598a6817
--- /dev/null
+++ b/src/pages/TeamPage.jsx
@@ -0,0 +1,227 @@
+import React from 'react';
+import { Card, Avatar, Badge, Button, Tooltip, EmptyState } from '../components/ui';
+import { formatDate, MEMBER_STATUS_COLORS } from '../utils';
+import { Typography, IconButton } from '@material-tailwind/react';
+import { PlusIcon, PencilIcon, TrashIcon, UsersIcon } from '@heroicons/react/24/outline';
+const TABLE_HEADERS = ['Member', 'Email', 'Role', 'Department', 'Status', 'Join Date', 'Actions'];
+
+export const TeamPage = React.memo(
+ ({ members = [], onAddMember, onEditMember, onDeleteMember, loading = false }) => {
+ if (loading) {
+ return (
+
+
+
+ {[...Array(5)].map((_, i) => (
+
+ ))}
+
+
+
+ );
+ }
+
+ return (
+
+ {/* Team Overview Cards */}
+
+
+
+
+ Total Members
+
+
+ {members.length}
+
+
+
+
+
+
+
+ Online Now
+
+
+ {members.filter((m) => m.status === 'online').length}
+
+
+
+
+
+
+
+ Departments
+
+
+ {new Set(members.map((m) => m.department)).size}
+
+
+
+
+
+
+
+ Avg. Rating
+
+
+ {members.length > 0
+ ? (members.reduce((sum, m) => sum + (m.rating || 0), 0) / members.length).toFixed(
+ 1
+ )
+ : '0.0'}
+
+
+
+
+
+ {/* Team Members Table */}
+
+ {/* Header */}
+
+
+
+
+ Team Members
+
+
+ Manage your team and their roles
+
+
+
+
+
+
+ {/* Table */}
+
+ {members.length === 0 ? (
+
+ Add First Member
+
+ }
+ />
+ ) : (
+
+
+
+ {TABLE_HEADERS.map((header) => (
+ |
+
+ {header}
+
+ |
+ ))}
+
+
+
+ {members.map((member) => (
+
+ ))}
+
+
+ )}
+
+
+
+ );
+ }
+);
+
+TeamPage.displayName = 'TeamPage';
+
+// Member Row Component
+const MemberRow = React.memo(({ member, onEdit, onDelete }) => {
+ return (
+
+ {/* Member */}
+
+
+
+
+
+ {member.name}
+
+ {member.location && (
+
+ {member.location}
+
+ )}
+
+
+ |
+
+ {/* Email */}
+
+
+ {member.email}
+
+ |
+
+ {/* Role */}
+
+
+ {member.role}
+
+ |
+
+ {/* Department */}
+
+
+ {member.department}
+
+ |
+
+ {/* Status */}
+
+
+ {member.status}
+
+ |
+
+ {/* Join Date */}
+
+
+ {formatDate(member.joinDate, 'short')}
+
+ |
+
+ {/* Actions */}
+
+
+
+ onEdit?.(member)}>
+
+
+
+
+ {
+ if (window.confirm(`Remove ${member.name} from the team?`)) {
+ onDelete?.(member.id);
+ }
+ }}
+ >
+
+
+
+
+ |
+
+ );
+});
+
+MemberRow.displayName = 'MemberRow';
diff --git a/src/pages/auth/ForgotPasswordPage.jsx b/src/pages/auth/ForgotPasswordPage.jsx
new file mode 100644
index 00000000..02ecbdb4
--- /dev/null
+++ b/src/pages/auth/ForgotPasswordPage.jsx
@@ -0,0 +1,15 @@
+import React from 'react';
+import { AuthLayout, ForgotPasswordForm } from '../../components/auth';
+import { useAuth } from '../../context';
+
+export const ForgotPasswordPage = React.memo(() => {
+ const { forgotPassword } = useAuth();
+
+ return (
+
+ window.history.back()} />
+
+ );
+});
+
+ForgotPasswordPage.displayName = 'ForgotPasswordPage';
diff --git a/src/pages/auth/LoginPage.jsx b/src/pages/auth/LoginPage.jsx
new file mode 100644
index 00000000..4be2becf
--- /dev/null
+++ b/src/pages/auth/LoginPage.jsx
@@ -0,0 +1,21 @@
+import React from 'react';
+import { AuthLayout, LoginForm } from '../../components/auth';
+import { useAuth } from '../../context';
+
+export const LoginPage = () => {
+ const { login, googleLogin, githubLogin } = useAuth();
+
+ return (
+
+ {}}
+ onSignUp={() => {}}
+ onGoogleLogin={googleLogin}
+ onGithubLogin={githubLogin}
+ />
+
+ );
+};
+
+LoginPage.displayName = 'LoginPage';
diff --git a/src/pages/auth/SignUpPage.jsx b/src/pages/auth/SignUpPage.jsx
new file mode 100644
index 00000000..e0fe3009
--- /dev/null
+++ b/src/pages/auth/SignUpPage.jsx
@@ -0,0 +1,15 @@
+import React from 'react';
+import { AuthLayout, SignUpForm } from '../../components/auth';
+import { useAuth } from '../../context';
+
+export const SignUpPage = React.memo(() => {
+ const { signup, login } = useAuth();
+
+ return (
+
+
+
+ );
+});
+
+SignUpPage.displayName = 'SignUpPage';
diff --git a/src/pages/auth/index.js b/src/pages/auth/index.js
index ca1bbcb6..9948a025 100644
--- a/src/pages/auth/index.js
+++ b/src/pages/auth/index.js
@@ -1,2 +1,3 @@
-export * from "@/pages/auth/sign-in";
-export * from "@/pages/auth/sign-up";
+export { LoginPage } from './LoginPage';
+export { SignUpPage } from './SignUpPage';
+export { ForgotPasswordPage } from './ForgotPasswordPage';
diff --git a/src/pages/auth/sign-in.jsx b/src/pages/auth/sign-in.jsx
deleted file mode 100644
index 3b3da41a..00000000
--- a/src/pages/auth/sign-in.jsx
+++ /dev/null
@@ -1,126 +0,0 @@
-import {
- Card,
- Input,
- Checkbox,
- Button,
- Typography,
-} from "@material-tailwind/react";
-import { Link } from "react-router-dom";
-
-
-export function SignIn() {
- return (
-
-
-
- Sign In
- Enter your email and password to Sign In.
-
-
-
-
-
-

-
-
-
- );
-}
-
-export default SignIn;
diff --git a/src/pages/auth/sign-up.jsx b/src/pages/auth/sign-up.jsx
deleted file mode 100644
index 5f040d04..00000000
--- a/src/pages/auth/sign-up.jsx
+++ /dev/null
@@ -1,94 +0,0 @@
-import {
- Card,
- Input,
- Checkbox,
- Button,
- Typography,
-} from "@material-tailwind/react";
-import { Link } from "react-router-dom";
-
-
-export function SignUp() {
- return (
-
-
-

-
-
-
- Join Us Today
- Enter your email and password to register.
-
-
-
-
-
- );
-}
-
-export default SignUp;
diff --git a/src/pages/dashboard/home.jsx b/src/pages/dashboard/home.jsx
deleted file mode 100644
index 2c700669..00000000
--- a/src/pages/dashboard/home.jsx
+++ /dev/null
@@ -1,258 +0,0 @@
-import React from "react";
-import {
- Typography,
- Card,
- CardHeader,
- CardBody,
- IconButton,
- Menu,
- MenuHandler,
- MenuList,
- MenuItem,
- Avatar,
- Tooltip,
- Progress,
-} from "@material-tailwind/react";
-import {
- EllipsisVerticalIcon,
- ArrowUpIcon,
-} from "@heroicons/react/24/outline";
-import { StatisticsCard } from "@/widgets/cards";
-import { StatisticsChart } from "@/widgets/charts";
-import {
- statisticsCardsData,
- statisticsChartsData,
- projectsTableData,
- ordersOverviewData,
-} from "@/data";
-import { CheckCircleIcon, ClockIcon } from "@heroicons/react/24/solid";
-
-export function Home() {
- return (
-
-
- {statisticsCardsData.map(({ icon, title, footer, ...rest }) => (
-
- {footer.value}
- {footer.label}
-
- }
- />
- ))}
-
-
- {statisticsChartsData.map((props) => (
-
-
- {props.footer}
-
- }
- />
- ))}
-
-
-
-
-
-
- Projects
-
-
-
- 30 done this month
-
-
-
-
-
-
-
-
- {["companies", "members", "budget", "completion"].map(
- (el) => (
- |
-
- {el}
-
- |
- )
- )}
-
-
-
- {projectsTableData.map(
- ({ img, name, members, budget, completion }, key) => {
- const className = `py-3 px-5 ${
- key === projectsTableData.length - 1
- ? ""
- : "border-b border-blue-gray-50"
- }`;
-
- return (
-
- |
-
- |
-
- {members.map(({ img, name }, key) => (
-
-
-
- ))}
- |
-
-
- {budget}
-
- |
-
-
- |
-
- );
- }
- )}
-
-
-
-
-
-
-
- Orders Overview
-
-
-
- 24% this month
-
-
-
- {ordersOverviewData.map(
- ({ icon, color, title, description }, key) => (
-
-
- {React.createElement(icon, {
- className: `!w-5 !h-5 ${color}`,
- })}
-
-
-
- {title}
-
-
- {description}
-
-
-
- )
- )}
-
-
-
-
- );
-}
-
-export default Home;
diff --git a/src/pages/dashboard/index.js b/src/pages/dashboard/index.js
deleted file mode 100644
index 7651895e..00000000
--- a/src/pages/dashboard/index.js
+++ /dev/null
@@ -1,4 +0,0 @@
-export * from "@/pages/dashboard/home";
-export * from "@/pages/dashboard/profile";
-export * from "@/pages/dashboard/tables";
-export * from "@/pages/dashboard/notifications";
diff --git a/src/pages/dashboard/notifications.jsx b/src/pages/dashboard/notifications.jsx
deleted file mode 100644
index f4be88b0..00000000
--- a/src/pages/dashboard/notifications.jsx
+++ /dev/null
@@ -1,88 +0,0 @@
-import React from "react";
-import {
- Typography,
- Alert,
- Card,
- CardHeader,
- CardBody,
-} from "@material-tailwind/react";
-import { InformationCircleIcon } from "@heroicons/react/24/outline";
-
-export function Notifications() {
- const [showAlerts, setShowAlerts] = React.useState({
- blue: true,
- green: true,
- orange: true,
- red: true,
- });
- const [showAlertsWithIcon, setShowAlertsWithIcon] = React.useState({
- blue: true,
- green: true,
- orange: true,
- red: true,
- });
- const alerts = ["gray", "green", "orange", "red"];
-
- return (
-
-
-
-
- Alerts
-
-
-
- {alerts.map((color) => (
- setShowAlerts((current) => ({ ...current, [color]: false }))}
- >
- A simple {color} alert with an example link. Give
- it a click if you like.
-
- ))}
-
-
-
-
-
- Alerts with Icon
-
-
-
- {alerts.map((color) => (
-
- }
- onClose={() => setShowAlertsWithIcon((current) => ({
- ...current,
- [color]: false,
- }))}
- >
- A simple {color} alert with an example link. Give
- it a click if you like.
-
- ))}
-
-
-
- );
-}
-
-export default Notifications;
diff --git a/src/pages/dashboard/profile.jsx b/src/pages/dashboard/profile.jsx
deleted file mode 100644
index 0d9f0115..00000000
--- a/src/pages/dashboard/profile.jsx
+++ /dev/null
@@ -1,221 +0,0 @@
-import {
- Card,
- CardBody,
- CardHeader,
- CardFooter,
- Avatar,
- Typography,
- Tabs,
- TabsHeader,
- Tab,
- Switch,
- Tooltip,
- Button,
-} from "@material-tailwind/react";
-import {
- HomeIcon,
- ChatBubbleLeftEllipsisIcon,
- Cog6ToothIcon,
- PencilIcon,
-} from "@heroicons/react/24/solid";
-import { Link } from "react-router-dom";
-import { ProfileInfoCard, MessageCard } from "@/widgets/cards";
-import { platformSettingsData, conversationsData, projectsData } from "@/data";
-
-export function Profile() {
- return (
- <>
-
-
-
-
-
-
-
-
- Richard Davis
-
-
- CEO / Co-Founder
-
-
-
-
-
-
-
-
- App
-
-
-
- Message
-
-
-
- Settings
-
-
-
-
-
-
-
-
- Platform Settings
-
-
- {platformSettingsData.map(({ title, options }) => (
-
-
- {title}
-
-
- {options.map(({ checked, label }) => (
-
- ))}
-
-
- ))}
-
-
-
-
-
-
-
- ),
- }}
- action={
-
-
-
- }
- />
-
-
- Platform Settings
-
-
- {conversationsData.map((props) => (
-
- reply
-
- }
- />
- ))}
-
-
-
-