Spaces:
Running
Running
James McCool
Add comprehensive BUILD_GUIDE.md for building a MongoDB Data Viewer React app, detailing setup, CSS architecture, component design, and deployment to Hugging Face. Update DataTable component sort indicator from 'βοΈ' to 'β΅' for improved clarity.
1bcf29b
| import React, { useState, useEffect } from 'react'; | |
| import { Table, TableHeader, TableBody, TableHead, TableRow, TableCell, TableCaption } from './ui/table'; | |
| import { Button } from './ui/button'; | |
| const DataTable = () => { | |
| const [data, setData] = useState([]); | |
| const [loading, setLoading] = useState(true); | |
| const [error, setError] = useState(null); | |
| const [sortConfig, setSortConfig] = useState({ key: null, direction: 'asc' }); | |
| // Mock data for demonstration - replace with actual MongoDB data | |
| const mockData = [ | |
| { id: 1, name: 'John Doe', email: 'john@example.com', role: 'Developer' }, | |
| { id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'Designer' }, | |
| { id: 3, name: 'Bob Johnson', email: 'bob@example.com', role: 'Manager' }, | |
| { id: 4, name: 'Alice Brown', email: 'alice@example.com', role: 'Developer' }, | |
| { id: 5, name: 'Charlie Wilson', email: 'charlie@example.com', role: 'Designer' }, | |
| ]; | |
| useEffect(() => { | |
| // Simulate loading data | |
| setTimeout(() => { | |
| setData(mockData); | |
| setLoading(false); | |
| }, 1000); | |
| }, []); | |
| // Sorting function | |
| const sortData = (key) => { | |
| let direction = 'asc'; | |
| // Three-state sorting: asc β desc β none | |
| if (sortConfig.key === key) { | |
| if (sortConfig.direction === 'asc') { | |
| direction = 'desc'; | |
| } else if (sortConfig.direction === 'desc') { | |
| // Reset to no sorting | |
| setSortConfig({ key: null, direction: 'none' }); | |
| return; | |
| } | |
| } | |
| setSortConfig({ key, direction }); | |
| }; | |
| // Get sorted data | |
| const getSortedData = () => { | |
| if (!sortConfig.key || sortConfig.direction === 'none') return data; | |
| return [...data].sort((a, b) => { | |
| let aValue = a[sortConfig.key]; | |
| let bValue = b[sortConfig.key]; | |
| // Handle string comparison (case-insensitive) | |
| if (typeof aValue === 'string') { | |
| aValue = aValue.toLowerCase(); | |
| bValue = bValue.toLowerCase(); | |
| } | |
| if (aValue < bValue) { | |
| return sortConfig.direction === 'asc' ? -1 : 1; | |
| } | |
| if (aValue > bValue) { | |
| return sortConfig.direction === 'asc' ? 1 : -1; | |
| } | |
| return 0; | |
| }); | |
| }; | |
| // Get sort indicator for column headers | |
| const getSortIndicator = (key) => { | |
| if (sortConfig.key !== key) return 'β΅'; | |
| if (sortConfig.direction === 'asc') return 'β'; | |
| if (sortConfig.direction === 'desc') return 'β'; | |
| return 'β΅'; | |
| }; | |
| const fetchFromMongoDB = async () => { | |
| try { | |
| setLoading(true); | |
| // Replace this with your actual MongoDB connection logic | |
| // const response = await fetch('/api/data'); | |
| // const result = await response.json(); | |
| // setData(result); | |
| // For now, using mock data | |
| setData(mockData); | |
| } catch (err) { | |
| setError('Failed to fetch data'); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| if (loading) { | |
| return ( | |
| <div className="flex items-center justify-center p-8"> | |
| <div className="text-lg text-muted-foreground">Loading...</div> | |
| </div> | |
| ); | |
| } | |
| if (error) { | |
| return ( | |
| <div className="flex items-center justify-center p-8"> | |
| <div className="text-red-500">{error}</div> | |
| </div> | |
| ); | |
| } | |
| const sortedData = getSortedData(); | |
| return ( | |
| <div className="w-full mx-auto p-6"> | |
| <div className="flex justify-between items-center mb-6"> | |
| <h2 className="text-2xl font-bold text-foreground">Interesting Mock Data</h2> | |
| <Button onClick={fetchFromMongoDB}> | |
| Refresh Data | |
| </Button> | |
| </div> | |
| <div className="border rounded-lg bg-card"> | |
| <Table> | |
| <TableCaption>A list of users from the database.</TableCaption> | |
| <TableHeader> | |
| <TableRow> | |
| <TableHead | |
| className="cursor-pointer hover:bg-muted/50 transition-colors" | |
| onClick={() => sortData('id')} | |
| > | |
| ID {getSortIndicator('id')} | |
| </TableHead> | |
| <TableHead | |
| className="cursor-pointer hover:bg-muted/50 transition-colors" | |
| onClick={() => sortData('name')} | |
| > | |
| Name {getSortIndicator('name')} | |
| </TableHead> | |
| <TableHead | |
| className="cursor-pointer hover:bg-muted/50 transition-colors" | |
| onClick={() => sortData('email')} | |
| > | |
| Email {getSortIndicator('email')} | |
| </TableHead> | |
| <TableHead | |
| className="cursor-pointer hover:bg-muted/50 transition-colors" | |
| onClick={() => sortData('role')} | |
| > | |
| Role {getSortIndicator('role')} | |
| </TableHead> | |
| </TableRow> | |
| </TableHeader> | |
| <TableBody> | |
| {sortedData.map((user) => ( | |
| <TableRow key={user.id}> | |
| <TableCell className="font-medium">{user.id}</TableCell> | |
| <TableCell>{user.name}</TableCell> | |
| <TableCell>{user.email}</TableCell> | |
| <TableCell>{user.role}</TableCell> | |
| </TableRow> | |
| ))} | |
| </TableBody> | |
| </Table> | |
| </div> | |
| </div> | |
| ); | |
| }; | |
| export default DataTable; | |