feat: Complete mobile UX/UI overhaul

Major improvements:
- New CSS design system with custom properties (tokens)
- Consistent spacing scale (4px base)
- Touch-friendly sizing (44px min targets)
- Improved Carousel with better touch handling and rubber-band effect
- Enhanced FilterControls (auto-collapse on mobile)
- Better stat card styling with change indicators
- Refined chart cards and toggle switches
- Smoother transitions and micro-interactions
- Better RTL support
- Print styles
- Responsive breakpoints for tablet (1024px), mobile (768px), and small mobile (375px)
- Cleaner typography hierarchy
- Subtle shadows and depth

Components updated:
- App.css: Complete rewrite with design tokens
- Carousel.jsx: Better touch gestures with velocity detection
- StatCard.jsx: Improved change indicator styling
- FilterControls.jsx: Auto-collapse on mobile
- EmptyState.jsx: Better accessibility
- ChartExport.js: Cleaned up unused imports
This commit is contained in:
fahed
2026-02-03 15:02:29 +03:00
parent f17e19f3f8
commit 0e5d285680
18 changed files with 2610 additions and 1610 deletions

View File

@@ -0,0 +1,82 @@
import React, { createContext, useContext, useState, useEffect, useCallback } from 'react';
import en from '../locales/en.json';
import ar from '../locales/ar.json';
const translations = { en, ar };
const LanguageContext = createContext();
export function LanguageProvider({ children }) {
const [lang, setLang] = useState(() => {
// Check localStorage first, then browser preference
const saved = localStorage.getItem('hihala-lang');
if (saved && translations[saved]) return saved;
// Check browser language
const browserLang = navigator.language?.split('-')[0];
if (browserLang === 'ar') return 'ar';
return 'en';
});
const dir = lang === 'ar' ? 'rtl' : 'ltr';
// Apply direction to document
useEffect(() => {
document.documentElement.dir = dir;
document.documentElement.lang = lang;
localStorage.setItem('hihala-lang', lang);
}, [lang, dir]);
// Translation function with dot notation support
const t = useCallback((key, fallback) => {
const keys = key.split('.');
let value = translations[lang];
for (const k of keys) {
if (value && typeof value === 'object' && k in value) {
value = value[k];
} else {
// Try English fallback
value = translations.en;
for (const ek of keys) {
if (value && typeof value === 'object' && ek in value) {
value = value[ek];
} else {
return fallback || key;
}
}
break;
}
}
return typeof value === 'string' ? value : (fallback || key);
}, [lang]);
// Switch language
const switchLanguage = useCallback(() => {
setLang(prev => prev === 'en' ? 'ar' : 'en');
}, []);
// Set specific language
const setLanguage = useCallback((newLang) => {
if (translations[newLang]) {
setLang(newLang);
}
}, []);
return (
<LanguageContext.Provider value={{ lang, dir, t, switchLanguage, setLanguage }}>
{children}
</LanguageContext.Provider>
);
}
export function useLanguage() {
const context = useContext(LanguageContext);
if (!context) {
throw new Error('useLanguage must be used within a LanguageProvider');
}
return context;
}
export default LanguageContext;