diff --git a/apps/web/app/[locale]/dashboard/app-url/page.tsx b/apps/web/app/[locale]/dashboard/app-url/page.tsx
new file mode 100644
index 000000000..fd86ca5ad
--- /dev/null
+++ b/apps/web/app/[locale]/dashboard/app-url/page.tsx
@@ -0,0 +1,11 @@
+import React from 'react'
+
+function AppUrls() {
+ return (
+
+
+
+ )
+}
+
+export default AppUrls
diff --git a/apps/web/app/[locale]/dashboard/team-dashboard/components/dashboard-header.tsx b/apps/web/app/[locale]/dashboard/team-dashboard/components/dashboard-header.tsx
new file mode 100644
index 000000000..ff2b73248
--- /dev/null
+++ b/apps/web/app/[locale]/dashboard/team-dashboard/components/dashboard-header.tsx
@@ -0,0 +1,43 @@
+"use client";
+
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select";
+import { DateRangePicker } from "./date-range-picker";
+
+export function DashboardHeader() {
+ return (
+
+
Team Dashboard
+
+
+
+
+
+
+ );
+}
diff --git a/apps/web/app/[locale]/dashboard/team-dashboard/components/date-range-picker.tsx b/apps/web/app/[locale]/dashboard/team-dashboard/components/date-range-picker.tsx
new file mode 100644
index 000000000..fe2255649
--- /dev/null
+++ b/apps/web/app/[locale]/dashboard/team-dashboard/components/date-range-picker.tsx
@@ -0,0 +1,218 @@
+"use client";
+
+import * as React from "react";
+import { Button } from "@/components/ui/button";
+import { Calendar } from "@/components/ui/calendar";
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/components/ui/popover";
+import { ChevronDown } from "lucide-react";
+import { cn } from "@/lib/utils";
+import { format, startOfWeek, endOfWeek, startOfMonth, endOfMonth, subDays, subWeeks, subMonths, isSameMonth, isSameYear, isEqual } from "date-fns";
+import { DateRange } from "react-day-picker";
+
+interface DateRangePickerProps {
+ className?: string;
+ onDateRangeChange?: (range: DateRange | undefined) => void;
+}
+
+export function DateRangePicker({ className, onDateRangeChange }: DateRangePickerProps) {
+ const [dateRange, setDateRange] = React.useState({
+ from: new Date(),
+ to: new Date(),
+ });
+ const [isPopoverOpen, setIsPopoverOpen] = React.useState(false);
+ const [currentMonth, setCurrentMonth] = React.useState(new Date());
+
+ const handleDateRangeChange = (range: DateRange | undefined) => {
+ setDateRange(range);
+ onDateRangeChange?.(range);
+ };
+
+ const predefinedRanges = [
+ {
+ label: "Today",
+ action: () => {
+ const today = new Date();
+ handleDateRangeChange({ from: today, to: today });
+ },
+ isSelected: (range: DateRange | undefined) => {
+ if (!range?.from || !range?.to) return false;
+ const today = new Date();
+ return isEqual(range.from, today) && isEqual(range.to, today);
+ }
+ },
+ {
+ label: "Yesterday",
+ action: () => {
+ const yesterday = subDays(new Date(), 1);
+ handleDateRangeChange({ from: yesterday, to: yesterday });
+ },
+ isSelected: (range: DateRange | undefined) => {
+ if (!range?.from || !range?.to) return false;
+ const yesterday = subDays(new Date(), 1);
+ return isEqual(range.from, yesterday) && isEqual(range.to, yesterday);
+ }
+ },
+ {
+ label: "Current Week",
+ action: () => {
+ const today = new Date();
+ handleDateRangeChange({
+ from: startOfWeek(today, { weekStartsOn: 1 }),
+ to: endOfWeek(today, { weekStartsOn: 1 })
+ });
+ },
+ isSelected: (range: DateRange | undefined) => {
+ if (!range?.from || !range?.to) return false;
+ const today = new Date();
+ const weekStart = startOfWeek(today, { weekStartsOn: 1 });
+ const weekEnd = endOfWeek(today, { weekStartsOn: 1 });
+ return isEqual(range.from, weekStart) && isEqual(range.to, weekEnd);
+ }
+ },
+ {
+ label: "Last Week",
+ action: () => {
+ const lastWeek = subWeeks(new Date(), 1);
+ handleDateRangeChange({
+ from: startOfWeek(lastWeek, { weekStartsOn: 1 }),
+ to: endOfWeek(lastWeek, { weekStartsOn: 1 })
+ });
+ },
+ isSelected: (range: DateRange | undefined) => {
+ if (!range?.from || !range?.to) return false;
+ const lastWeek = subWeeks(new Date(), 1);
+ const weekStart = startOfWeek(lastWeek, { weekStartsOn: 1 });
+ const weekEnd = endOfWeek(lastWeek, { weekStartsOn: 1 });
+ return isEqual(range.from, weekStart) && isEqual(range.to, weekEnd);
+ }
+ },
+ {
+ label: "Current Month",
+ action: () => {
+ const today = new Date();
+ handleDateRangeChange({
+ from: startOfMonth(today),
+ to: endOfMonth(today)
+ });
+ },
+ isSelected: (range: DateRange | undefined) => {
+ if (!range?.from || !range?.to) return false;
+ const today = new Date();
+ const monthStart = startOfMonth(today);
+ const monthEnd = endOfMonth(today);
+ return isEqual(range.from, monthStart) && isEqual(range.to, monthEnd);
+ }
+ },
+ {
+ label: "Last Month",
+ action: () => {
+ const lastMonth = subMonths(new Date(), 1);
+ handleDateRangeChange({
+ from: startOfMonth(lastMonth),
+ to: endOfMonth(lastMonth)
+ });
+ },
+ isSelected: (range: DateRange | undefined) => {
+ if (!range?.from || !range?.to) return false;
+ const lastMonth = subMonths(new Date(), 1);
+ const monthStart = startOfMonth(lastMonth);
+ const monthEnd = endOfMonth(lastMonth);
+ return isEqual(range.from, monthStart) && isEqual(range.to, monthEnd);
+ }
+ },
+ ];
+
+ const formatDateRange = (range: DateRange) => {
+ if (!range.from) return "Select date range";
+ if (!range.to) return format(range.from, "d MMM yyyy");
+
+ if (isSameYear(range.from, range.to)) {
+ if (isSameMonth(range.from, range.to)) {
+ return `${format(range.from, "d")} - ${format(range.to, "d MMM yyyy")}`;
+ }
+ return `${format(range.from, "d MMM")} - ${format(range.to, "d MMM yyyy")}`;
+ }
+ return `${format(range.from, "d MMM yyyy")} - ${format(range.to, "d MMM yyyy")}`;
+ };
+
+ return (
+
+
+
+
+ e.stopPropagation()}
+ onMouseDown={(e) => e.stopPropagation()}
+ onChange={(e) => e.stopPropagation()}
+ className="p-0 w-auto"
+ align="center"
+ >
+
+
+ {predefinedRanges.map((range) => (
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/web/app/[locale]/dashboard/team-dashboard/components/team-stats-chart.tsx b/apps/web/app/[locale]/dashboard/team-dashboard/components/team-stats-chart.tsx
new file mode 100644
index 000000000..941c667fd
--- /dev/null
+++ b/apps/web/app/[locale]/dashboard/team-dashboard/components/team-stats-chart.tsx
@@ -0,0 +1,116 @@
+"use client";
+
+import { Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis, CartesianGrid } from "recharts";
+import { Button } from "@/components/ui/button";
+import { chartData } from "../data/mock-data";
+
+interface TooltipProps {
+ active?: boolean;
+ payload?: {
+ name: string;
+ value: number;
+ color: string;
+ }[];
+ label?: string;
+}
+const CustomTooltip = ({ active, payload, label }: TooltipProps) => {
+ if (active && payload && payload.length) {
+ return (
+
+
{label}
+ {payload.map((item, index) => (
+
+ {item.name}: {item.value}
+
+ ))}
+
+ );
+ }
+ return null;
+};
+
+export function TeamStatsChart() {
+ return (
+
+
+
+
+
+
+ `${value}`}
+ padding={{ top: 10, bottom: 10 }}
+ tickCount={8}
+ />
+ } />
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/web/app/[locale]/dashboard/team-dashboard/components/team-stats-grid.tsx b/apps/web/app/[locale]/dashboard/team-dashboard/components/team-stats-grid.tsx
new file mode 100644
index 000000000..42dc713b9
--- /dev/null
+++ b/apps/web/app/[locale]/dashboard/team-dashboard/components/team-stats-grid.tsx
@@ -0,0 +1,70 @@
+"use client";
+
+import { Card } from "@/components/ui/card";
+
+const stats = [
+ {
+ title: "Members worked",
+ value: "17",
+ type: "number"
+ },
+ {
+ title: "Tracked",
+ value: "47:23",
+ type: "time",
+ color: "text-blue-500",
+ progress: 70,
+ progressColor: "bg-blue-500"
+ },
+ {
+ title: "Manual",
+ value: "18:33",
+ type: "time",
+ color: "text-red-500",
+ progress: 30,
+ progressColor: "bg-red-500"
+ },
+ {
+ title: "Idle",
+ value: "05:10",
+ type: "time",
+ color: "text-yellow-500",
+ progress: 10,
+ progressColor: "bg-yellow-500"
+ },
+ {
+ title: "Total Hours",
+ value: "70:66",
+ type: "time"
+ }
+];
+
+export function TeamStatsGrid() {
+ return (
+ <>
+
+
+ {stats.map((stat) => (
+
+
+
{stat.title}
+
+ {stat.value}
+
+ {stat.progress && (
+
+ )}
+
+
+ ))}
+
+ >
+ );
+}
diff --git a/apps/web/app/[locale]/dashboard/team-dashboard/components/team-stats-table.tsx b/apps/web/app/[locale]/dashboard/team-dashboard/components/team-stats-table.tsx
new file mode 100644
index 000000000..2de3a9c95
--- /dev/null
+++ b/apps/web/app/[locale]/dashboard/team-dashboard/components/team-stats-table.tsx
@@ -0,0 +1,91 @@
+'use client';
+
+import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
+import { Button } from '@/components/ui/button';
+import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
+import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react';
+import { members } from '../data/mock-data';
+
+const getProgressColor = (activityLevel: string) => {
+ const level = parseInt(activityLevel, 10);
+ if (isNaN(level) || level < 0) return 'bg-gray-300';
+ if (level > 100) return 'bg-green-500';
+ if (level <= 20) return 'bg-red-500';
+ if (level <= 50) return 'bg-yellow-500';
+ return 'bg-green-500';
+};
+
+export function TeamStatsTable() {
+ return (
+
+
+
+
+
+ Member
+ Tracked Time
+ Manual Time
+ Idle Time
+ Unknown Activity
+ Activity Level
+
+
+
+ {members.map((member) => (
+
+
+
+
+
+ {member.name[0]}
+
+ {member.name}
+
+
+ {member.trackedTime}
+ {member.manualTime}
+ {member.idleTime}
+ {member.unknownActivity}
+
+
+
+
{member.activityLevel}
+
+
+
+ ))}
+
+
+
+
+
+
+ Showing 1 to {members.length} of {members.length} entries
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/web/app/[locale]/dashboard/team-dashboard/data/mock-data.ts b/apps/web/app/[locale]/dashboard/team-dashboard/data/mock-data.ts
new file mode 100644
index 000000000..8da4efc4b
--- /dev/null
+++ b/apps/web/app/[locale]/dashboard/team-dashboard/data/mock-data.ts
@@ -0,0 +1,74 @@
+export const chartData = [
+ {
+ date: "Mon",
+ tracked: 4000,
+ manual: 2400,
+ idle: 2400,
+ },
+ {
+ date: "Tue",
+ tracked: 3000,
+ manual: 1398,
+ idle: 2210,
+ },
+ {
+ date: "Wed",
+ tracked: 2000,
+ manual: 9800,
+ idle: 2290,
+ },
+ {
+ date: "Thu",
+ tracked: 2780,
+ manual: 3908,
+ idle: 2000,
+ },
+ {
+ date: "Fri",
+ tracked: 1890,
+ manual: 4800,
+ idle: 2181,
+ },
+ {
+ date: "Sat",
+ tracked: 2390,
+ manual: 3800,
+ idle: 2500,
+ },
+ {
+ date: "Sun",
+ tracked: 3490,
+ manual: 4300,
+ idle: 2100,
+ },
+];
+
+export const members = [
+ {
+ name: "Elanor Pena",
+ avatar: "/avatars/01.png",
+ trackedTime: "8h 12m",
+ manualTime: "1h 5m",
+ idleTime: "45m",
+ unknownActivity: "15m",
+ activityLevel: "85%",
+ },
+ {
+ name: "Devon Lane",
+ avatar: "/avatars/02.png",
+ trackedTime: "7h 30m",
+ manualTime: "2h 15m",
+ idleTime: "30m",
+ unknownActivity: "10m",
+ activityLevel: "45%",
+ },
+ {
+ name: "Brooklyn Simmons",
+ avatar: "/avatars/03.png",
+ trackedTime: "6h 45m",
+ manualTime: "1h 30m",
+ idleTime: "1h",
+ unknownActivity: "20m",
+ activityLevel: "15%",
+ },
+];
diff --git a/apps/web/app/[locale]/dashboard/team-dashboard/page.tsx b/apps/web/app/[locale]/dashboard/team-dashboard/page.tsx
new file mode 100644
index 000000000..4de204a6b
--- /dev/null
+++ b/apps/web/app/[locale]/dashboard/team-dashboard/page.tsx
@@ -0,0 +1,77 @@
+'use client';
+
+import { useMemo } from 'react';
+import { useParams,useRouter } from 'next/navigation';
+import { useTranslations } from 'next-intl';
+import { Card } from '@/components/ui/card';
+import { ArrowLeftIcon } from 'lucide-react';
+import { TeamStatsChart } from './components/team-stats-chart';
+import { TeamStatsGrid } from './components/team-stats-grid';
+import { TeamStatsTable } from './components/team-stats-table';
+import { DashboardHeader } from './components/dashboard-header';
+import { useOrganizationTeams } from '@app/hooks/features/useOrganizationTeams';
+import { MainLayout } from '@/lib/layout';
+import { Breadcrumb, Container } from '@/lib/components';
+import { cn } from '@/lib/utils';
+import { useAtomValue } from 'jotai';
+import { fullWidthState } from '@app/stores/fullWidth';
+import { withAuthentication } from '@/lib/app/authenticator';
+
+function TeamDashboard() {
+ const { activeTeam, isTrackingEnabled } = useOrganizationTeams();
+ const router = useRouter();
+ const t = useTranslations();
+ const fullWidth = useAtomValue(fullWidthState);
+ const paramsUrl = useParams<{ locale: string }>();
+ const currentLocale = paramsUrl?.locale;
+
+ const breadcrumbPath = useMemo(
+ () => [
+ { title: JSON.parse(t('pages.home.BREADCRUMB')), href: '/' },
+ { title: activeTeam?.name || '', href: '/' },
+ { title: 'team-dashboard', href: `/${currentLocale}/dashboard/team-dashboard` }
+ ],
+ [activeTeam?.name, currentLocale, t]
+ );
+
+ return (
+
+
+
+
{' '}
+
+
+
+
+
+
+
+
+
+
+
+ }
+ >
+
+
+
+
+
+
+ );
+}
+
+export default withAuthentication(TeamDashboard, {
+ displayName: 'Team-dashboard',
+ showPageSkeleton: true
+});
diff --git a/apps/web/components/ui/card.tsx b/apps/web/components/ui/card.tsx
index fbb0c3772..e75b9a0d8 100644
--- a/apps/web/components/ui/card.tsx
+++ b/apps/web/components/ui/card.tsx
@@ -1,43 +1,79 @@
-import * as React from 'react';
-
-import { cn } from '@/lib/utils';
-
-const Card = React.forwardRef>(({ className, ...props }, ref) => (
-
-));
-Card.displayName = 'Card';
-
-const CardHeader = React.forwardRef>(
- ({ className, ...props }, ref) => (
-
- )
-);
-CardHeader.displayName = 'CardHeader';
-
-const CardTitle = React.forwardRef>(
- ({ className, ...props }, ref) => (
-
- )
-);
-CardTitle.displayName = 'CardTitle';
-
-const CardDescription = React.forwardRef>(
- ({ className, ...props }, ref) => (
-
- )
-);
-CardDescription.displayName = 'CardDescription';
-
-const CardContent = React.forwardRef>(
- ({ className, ...props }, ref) =>
-);
-CardContent.displayName = 'CardContent';
-
-const CardFooter = React.forwardRef>(
- ({ className, ...props }, ref) => (
-
- )
-);
-CardFooter.displayName = 'CardFooter';
-
-export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+const Card = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+Card.displayName = "Card"
+
+const CardHeader = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardHeader.displayName = "CardHeader"
+
+const CardTitle = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardTitle.displayName = "CardTitle"
+
+const CardDescription = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardDescription.displayName = "CardDescription"
+
+const CardContent = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardContent.displayName = "CardContent"
+
+const CardFooter = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardFooter.displayName = "CardFooter"
+
+export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
diff --git a/apps/web/components/ui/chart.tsx b/apps/web/components/ui/chart.tsx
new file mode 100644
index 000000000..0c401945f
--- /dev/null
+++ b/apps/web/components/ui/chart.tsx
@@ -0,0 +1,363 @@
+import * as React from "react"
+import * as RechartsPrimitive from "recharts"
+
+import { cn } from "@/lib/utils"
+
+// Format: { THEME_NAME: CSS_SELECTOR }
+const THEMES = { light: "", dark: ".dark" } as const
+
+export type ChartConfig = {
+ [k in string]: {
+ label?: React.ReactNode
+ icon?: React.ComponentType
+ } & (
+ | { color?: string; theme?: never }
+ | { color?: never; theme: Record }
+ )
+}
+
+type ChartContextProps = {
+ config: ChartConfig
+}
+
+const ChartContext = React.createContext(null)
+
+function useChart() {
+ const context = React.useContext(ChartContext)
+
+ if (!context) {
+ throw new Error("useChart must be used within a ")
+ }
+
+ return context
+}
+
+const ChartContainer = React.forwardRef<
+ HTMLDivElement,
+ React.ComponentProps<"div"> & {
+ config: ChartConfig
+ children: React.ComponentProps<
+ typeof RechartsPrimitive.ResponsiveContainer
+ >["children"]
+ }
+>(({ id, className, children, config, ...props }, ref) => {
+ const uniqueId = React.useId()
+ const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
+
+ return (
+
+
+
+
+ {children}
+
+
+
+ )
+})
+ChartContainer.displayName = "Chart"
+
+const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
+ const colorConfig = Object.entries(config).filter(
+ ([, config]) => config.theme || config.color
+ )
+
+ if (!colorConfig.length) {
+ return null
+ }
+
+ return (
+