diff --git a/worklenz-frontend/public/locales/en/time-report.json b/worklenz-frontend/public/locales/en/time-report.json
index 5d73c247..4a418b6d 100644
--- a/worklenz-frontend/public/locales/en/time-report.json
+++ b/worklenz-frontend/public/locales/en/time-report.json
@@ -5,6 +5,7 @@
"searchByName": "Search by name",
"selectAll": "Select All",
+ "clearAll": "Clear All",
"teams": "Teams",
"searchByProject": "Search by project name",
@@ -15,6 +16,8 @@
"billable": "Billable",
"nonBillable": "Non Billable",
+ "filterByBillableStatus": "Filter by Billable Status",
+ "allBillableTypes": "All Billable Types",
"total": "Total",
diff --git a/worklenz-frontend/public/locales/es/time-report.json b/worklenz-frontend/public/locales/es/time-report.json
index 03fdd744..eb99c8e5 100644
--- a/worklenz-frontend/public/locales/es/time-report.json
+++ b/worklenz-frontend/public/locales/es/time-report.json
@@ -5,6 +5,7 @@
"searchByName": "Buscar por nombre",
"selectAll": "Seleccionar Todo",
+ "clearAll": "Limpiar Todo",
"teams": "Equipos",
"searchByProject": "Buscar por nombre de proyecto",
@@ -15,6 +16,8 @@
"billable": "Facturable",
"nonBillable": "No Facturable",
+ "filterByBillableStatus": "Filtrar por Estado Facturable",
+ "allBillableTypes": "Todos los Tipos Facturables",
"total": "Total",
diff --git a/worklenz-frontend/public/locales/pt/time-report.json b/worklenz-frontend/public/locales/pt/time-report.json
index 97679df8..2467274d 100644
--- a/worklenz-frontend/public/locales/pt/time-report.json
+++ b/worklenz-frontend/public/locales/pt/time-report.json
@@ -5,6 +5,7 @@
"searchByName": "Pesquisar por nome",
"selectAll": "Selecionar Todos",
+ "clearAll": "Limpar Todos",
"teams": "Equipes",
"searchByProject": "Pesquisar por nome do projeto",
@@ -15,6 +16,8 @@
"billable": "Cobrável",
"nonBillable": "Não Cobrável",
+ "filterByBillableStatus": "Filtrar por Status de Cobrança",
+ "allBillableTypes": "Todos os Tipos Cobráveis",
"total": "Total",
diff --git a/worklenz-frontend/src/pages/reporting/timeReports/page-header/billable.tsx b/worklenz-frontend/src/pages/reporting/timeReports/page-header/billable.tsx
index ffb15f59..cea08c8e 100644
--- a/worklenz-frontend/src/pages/reporting/timeReports/page-header/billable.tsx
+++ b/worklenz-frontend/src/pages/reporting/timeReports/page-header/billable.tsx
@@ -1,45 +1,236 @@
import { setSelectOrDeselectBillable } from '@/features/reporting/time-reports/time-reports-overview.slice';
import { useAppDispatch } from '@/hooks/useAppDispatch';
import { useAppSelector } from '@/hooks/useAppSelector';
-import { CaretDownFilled } from '@ant-design/icons';
-import { Button, Checkbox, Dropdown, MenuProps } from 'antd';
-import React from 'react';
+import { CaretDownFilled, FilterOutlined, CheckCircleFilled } from '@ant-design/icons';
+import { Button, Checkbox, Dropdown, MenuProps, Space, Badge, Divider, theme } from 'antd';
+import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
const Billable: React.FC = () => {
const { t } = useTranslation('time-report');
const dispatch = useAppDispatch();
+ const { token } = theme.useToken();
const { billable } = useAppSelector(state => state.timeReportsOverviewReducer);
+ // Calculate active filters count
+ const activeFiltersCount = useMemo(() => {
+ let count = 0;
+ if (billable.billable) count++;
+ if (billable.nonBillable) count++;
+ return count;
+ }, [billable.billable, billable.nonBillable]);
+
+ // Check if all options are selected
+ const isAllSelected = billable.billable && billable.nonBillable;
+ const isNoneSelected = !billable.billable && !billable.nonBillable;
+
+ // Handle select all
+ const handleSelectAll = () => {
+ dispatch(setSelectOrDeselectBillable({
+ billable: true,
+ nonBillable: true
+ }));
+ };
+
+ // Handle clear all
+ const handleClearAll = () => {
+ dispatch(setSelectOrDeselectBillable({
+ billable: false,
+ nonBillable: false
+ }));
+ };
+
+ // Theme-aware colors
+ const isDark = token.colorBgContainer !== '#ffffff';
+ const colors = {
+ headerText: isDark ? token.colorTextSecondary : '#262626',
+ borderColor: isDark ? token.colorBorder : '#f0f0f0',
+ linkActive: token.colorPrimary,
+ linkDisabled: isDark ? token.colorTextDisabled : '#d9d9d9',
+ successColor: token.colorSuccess,
+ errorColor: token.colorError,
+ buttonBorder: activeFiltersCount > 0 ? token.colorPrimary : token.colorBorder,
+ buttonText: activeFiltersCount > 0 ? token.colorPrimary : token.colorTextSecondary,
+ buttonBg: activeFiltersCount > 0 ? (isDark ? token.colorPrimaryBg : '#f6ffed') : 'transparent',
+ dropdownBg: token.colorBgElevated,
+ dropdownBorder: token.colorBorderSecondary,
+ };
+
// Dropdown items for the menu
const menuItems: MenuProps['items'] = [
{
- key: 'search',
- label: