Skip to content

Commit

Permalink
refactor: Comprehensive dashboard data and visualization updates
Browse files Browse the repository at this point in the history
- Restructure data services with enhanced caching and error handling
- Update data export script to generate more comprehensive JSON files
- Modify Catch, Revenue, and About pages with advanced data visualization
- Implement new constants for landing sites and map configurations
- Improve data processing with more robust filtering and transformation
- Remove deprecated data files and replace with more structured datasets
  • Loading branch information
langbart committed Feb 16, 2025
1 parent 538c33d commit eafe2e7
Show file tree
Hide file tree
Showing 18 changed files with 16,161 additions and 6,722 deletions.
42 changes: 27 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
# Tabler Dashboard
# WorldFish Dashboard

A modern admin dashboard built with React and Tabler UI components.
A modern dashboard for visualizing fisheries data from Zanzibar, built with React and Tabler UI components.

## Features

- Modern and clean UI using Tabler components
- Responsive design
- React Router for navigation
- Tabler Icons integration
- Basic layout with navbar and sidebar
- Interactive data visualization for catch and revenue metrics
- Monthly and yearly view modes for time series data
- Seasonal patterns visualization using radar charts
- Multiple currency support (TZS, USD, EUR) for revenue data
- Responsive design optimized for all screen sizes
- Dark/Light theme support
- Landing site selection and filtering
- Real-time data updates and calculations
- Median-based statistical analysis

## Getting Started

Expand Down Expand Up @@ -39,8 +43,15 @@ The application will open in your default browser at `http://localhost:3000`.
## Project Structure

- `/src` - Source code
- `App.js` - Main application component
- `index.js` - Application entry point
- `/components` - React components
- `/pages` - Main page components (Catch.js, Revenue.js, etc.)
- `/charts` - Chart components
- `/layout` - Layout components
- `/services` - Data services and API calls
- `/utils` - Utility functions
- `/constants` - Configuration constants
- `/data` - Static data files
- `/styles` - CSS and style files
- `/public` - Static files

## Available Scripts
Expand All @@ -50,13 +61,14 @@ The application will open in your default browser at `http://localhost:3000`.
- `npm test` - Runs the test suite
- `npm eject` - Ejects from create-react-app

## Customization
## Data Visualization

You can customize the dashboard by:
1. Modifying the components in `App.js`
2. Adding new routes and components
3. Customizing Tabler UI components and styles
4. Adding more features and functionality as needed
The dashboard provides several visualization features:
- Time series charts for catch and revenue data
- Radar charts for seasonal patterns
- Statistical indicators (median values, percentage changes)
- Interactive filters and controls
- Responsive data updates

## License

Expand Down
51 changes: 23 additions & 28 deletions scripts/exportData.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,43 +39,38 @@ async function exportData() {
await client.connect();
const db = client.db(DB_NAME);

// Export CPUE data
console.log('Exporting CPUE data...');
const cpueCollection = db.collection('monthly-cpue');
const cpueData = await cpueCollection.find({}).toArray();
// Export gear metrics data
console.log('Exporting gear metrics data...');
const gearMetricsCollection = db.collection('gear_metrics');
const gearMetricsData = await gearMetricsCollection.find({}).toArray();

fs.writeFileSync(
path.join(DATA_DIR, 'cpue.json'),
JSON.stringify(cpueData, null, 2)
path.join(DATA_DIR, 'gear-metrics.json'),
JSON.stringify(gearMetricsData, null, 2)
);
console.log(`Exported ${cpueData.length} CPUE records`);
console.log(`Exported ${gearMetricsData.length} gear metrics records`);

// Export landing sites data
const landingSites = [...new Set(cpueData.map(record => record.landing_site))].sort();
// Export monthly metrics data
console.log('Exporting monthly metrics data...');
const monthlyMetricsCollection = db.collection('monthly_metrics');
const monthlyMetricsData = await monthlyMetricsCollection.find({}).toArray();

fs.writeFileSync(
path.join(DATA_DIR, 'landing-sites.json'),
JSON.stringify(landingSites, null, 2)
path.join(DATA_DIR, 'monthly-metrics.json'),
JSON.stringify(monthlyMetricsData, null, 2)
);
console.log(`Exported ${landingSites.length} landing sites`);

// Export summary statistics
console.log('Calculating summary statistics...');
const stats = await cpueCollection.aggregate([
{
$group: {
_id: '$landing_site',
avgCpue: { $avg: '$cpue' },
avgCatch: { $avg: '$catch' },
count: { $sum: 1 }
}
}
]).toArray();
console.log(`Exported ${monthlyMetricsData.length} monthly metrics records`);

// Export taxa proportions data
console.log('Exporting taxa proportions data...');
const taxaProportionsCollection = db.collection('taxa_proportions');
const taxaProportionsData = await taxaProportionsCollection.find({}).toArray();

fs.writeFileSync(
path.join(DATA_DIR, 'summary-stats.json'),
JSON.stringify(stats, null, 2)
path.join(DATA_DIR, 'taxa-proportions.json'),
JSON.stringify(taxaProportionsData, null, 2)
);
console.log('Exported summary statistics');
console.log(`Exported ${taxaProportionsData.length} taxa proportions records`);

// Export effort map data
console.log('Exporting effort map data...');
Expand Down
2 changes: 1 addition & 1 deletion src/components/charts/Map.js
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ const InfoPanel = memo(({ theme, data, colorRange, selectedRanges, onRangeToggle
const Map = memo(({ theme }) => {
const [viewState, setViewState] = useState(INITIAL_VIEW_STATE);
const [selectedRanges, setSelectedRanges] = useState(TIME_BREAKS);
const [isSatellite, setIsSatellite] = useState(false);
const [isSatellite, setIsSatellite] = useState(true);

// Memoize filtered data based on selected ranges
const transformedData = useMemo(() =>
Expand Down
72 changes: 35 additions & 37 deletions src/components/layout/Navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,7 @@ import {
IconInfoSquare,
IconMap2
} from '@tabler/icons-react';

// List of all landing sites
const LANDING_SITES = [
'bwawani', 'chole', 'chwaka', 'fumba', 'jambiani', 'jasini',
'kigombe', 'kizimkazi', 'kukuu', 'mangapwani', 'matemwe', 'mazizini',
'mkinga', 'mkoani', 'mkokotoni', 'mkumbuu', 'moa', 'msuka',
'mtangani', 'mvumoni_furaha', 'ndumbani', 'nungwi', 'other_site', 'sahare',
'shumba_mjini', 'tanga', 'tongoni', 'wesha', 'wete'
];
import { LANDING_SITES } from '../../constants/landingSites';

const Navigation = ({ selectedLandingSite, setSelectedLandingSite }) => {
const location = useLocation();
Expand All @@ -27,6 +19,9 @@ const Navigation = ({ selectedLandingSite, setSelectedLandingSite }) => {
: site.charAt(0).toUpperCase() + site.slice(1).replace('_', ' ');
};

// Check if current page needs landing site selector
const shouldShowLandingSiteSelector = ['/catch', '/revenue'].includes(location.pathname);

return (
<header className="navbar-expand-md">
<div className="collapse navbar-collapse" id="navbar-menu">
Expand Down Expand Up @@ -66,41 +61,44 @@ const Navigation = ({ selectedLandingSite, setSelectedLandingSite }) => {
</Link>
</li>
</ul>
<div className="navbar-nav ms-auto">
<div className="nav-item dropdown">
<button
type="button"
className="nav-link dropdown-toggle"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<IconMap2 className="icon me-2" stroke={1.5} />
{formatLandingSiteName(selectedLandingSite)}
</button>
<div className="dropdown-menu dropdown-menu-end">
{shouldShowLandingSiteSelector && (
<div className="navbar-nav ms-auto">
<div className="nav-item dropdown">
<button
type="button"
className={`dropdown-item ${selectedLandingSite === 'all' ? 'active' : ''}`}
onClick={() => setSelectedLandingSite('all')}
className="btn btn-primary dropdown-toggle"
data-bs-toggle="dropdown"
aria-expanded="false"
>
All Landing Sites
<IconMap2 className="icon me-2" stroke={1.5} />
<span className="nav-link-title">{formatLandingSiteName(selectedLandingSite)}</span>
</button>
<div className="dropdown-divider"></div>
<div className="dropdown-menu-scrollable" style={{ maxHeight: '400px', overflowY: 'auto' }}>
{LANDING_SITES.map(site => (
<button
key={site}
type="button"
className={`dropdown-item ${selectedLandingSite === site ? 'active' : ''}`}
onClick={() => setSelectedLandingSite(site)}
>
{formatLandingSiteName(site)}
</button>
))}
<div className="dropdown-menu dropdown-menu-end">
<div className="dropdown-header">Select Landing Site</div>
<button
type="button"
className={`dropdown-item ${selectedLandingSite === 'all' ? 'active fw-bold' : ''}`}
onClick={() => setSelectedLandingSite('all')}
>
All Landing Sites
</button>
<div className="dropdown-divider"></div>
<div className="dropdown-menu-scrollable" style={{ maxHeight: '400px', overflowY: 'auto' }}>
{LANDING_SITES.map(site => (
<button
key={site}
type="button"
className={`dropdown-item ${selectedLandingSite === site ? 'active fw-bold' : ''}`}
onClick={() => setSelectedLandingSite(site)}
>
{formatLandingSiteName(site)}
</button>
))}
</div>
</div>
</div>
</div>
</div>
)}
</div>
</div>
</div>
Expand Down
Loading

0 comments on commit eafe2e7

Please # to comment.