diff --git a/src/App.tsx b/src/App.tsx index 16a757e..9a3a848 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -45,8 +45,9 @@ function App() { } /> } /> - } /> - } /> + }> + } /> + } /> diff --git a/src/screens/DailySalesReportPage.tsx b/src/screens/DailySalesReportPage.tsx index fd2bd84..402baf2 100644 --- a/src/screens/DailySalesReportPage.tsx +++ b/src/screens/DailySalesReportPage.tsx @@ -21,31 +21,34 @@ export function DailySalesReportPage() { const [reportData, setReportData] = useState(null); const downloadPDF = () => { - const doc = new jsPDF(); + const doc = new jsPDF({ orientation: 'landscape' }); + const pageWidth = doc.internal.pageSize.getWidth(); + const centerX = pageWidth / 2; + const rightX = pageWidth - 14; // Headers doc.setFontSize(16); doc.setFont('helvetica', 'bold'); - doc.text('Krishna Flour Mills (Bangalore) Pvt. Limited', 105, 15, { align: 'center' }); + doc.text('Krishna Flour Mills (Bangalore) Pvt. Limited', centerX, 15, { align: 'center' }); doc.setFontSize(11); doc.setFont('helvetica', 'normal'); - doc.text('19, Platform Road, Bengaluru - 560 020', 105, 22, { align: 'center' }); + doc.text('19, Platform Road, Bengaluru - 560 020', centerX, 22, { align: 'center' }); doc.setFontSize(12); doc.setFont('helvetica', 'bold'); - doc.text('DAILY ORDER REPORT', 105, 30, { align: 'center' }); + doc.text('DAILY ORDER REPORT', centerX, 30, { align: 'center' }); // Simple underline for DAILY ORDER REPORT const textWidth = doc.getTextWidth('DAILY ORDER REPORT'); doc.setLineWidth(0.5); - doc.line(105 - textWidth / 2, 31, 105 + textWidth / 2, 31); + doc.line(centerX - textWidth / 2, 31, centerX + textWidth / 2, 31); // Meta Info doc.setFontSize(10); doc.text(`Distributor Name : ${distributor}`, 14, 40); - doc.text(`Date : ${date}`, 196, 40, { align: 'right' }); - doc.text(`SO Name : ${soName}`, 196, 45, { align: 'right' }); + doc.text(`Date : ${date}`, rightX, 40, { align: 'right' }); + doc.text(`SO Name : ${soName}`, rightX, 45, { align: 'right' }); // Table autoTable(doc, { @@ -60,31 +63,34 @@ export function DailySalesReportPage() { }; const printReport = async () => { - const doc = new jsPDF(); + const doc = new jsPDF({ orientation: 'landscape' }); + const pageWidth = doc.internal.pageSize.getWidth(); + const centerX = pageWidth / 2; + const rightX = pageWidth - 14; // Headers doc.setFontSize(16); doc.setFont('helvetica', 'bold'); - doc.text('Krishna Flour Mills (Bangalore) Pvt. Limited', 105, 15, { align: 'center' }); + doc.text('Krishna Flour Mills (Bangalore) Pvt. Limited', centerX, 15, { align: 'center' }); doc.setFontSize(11); doc.setFont('helvetica', 'normal'); - doc.text('19, Platform Road, Bengaluru - 560 020', 105, 22, { align: 'center' }); + doc.text('19, Platform Road, Bengaluru - 560 020', centerX, 22, { align: 'center' }); doc.setFontSize(12); doc.setFont('helvetica', 'bold'); - doc.text('DAILY ORDER REPORT', 105, 30, { align: 'center' }); + doc.text('DAILY ORDER REPORT', centerX, 30, { align: 'center' }); // Simple underline for DAILY ORDER REPORT const textWidth = doc.getTextWidth('DAILY ORDER REPORT'); doc.setLineWidth(0.5); - doc.line(105 - textWidth / 2, 31, 105 + textWidth / 2, 31); + doc.line(centerX - textWidth / 2, 31, centerX + textWidth / 2, 31); // Meta Info doc.setFontSize(10); doc.text(`Distributor Name : ${distributor}`, 14, 40); - doc.text(`Date : ${date}`, 196, 40, { align: 'right' }); - doc.text(`SO Name : ${soName}`, 196, 45, { align: 'right' }); + doc.text(`Date : ${date}`, rightX, 40, { align: 'right' }); + doc.text(`SO Name : ${soName}`, rightX, 45, { align: 'right' }); // Table autoTable(doc, { diff --git a/src/screens/admin/DatasetItemsPage.tsx b/src/screens/admin/DatasetItemsPage.tsx index 90039be..1c8935b 100644 --- a/src/screens/admin/DatasetItemsPage.tsx +++ b/src/screens/admin/DatasetItemsPage.tsx @@ -19,6 +19,7 @@ export function DatasetItemsPage() { // Local state for items editing const [items, setItems] = useState([]); + const [searchTerm, setSearchTerm] = useState(''); useEffect(() => { if (id) fetchDataset(id); @@ -220,90 +221,113 @@ export function DatasetItemsPage() { const isString = dataset.data_type === 'string'; const hasChanges = JSON.stringify(items) !== JSON.stringify(dataset.items || []); + const filteredIndices = items + .map((item, idx) => ({ item, idx })) + .filter(({ item }) => { + if (!searchTerm) return true; + const lowerSearch = searchTerm.toLowerCase(); + if (isString) { + return (typeof item === 'string') && item.toLowerCase().includes(lowerSearch); + } else { + return dataset?.keys?.some(k => + item[k] && typeof item[k] === 'string' && item[k].toLowerCase().includes(lowerSearch) + ); + } + }) + .map(({ idx }) => idx); + return ( -
-
+
+
- -
-

- {dataset.name} - - {items.length} {isString ? 'Items' : 'Rows'} - -

-

Manage items for this dataset

-
+

+ {dataset.name} + + {!isString ? 'Table' : 'List'} + + + {items.length} items {dataset.keys?.length ? `- ${dataset.keys.length} cols` : ''} + +

- +
{selectedRows.size > 0 && ( - )} {hasChanges && ( - )} -
{error &&
{error}
} -
+
{isString ? ( - <> -
-

- {dataset.name} -

+
+
+
+ setSearchTerm(e.target.value)} + placeholder="Search rows..." + className="w-full text-sm pl-8 pr-4 py-2 border border-slate-200 rounded-md focus:outline-none focus:ring-1 focus:ring-brand-500" + /> + + + +
+
{items.length} rows
-
- - +
+
+ - - - - + + + - - {items.map((val, idx) => ( - - + {filteredIndices.map((idx) => { + const val = items[idx]; + return ( + + - - + @@ -317,7 +341,8 @@ export function DatasetItemsPage() { - ))} + ); + })} {items.length === 0 && (
- 0} onChange={toggleSelectAll} className="rounded border-emerald-800 text-emerald-600 focus:ring-emerald-500 cursor-pointer" /> + + 0} onChange={toggleSelectAll} className="rounded border-slate-300 text-brand-600 focus:ring-brand-500 cursor-pointer" /> #ValueActions#VALUEACTIONS
+
toggleRowSelection(idx)} - className="rounded border-slate-300 text-emerald-600 focus:ring-emerald-500 cursor-pointer" + className="rounded border-slate-300 text-brand-600 focus:ring-brand-500 cursor-pointer" /> {idx + 1} - {idx + 1} + handleStringChange(idx, e.target.value)} - className="absolute inset-0 w-full h-full px-4 py-2 border-none bg-transparent text-sm focus:outline-none focus:ring-2 focus:ring-inset focus:ring-emerald-500 transition-colors" + className="absolute inset-0 w-full h-full px-4 py-2 border-none bg-transparent text-sm text-brand-700 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-brand-500 transition-colors" placeholder="Enter value" />
@@ -328,59 +353,72 @@ export function DatasetItemsPage() {
-
- +
{items.length} rows
- +
) : ( - <> -
-

- {dataset.name} -

+
+
+
+ setSearchTerm(e.target.value)} + placeholder="Search rows..." + className="w-full text-sm pl-8 pr-4 py-2 border border-slate-200 rounded-md focus:outline-none focus:ring-1 focus:ring-brand-500" + /> + + + +
+
{items.length} rows
-
- - +
+
+ - - + {dataset.keys?.map(k => ( - + ))} - + - - {items.map((rowObj, idx) => ( - - + {filteredIndices.map((idx) => { + const rowObj = items[idx]; + return ( + + - + {dataset.keys?.map(k => { const isInvalid = invalidCells.has(`${idx}-${k}`); return ( - - ))} + ); + })} {items.length === 0 && (
- 0} onChange={toggleSelectAll} className="rounded border-emerald-800 text-emerald-600 focus:ring-emerald-500 cursor-pointer" /> + + 0} onChange={toggleSelectAll} className="rounded border-slate-300 text-brand-600 focus:ring-brand-500 cursor-pointer" /> ##{k}{k}ActionsACTIONS
+
toggleRowSelection(idx)} - className="rounded border-slate-300 text-emerald-600 focus:ring-emerald-500 cursor-pointer" + className="rounded border-slate-300 text-brand-600 focus:ring-brand-500 cursor-pointer" /> {idx + 1}{idx + 1} - + handleObjectChange(idx, k, e.target.value)} - className={`absolute inset-0 w-full h-full px-4 py-2 border-none bg-transparent text-sm focus:outline-none transition-colors ${isInvalid - ? 'ring-2 ring-inset ring-red-400 bg-red-50/50 focus:ring-red-500' - : 'focus:ring-2 focus:ring-inset focus:ring-emerald-500' + className={`absolute inset-0 w-full h-full px-4 py-2 border-none bg-transparent text-sm text-brand-700 transition-colors ${isInvalid + ? 'ring-2 ring-inset ring-red-400 bg-red-50/50 focus:ring-red-500 focus:outline-none' + : 'focus:outline-none focus:ring-2 focus:ring-inset focus:ring-brand-500' }`} placeholder={`Enter ${k}`} /> @@ -397,7 +435,8 @@ export function DatasetItemsPage() {
@@ -408,12 +447,13 @@ export function DatasetItemsPage() {
-
- +
{items.length} rows - {dataset.keys?.length || 0} cols
- +
)}
diff --git a/src/screens/admin/DatasetsPage.tsx b/src/screens/admin/DatasetsPage.tsx index fd9616e..66dd46f 100644 --- a/src/screens/admin/DatasetsPage.tsx +++ b/src/screens/admin/DatasetsPage.tsx @@ -1,14 +1,15 @@ import { useEffect, useState } from 'react'; import { orderBookingClient } from '../../api/clients'; import type { Dataset } from '../../api/types'; -import { Card } from '../../components/reusable/Card'; -import { Button } from '../../components/buttons/Button'; import { Spinner } from '../../components/reusable/Spinner'; -import { Database, List } from 'lucide-react'; -import { useNavigate } from 'react-router-dom'; +import { Database, LayoutList, Table2 } from 'lucide-react'; +import { useNavigate, useLocation, Outlet, useParams } from 'react-router-dom'; export function DatasetsPage() { const navigate = useNavigate(); + const location = useLocation(); + const { id } = useParams<{ id: string }>(); + const [datasets, setDatasets] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); @@ -23,63 +24,82 @@ export function DatasetsPage() { const res = await orderBookingClient.getDatasets(); setDatasets(res); setError(''); + + // Auto-select first dataset if none selected and on base path + if (res.length > 0 && location.pathname.endsWith('/admin/datasets')) { + navigate(`/admin/datasets/${res[0].id}`, { replace: true }); + } } catch (err: any) { setError(err.message || 'Failed to fetch datasets'); } finally { setLoading(false); } }; - + if (loading) { + return
; + } + + if (error) { + return
{error}
; + } return ( -
-
-

- Datasets -

+
+ {/* Sidebar */} +
+
+ +

DATASETS

+
+
+
    + {datasets.map((ds) => { + const isActive = id === String(ds.id); + const isTable = ds.data_type === 'object'; + return ( +
  • + +
    +
  • + ); + })} +
+
+
-
- - {error &&
{error}
} - - - {loading ? ( -
+ {/* Main Content Area */} +
+ {id ? ( + ) : ( -
- - - - - - - - - - {datasets.map((ds) => ( - - - - - - ))} - {datasets.length === 0 && ( - - - - )} - -
NameItemsActions
{ds.name}{ds.item_count || 0} - -
No datasets found.
+
+ Select a dataset to view its contents
)} - - - +
); } +