hMarket Trae ilk versiyon

This commit is contained in:
hOLOlu
2026-02-03 01:22:08 +03:00
commit 2b861156fe
74 changed files with 42127 additions and 0 deletions

View File

@@ -0,0 +1,331 @@
import React from 'react';
import {
Container,
Typography,
Box,
Card,
CardContent,
CardActions,
Button,
List,
ListItem,
ListItemText,
ListItemIcon,
Avatar,
LinearProgress,
IconButton,
} from '@mui/material';
import {
Add,
List as ListIcon,
ShoppingCart,
CheckCircle,
PendingActions,
Share,
TrendingUp,
Refresh,
} from '@mui/icons-material';
import { useNavigate } from 'react-router-dom';
import { useQuery } from '@tanstack/react-query';
import { dashboardAPI, listsAPI } from '../../services/api';
import { useAuth } from '../../contexts/AuthContext';
import { formatDistanceToNow } from 'date-fns';
import { ListsResponse } from '../../types';
const DashboardPage: React.FC = () => {
const navigate = useNavigate();
const { user } = useAuth();
// Fetch dashboard stats
const { data: statsData, isLoading: statsLoading, refetch: refetchStats } = useQuery({
queryKey: ['dashboard', 'stats'],
queryFn: () => dashboardAPI.getStats(),
});
// Fetch recent lists
const { data: listsData, isLoading: listsLoading } = useQuery<ListsResponse>({
queryKey: ['lists', 'recent'],
queryFn: () => listsAPI.getLists({ limit: 5, sortBy: 'updatedAt', sortOrder: 'desc' }).then(response => response.data),
});
// Fetch recent activity
const { data: activityData, isLoading: activityLoading } = useQuery({
queryKey: ['dashboard', 'activity'],
queryFn: () => dashboardAPI.getRecentActivity({ limit: 10 }),
});
const stats = statsData?.data?.data;
const recentLists = listsData?.data?.lists || [];
const recentActivity = activityData?.data?.data?.activities || [];
const completionRate = stats ? Math.round((stats.completedItems / stats.totalItems) * 100) || 0 : 0;
return (
<Container maxWidth="xl" sx={{ mt: 4, mb: 4 }}>
{/* Welcome Section */}
<Box sx={{ mb: 4 }}>
<Typography variant="h4" gutterBottom>
Tekrar hoş geldin, {user?.firstName}! 👋
</Typography>
<Typography variant="body1" color="text.secondary">
Bugün alışveriş listelerinizde neler oluyor, işte özet.
</Typography>
</Box>
<Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', sm: '1fr 1fr', md: '1fr 1fr 1fr 1fr' }, gap: 3 }}>
{/* Stats Cards */}
<Box>
<Card>
<CardContent>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<ListIcon sx={{ fontSize: 40, color: 'primary.main', mr: 2 }} />
<Box>
<Typography color="textSecondary" gutterBottom>
Toplam Liste
</Typography>
<Typography variant="h4">
{statsLoading ? '-' : stats?.totalLists || 0}
</Typography>
</Box>
</Box>
</CardContent>
</Card>
</Box>
<Box>
<Card>
<CardContent>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<ShoppingCart sx={{ fontSize: 40, color: 'info.main', mr: 2 }} />
<Box>
<Typography color="textSecondary" gutterBottom>
Toplam Ürün
</Typography>
<Typography variant="h4">
{statsLoading ? '-' : stats?.totalItems || 0}
</Typography>
</Box>
</Box>
</CardContent>
</Card>
</Box>
<Box>
<Card>
<CardContent>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<CheckCircle sx={{ fontSize: 40, color: 'success.main', mr: 2 }} />
<Box>
<Typography color="textSecondary" gutterBottom>
Tamamlanan
</Typography>
<Typography variant="h4">
{statsLoading ? '-' : stats?.completedItems || 0}
</Typography>
</Box>
</Box>
</CardContent>
</Card>
</Box>
<Box>
<Card>
<CardContent>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<PendingActions sx={{ fontSize: 40, color: 'warning.main', mr: 2 }} />
<Box>
<Typography color="textSecondary" gutterBottom>
Bekleyen
</Typography>
<Typography variant="h4">
{statsLoading ? '-' : stats?.pendingItems || 0}
</Typography>
</Box>
</Box>
</CardContent>
</Card>
</Box>
</Box>
<Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', md: '1fr 1fr' }, gap: 3, mt: 3 }}>
{/* Completion Rate */}
<Box>
<Card>
<CardContent>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
<Typography variant="h6">Alışveriş İlerlemesi</Typography>
<IconButton onClick={() => refetchStats()}>
<Refresh />
</IconButton>
</Box>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
<TrendingUp sx={{ mr: 1, color: 'success.main' }} />
<Typography variant="h4" color="success.main">
{completionRate}%
</Typography>
<Typography variant="body2" color="text.secondary" sx={{ ml: 1 }}>
tamamlanma oranı
</Typography>
</Box>
<LinearProgress
variant="determinate"
value={completionRate}
sx={{ height: 8, borderRadius: 4 }}
/>
<Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}>
{stats?.totalItems || 0} üründen {stats?.completedItems || 0} tanesi tamamlandı
</Typography>
</CardContent>
</Card>
</Box>
{/* Quick Actions */}
<Box>
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
Hızlı İşlemler
</Typography>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
<Button
fullWidth
variant="contained"
startIcon={<Add />}
onClick={() => navigate('/lists?action=create')}
>
Yeni Liste Oluştur
</Button>
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 2 }}>
<Box>
<Button
fullWidth
variant="outlined"
startIcon={<ListIcon />}
onClick={() => navigate('/lists')}
>
Tüm Listeler
</Button>
</Box>
<Box>
<Button
fullWidth
variant="outlined"
startIcon={<ShoppingCart />}
onClick={() => navigate('/products')}
>
Ürünlere Gözat
</Button>
</Box>
</Box>
</Box>
</CardContent>
</Card>
</Box>
</Box>
<Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', md: '1fr 1fr' }, gap: 3, mt: 3 }}>
{/* Recent Lists */}
<Box>
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
Son Listeler
</Typography>
{listsLoading ? (
<Typography>Yükleniyor...</Typography>
) : recentLists.length === 0 ? (
<Typography color="text.secondary">
Henüz liste yok. İlk alışveriş listenizi oluşturun!
</Typography>
) : (
<List>
{recentLists.map((list: any) => (
<ListItem
key={list.id}
onClick={() => navigate(`/lists/${list.id}`)}
sx={{ px: 0, cursor: 'pointer' }}
>
<ListItemIcon>
<Avatar sx={{ bgcolor: list.color || 'primary.main', width: 32, height: 32 }}>
{list.isShared ? <Share /> : <ListIcon />}
</Avatar>
</ListItemIcon>
<ListItemText
primary={list.name}
secondary={
<span style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<span style={{ fontSize: '0.875rem', color: 'rgba(0, 0, 0, 0.6)' }}>
{list._count?.items || 0} ürün
</span>
{list.isShared && (
<span style={{
fontSize: '0.75rem',
backgroundColor: '#1976d2',
color: 'white',
padding: '2px 8px',
borderRadius: '12px',
fontWeight: 500
}}>
Paylaşılan
</span>
)}
</span>
}
/>
</ListItem>
))}
</List>
)}
</CardContent>
<CardActions>
<Button size="small" onClick={() => navigate('/lists')}>
Tüm Listeleri Görüntüle
</Button>
</CardActions>
</Card>
</Box>
{/* Recent Activity */}
<Box>
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
Son Aktiviteler
</Typography>
{activityLoading ? (
<Typography>Yükleniyor...</Typography>
) : recentActivity.length === 0 ? (
<Typography color="text.secondary">
Son aktivite yok.
</Typography>
) : (
<List>
{recentActivity.slice(0, 5).map((activity: any) => (
<ListItem key={activity.id} sx={{ px: 0 }}>
<ListItemIcon>
<Avatar sx={{ width: 32, height: 32 }}>
{activity.user?.firstName?.[0] || '?'}
</Avatar>
</ListItemIcon>
<ListItemText
primary={activity.description}
secondary={formatDistanceToNow(new Date(activity.createdAt), { addSuffix: true })}
/>
</ListItem>
))}
</List>
)}
</CardContent>
<CardActions>
<Button size="small" onClick={() => navigate('/activity')}>
Tüm Aktiviteleri Görüntüle
</Button>
</CardActions>
</Card>
</Box>
</Box>
</Container>
);
};
export default DashboardPage;