Skip to content

Commit 5e3c695

Browse files
author
Cohen, Yohay
committed
Refactor dashboard components for improved UI and functionality, including enhanced month selection in the Financial Command Center, updated styles for better visual consistency, and new currency formatting logic. Add scrollbar utility classes for better overflow handling. Update localization files for new UI elements and insights features.
1 parent 418efd3 commit 5e3c695

9 files changed

Lines changed: 366 additions & 176 deletions

File tree

client/dev-dist/sw.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ define(['./workbox-46f6dd99'], (function (workbox) { 'use strict';
8282
"revision": "3ca0b8505b4bec776b69afdba2768812"
8383
}, {
8484
"url": "index.html",
85-
"revision": "0.1ic2ov999m8"
85+
"revision": "0.7o12b4l4vl4"
8686
}], {});
8787
workbox.cleanupOutdatedCaches();
8888
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {

client/src/App.tsx

Lines changed: 109 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -68,79 +68,122 @@ function App() {
6868

6969
return (
7070
<>
71-
<div className="flex flex-col h-screen bg-gray-100 overflow-hidden">
72-
<header className="bg-white border-b border-gray-200 p-4 shadow-sm z-10 w-full">
73-
<div className="container mx-auto flex items-center justify-between">
74-
<div className="flex items-center gap-4">
75-
<h1 className="text-xl font-bold text-gray-800 flex items-center gap-2">
76-
<span className="text-blue-600 text-2xl"></span> {t('common.title')}
71+
<div className="flex flex-col h-screen bg-gray-50 overflow-hidden">
72+
<header className="bg-white border-b border-gray-200/80 shadow-sm z-10 w-full">
73+
<div className="container mx-auto px-3 sm:px-4 py-2.5 sm:py-3 flex flex-col gap-2">
74+
<div className="flex items-center justify-between gap-2 min-w-0">
75+
<h1 className="text-base sm:text-lg font-bold text-emerald-800 tracking-tight truncate min-w-0 flex-1">
76+
{t('common.title')}
7777
</h1>
78-
</div>
78+
<div className="flex items-center gap-1 sm:gap-1.5 shrink-0">
79+
{view === 'dashboard' && <DashboardAlertsDropdown selectedMonth={selectedMonth} />}
7980

80-
{/* View Switcher and Language Switcher */}
81-
<div className="flex items-center gap-2 overflow-x-auto max-w-full">
82-
<div className="flex bg-gray-100 p-1 rounded-lg shrink-0">
83-
<button
84-
onClick={() => setView('dashboard')}
85-
className={`px-3 py-1.5 rounded-md text-sm font-medium transition-all flex items-center gap-2 ${view === 'dashboard' ? 'bg-white text-blue-600 shadow-sm' : 'text-gray-500 hover:text-gray-700'}`}
86-
>
87-
{t('common.dashboard')}
88-
</button>
89-
<button
90-
onClick={() => setView('scrape')}
91-
className={`px-3 py-1.5 rounded-md text-sm font-medium transition-all flex items-center gap-2 ${view === 'scrape' ? 'bg-white text-blue-600 shadow-sm' : 'text-gray-500 hover:text-gray-700'}`}
92-
>
93-
{t('common.scrape')}
94-
</button>
95-
<button
96-
onClick={() => setView('logs')}
97-
className={`px-3 py-1.5 rounded-md text-sm font-medium transition-all flex items-center gap-2 ${view === 'logs' ? 'bg-white text-blue-600 shadow-sm' : 'text-gray-500 hover:text-gray-700'}`}
98-
>
99-
{t('common.logs')}
100-
</button>
101-
<button
102-
onClick={() => setView('configuration')}
103-
className={`px-3 py-1.5 rounded-md text-sm font-medium transition-all flex items-center gap-2 ${view === 'configuration' ? 'bg-white text-blue-600 shadow-sm' : 'text-gray-500 hover:text-gray-700'}`}
104-
title={t('common.configuration')}
105-
>
106-
{t('common.configuration')}
107-
</button>
81+
<div className="flex items-center gap-0.5 border-s border-gray-200 ps-1.5 ms-0.5">
82+
<button
83+
type="button"
84+
onClick={toggleLanguage}
85+
className="h-9 w-9 inline-flex items-center justify-center rounded-full text-gray-500 hover:text-emerald-800 hover:bg-emerald-50 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-emerald-500"
86+
aria-label={t('common.language_toggle_aria')}
87+
title={i18n.language === 'he' ? t('common.english') : t('common.hebrew')}
88+
>
89+
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden>
90+
<path
91+
strokeLinecap="round"
92+
strokeLinejoin="round"
93+
strokeWidth={1.75}
94+
d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
95+
/>
96+
</svg>
97+
</button>
98+
<button
99+
type="button"
100+
onClick={() => window.open(`${import.meta.env.BASE_URL}GUIDE.html`, '_blank')}
101+
className="h-9 w-9 inline-flex items-center justify-center rounded-full text-gray-500 hover:text-emerald-800 hover:bg-emerald-50 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-emerald-500"
102+
title={t('common.help')}
103+
aria-label={t('common.help')}
104+
>
105+
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden>
106+
<path
107+
strokeLinecap="round"
108+
strokeLinejoin="round"
109+
strokeWidth={1.75}
110+
d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
111+
/>
112+
</svg>
113+
</button>
114+
{onboarding.completed ? (
115+
<button
116+
type="button"
117+
onClick={() => {
118+
if (window.confirm(t('onboarding.rerun_confirm'))) {
119+
onboarding.restartWizard();
120+
}
121+
}}
122+
className="h-9 w-9 inline-flex items-center justify-center rounded-full text-indigo-600 hover:text-indigo-800 hover:bg-indigo-50 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500"
123+
title={t('onboarding.rerun_wizard')}
124+
aria-label={t('onboarding.rerun_wizard')}
125+
>
126+
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden>
127+
<path
128+
strokeLinecap="round"
129+
strokeLinejoin="round"
130+
strokeWidth={1.75}
131+
d="M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09zM18.259 8.715L18 9.75l-.259-1.035a3.375 3.375 0 00-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 002.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 002.456 2.456L21.75 6l-1.035.259a3.375 3.375 0 00-2.456 2.456zM16.894 20.567L16.5 21.75l-.394-1.183a2.25 2.25 0 00-1.423-1.423L13.5 18.75l1.183-.394a2.25 2.25 0 001.423-1.423l.394-1.183.394 1.183a2.25 2.25 0 001.423 1.423l1.183.394-1.183.394a2.25 2.25 0 00-1.423 1.423z"
132+
/>
133+
</svg>
134+
</button>
135+
) : (
136+
<button
137+
type="button"
138+
onClick={() => setView('configuration')}
139+
className="h-9 w-9 inline-flex items-center justify-center rounded-full text-gray-500 hover:text-emerald-800 hover:bg-emerald-50 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-emerald-500"
140+
title={t('common.configuration')}
141+
aria-label={t('common.open_configuration_aria')}
142+
>
143+
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden>
144+
<path
145+
strokeLinecap="round"
146+
strokeLinejoin="round"
147+
strokeWidth={1.75}
148+
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"
149+
/>
150+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.75} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
151+
</svg>
152+
</button>
153+
)}
154+
</div>
108155
</div>
156+
</div>
109157

110-
{view === 'dashboard' && <DashboardAlertsDropdown selectedMonth={selectedMonth} />}
111-
112-
{onboarding.completed && (
158+
<nav
159+
className="flex flex-nowrap overflow-x-auto scrollbar-none items-stretch gap-0.5 sm:gap-3 min-w-0 w-full justify-start sm:justify-center -mx-1 px-1"
160+
role="tablist"
161+
aria-label={t('common.main_navigation_aria')}
162+
>
163+
{(
164+
[
165+
['dashboard', t('common.dashboard')] as const,
166+
['scrape', t('common.scrape')] as const,
167+
['logs', t('common.logs')] as const,
168+
['configuration', t('common.configuration')] as const,
169+
] as const
170+
).map(([key, label]) => (
113171
<button
172+
key={key}
114173
type="button"
115-
onClick={() => {
116-
if (window.confirm(t('onboarding.rerun_confirm'))) {
117-
onboarding.restartWizard();
118-
}
119-
}}
120-
className="px-3 py-1.5 bg-indigo-50 hover:bg-indigo-100 rounded-md text-xs font-bold text-indigo-700 transition-colors border border-indigo-200"
121-
title={t('onboarding.rerun_wizard')}
174+
role="tab"
175+
aria-selected={view === key}
176+
onClick={() => setView(key)}
177+
className={`shrink-0 px-2 sm:px-3 py-2 text-sm transition-colors border-b-2 -mb-px focus:outline-none focus-visible:ring-2 focus-visible:ring-emerald-500 focus-visible:ring-offset-2 rounded-t ${
178+
view === key
179+
? 'font-semibold text-emerald-800 border-emerald-600'
180+
: 'font-medium text-gray-500 border-transparent hover:text-gray-800'
181+
}`}
122182
>
123-
{t('onboarding.rerun_wizard')}
183+
{label}
124184
</button>
125-
)}
126-
<button
127-
onClick={() => window.open(`${import.meta.env.BASE_URL}GUIDE.html`, '_blank')}
128-
className="px-3 py-1.5 bg-gray-100 hover:bg-gray-200 rounded-md text-xs font-bold text-gray-600 transition-colors border border-gray-200 flex items-center gap-1.5"
129-
title={t('common.help')}
130-
>
131-
[?]
132-
</button>
133-
<button
134-
onClick={toggleLanguage}
135-
className="px-3 py-1.5 bg-gray-100 hover:bg-gray-200 rounded-md text-xs font-bold text-gray-600 transition-colors border border-gray-200"
136-
>
137-
{i18n.language === 'he' ? 'EN' : 'HE'}
138-
</button>
139-
</div>
140-
141-
<div className="text-sm text-gray-500 hidden md:block">
142-
{t('common.version')}
143-
</div>
185+
))}
186+
</nav>
144187
</div>
145188
</header>
146189

@@ -242,7 +285,7 @@ function App() {
242285
)}
243286

244287
<div className="flex flex-1 overflow-hidden">
245-
<div className="flex-1 overflow-hidden relative bg-gray-50/50">
288+
<div className="flex-1 overflow-hidden relative bg-white">
246289
<div className={view === 'dashboard' ? 'h-full overflow-y-auto p-4' : 'hidden'}>
247290
<FinancialCommandCenter
248291
selectedMonth={selectedMonth}

client/src/components/AnalyticsDashboard.tsx

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -59,21 +59,6 @@ export function AnalyticsDashboard({
5959
});
6060
}, [activeDayFilter]);
6161

