214 lines
9.8 KiB
TypeScript
214 lines
9.8 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { ShoppingEvent } from '../types';
|
|
import { shoppingEventApi } from '../services/api';
|
|
|
|
const Dashboard: React.FC = () => {
|
|
const navigate = useNavigate();
|
|
const [recentEvents, setRecentEvents] = useState<ShoppingEvent[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
fetchRecentEvents();
|
|
}, []);
|
|
|
|
const fetchRecentEvents = async () => {
|
|
try {
|
|
setLoading(true);
|
|
const response = await shoppingEventApi.getAll();
|
|
// Get the 3 most recent events
|
|
const recent = response.data
|
|
.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())
|
|
.slice(0, 3);
|
|
setRecentEvents(recent);
|
|
} catch (error) {
|
|
console.error('Error fetching recent events:', error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<div>
|
|
<h1 className="text-2xl font-bold text-gray-900">Dashboard</h1>
|
|
<p className="text-gray-600">Welcome to your product tracker!</p>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
{/* Stats Cards */}
|
|
<div className="bg-white rounded-lg shadow p-6">
|
|
<div className="flex items-center">
|
|
<div className="p-2 bg-blue-100 rounded-md">
|
|
<svg className="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" />
|
|
</svg>
|
|
</div>
|
|
<div className="ml-4">
|
|
<p className="text-sm font-medium text-gray-600">Total Shopping Events</p>
|
|
<p className="text-2xl font-semibold text-gray-900">-</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-white rounded-lg shadow p-6">
|
|
<div className="flex items-center">
|
|
<div className="p-2 bg-green-100 rounded-md">
|
|
<svg className="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1" />
|
|
</svg>
|
|
</div>
|
|
<div className="ml-4">
|
|
<p className="text-sm font-medium text-gray-600">Total Spent</p>
|
|
<p className="text-2xl font-semibold text-gray-900">$-</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-white rounded-lg shadow p-6">
|
|
<div className="flex items-center">
|
|
<div className="p-2 bg-yellow-100 rounded-md">
|
|
<svg className="w-6 h-6 text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
|
|
</svg>
|
|
</div>
|
|
<div className="ml-4">
|
|
<p className="text-sm font-medium text-gray-600">Unique Items</p>
|
|
<p className="text-2xl font-semibold text-gray-900">-</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-white rounded-lg shadow p-6">
|
|
<div className="flex items-center">
|
|
<div className="p-2 bg-purple-100 rounded-md">
|
|
<svg className="w-6 h-6 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
|
|
</svg>
|
|
</div>
|
|
<div className="ml-4">
|
|
<p className="text-sm font-medium text-gray-600">Shops Visited</p>
|
|
<p className="text-2xl font-semibold text-gray-900">-</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Quick Actions */}
|
|
<div className="bg-white rounded-lg shadow">
|
|
<div className="px-6 py-4 border-b border-gray-200">
|
|
<h2 className="text-lg font-medium text-gray-900">Quick Actions</h2>
|
|
</div>
|
|
<div className="p-6">
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
<button
|
|
onClick={() => navigate('/shopping-events/new')}
|
|
className="flex items-center p-4 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
|
|
>
|
|
<div className="p-2 bg-blue-100 rounded-md mr-3">
|
|
<svg className="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<p className="font-medium text-gray-900">Add New Event</p>
|
|
<p className="text-sm text-gray-600">Record a new shopping event</p>
|
|
</div>
|
|
</button>
|
|
|
|
<button
|
|
onClick={() => navigate('/products?add=true')}
|
|
className="flex items-center p-4 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
|
|
>
|
|
<div className="p-2 bg-green-100 rounded-md mr-3">
|
|
<svg className="w-5 h-5 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<p className="font-medium text-gray-900">Add Product</p>
|
|
<p className="text-sm text-gray-600">Add a new product item</p>
|
|
</div>
|
|
</button>
|
|
|
|
<button
|
|
onClick={() => navigate('/shops?add=true')}
|
|
className="flex items-center p-4 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
|
|
>
|
|
<div className="p-2 bg-purple-100 rounded-md mr-3">
|
|
<svg className="w-5 h-5 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<p className="font-medium text-gray-900">Add Shop</p>
|
|
<p className="text-sm text-gray-600">Register a new shop</p>
|
|
</div>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Recent Activity */}
|
|
<div className="bg-white rounded-lg shadow">
|
|
<div className="px-6 py-4 border-b border-gray-200">
|
|
<h2 className="text-lg font-medium text-gray-900">Recent Shopping Events</h2>
|
|
</div>
|
|
<div className="p-6">
|
|
{loading ? (
|
|
<div className="flex justify-center items-center py-8">
|
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
|
|
</div>
|
|
) : recentEvents.length === 0 ? (
|
|
<div className="text-center py-8">
|
|
<svg className="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 48 48">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
|
|
</svg>
|
|
<h3 className="mt-2 text-sm font-medium text-gray-900">No shopping events yet</h3>
|
|
<p className="mt-1 text-sm text-gray-500">Get started by adding your first event!</p>
|
|
</div>
|
|
) : (
|
|
<div className="space-y-4">
|
|
{recentEvents.map((event) => (
|
|
<div key={event.id} className="border border-gray-200 rounded-lg p-4 hover:bg-gray-50 transition-colors">
|
|
<div className="flex justify-between items-start">
|
|
<div className="flex-1">
|
|
<div className="flex items-center space-x-2">
|
|
<h4 className="font-medium text-gray-900">{event.shop.name}</h4>
|
|
<span className="text-sm text-gray-500">•</span>
|
|
<span className="text-sm text-gray-500">{event.shop.city}</span>
|
|
</div>
|
|
<p className="text-sm text-gray-600 mt-1">
|
|
{new Date(event.date).toLocaleDateString()}
|
|
</p>
|
|
{event.products.length > 0 && (
|
|
<p className="text-sm text-gray-500 mt-1">
|
|
{event.products.length} item{event.products.length !== 1 ? 's' : ''}
|
|
</p>
|
|
)}
|
|
</div>
|
|
<div className="text-right">
|
|
{event.total_amount && (
|
|
<p className="font-semibold text-green-600">
|
|
${event.total_amount.toFixed(2)}
|
|
</p>
|
|
)}
|
|
<button
|
|
onClick={() => navigate('/shopping-events')}
|
|
className="text-sm text-blue-600 hover:text-blue-800 mt-1"
|
|
>
|
|
View all
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Dashboard;
|