62-
if (!displayTransactions || displayTransactions.length === 0) {
63-
return (
64-
<div className="flex items-center justify-center h-64 text-gray-400">
65-
<p>{t('analytics.no_data')}</p>
66-
</div>
67-
);
68-
}
69-
70-
const formatCurrency = (value: number) =>
71-
new Intl.NumberFormat(i18n.language === 'he' ? 'he-IL' : 'en-US', {
72-
style: 'currency',
73-
currency: 'ILS',
74-
maximumFractionDigits: 0
75-
}).format(value);
76-
7762
const treemapCell = useMemo(() => {
7863
const rtl = i18n.dir() === 'rtl';
7964
const lang = i18n.language;
@@ -216,6 +201,21 @@ export function AnalyticsDashboard({
216201
};
217202
}, [t, i18n]);
218203

204+
const formatCurrency = (value: number) =>
205+
new Intl.NumberFormat(i18n.language === 'he' ? 'he-IL' : 'en-US', {
206+
style: 'currency',
207+
currency: 'ILS',
208+
maximumFractionDigits: 0
209+
}).format(value);
210+
211+
if (!displayTransactions || displayTransactions.length === 0) {
212+
return (
213+
<div className="flex items-center justify-center h-64 text-gray-400">
214+
<p>{t('analytics.no_data')}</p>
215+
</div>
216+
);
217+
}
218+
219219
const getWeekdayLabel = (dayIndex: number) => {
220220
const baseSunday = new Date(Date.UTC(2024, 0, 7 + dayIndex));
221221
return new Intl.DateTimeFormat(i18n.language === 'he' ? 'he-IL' : 'en-US', {

0 commit comments

Comments
 (0)