feat(finance): implement project finance and rate card management features
- Added new controllers for managing project finance and rate cards, including CRUD operations for rate card roles and project finance tasks. - Introduced API routes for project finance and rate card functionalities, enhancing the backend structure. - Developed frontend components for displaying and managing project finance data, including a finance drawer and rate card settings. - Enhanced localization files to support new UI elements and ensure consistency across multiple languages. - Implemented utility functions for handling man-days and financial calculations, improving overall functionality.
This commit is contained in:
@@ -5,6 +5,7 @@ import i18n from '@/i18n';
|
||||
// Import core components synchronously to avoid suspense in main tabs
|
||||
import ProjectViewEnhancedBoard from '@/pages/projects/projectView/enhancedBoard/project-view-enhanced-board';
|
||||
import TaskListV2 from '@/components/task-list-v2/TaskListV2';
|
||||
import ProjectViewFinance from '@/pages/projects/projectView/finance/ProjectViewFinance';
|
||||
|
||||
// Lazy load less critical components
|
||||
const ProjectViewInsights = React.lazy(
|
||||
@@ -117,6 +118,16 @@ export const tabItems: TabItems[] = [
|
||||
React.createElement(ProjectViewUpdates)
|
||||
),
|
||||
},
|
||||
{
|
||||
index: 6,
|
||||
key: 'finance',
|
||||
label: getTabLabel('finance'),
|
||||
element: React.createElement(
|
||||
Suspense,
|
||||
{ fallback: React.createElement(InlineSuspenseFallback) },
|
||||
React.createElement(ProjectViewFinance)
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
// Function to update tab labels when language changes
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
export enum FinanceTableColumnKeys {
|
||||
TASK = 'task',
|
||||
MEMBERS = 'members',
|
||||
HOURS = 'hours',
|
||||
MAN_DAYS = 'man_days',
|
||||
TOTAL_TIME_LOGGED = 'total_time_logged',
|
||||
ESTIMATED_COST = 'estimated_cost',
|
||||
COST = 'cost',
|
||||
FIXED_COST = 'fixedCost',
|
||||
TOTAL_BUDGET = 'totalBudget',
|
||||
TOTAL_ACTUAL = 'totalActual',
|
||||
VARIANCE = 'variance',
|
||||
}
|
||||
|
||||
type FinanceTableColumnsType = {
|
||||
key: FinanceTableColumnKeys;
|
||||
name: string;
|
||||
width: number;
|
||||
type: 'string' | 'hours' | 'currency' | 'man_days' | 'effort_variance';
|
||||
render?: (value: any) => React.ReactNode;
|
||||
};
|
||||
|
||||
// finance table columns
|
||||
export const financeTableColumns: FinanceTableColumnsType[] = [
|
||||
{
|
||||
key: FinanceTableColumnKeys.TASK,
|
||||
name: 'taskColumn',
|
||||
width: 240,
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
key: FinanceTableColumnKeys.MEMBERS,
|
||||
name: 'membersColumn',
|
||||
width: 120,
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
key: FinanceTableColumnKeys.HOURS,
|
||||
name: 'hoursColumn',
|
||||
width: 100,
|
||||
type: 'hours',
|
||||
},
|
||||
{
|
||||
key: FinanceTableColumnKeys.MAN_DAYS,
|
||||
name: 'manDaysColumn',
|
||||
width: 100,
|
||||
type: 'man_days',
|
||||
},
|
||||
{
|
||||
key: FinanceTableColumnKeys.TOTAL_TIME_LOGGED,
|
||||
name: 'totalTimeLoggedColumn',
|
||||
width: 120,
|
||||
type: 'hours',
|
||||
},
|
||||
{
|
||||
key: FinanceTableColumnKeys.ESTIMATED_COST,
|
||||
name: 'estimatedCostColumn',
|
||||
width: 120,
|
||||
type: 'currency',
|
||||
},
|
||||
{
|
||||
key: FinanceTableColumnKeys.COST,
|
||||
name: 'costColumn',
|
||||
width: 120,
|
||||
type: 'currency',
|
||||
},
|
||||
{
|
||||
key: FinanceTableColumnKeys.FIXED_COST,
|
||||
name: 'fixedCostColumn',
|
||||
width: 120,
|
||||
type: 'currency',
|
||||
},
|
||||
{
|
||||
key: FinanceTableColumnKeys.TOTAL_BUDGET,
|
||||
name: 'totalBudgetedCostColumn',
|
||||
width: 120,
|
||||
type: 'currency',
|
||||
},
|
||||
{
|
||||
key: FinanceTableColumnKeys.TOTAL_ACTUAL,
|
||||
name: 'totalActualCostColumn',
|
||||
width: 120,
|
||||
type: 'currency',
|
||||
},
|
||||
{
|
||||
key: FinanceTableColumnKeys.VARIANCE,
|
||||
name: 'varianceColumn',
|
||||
width: 120,
|
||||
type: 'currency',
|
||||
},
|
||||
];
|
||||
|
||||
// Function to get columns based on calculation method
|
||||
export const getFinanceTableColumns = (
|
||||
calculationMethod: 'hourly' | 'man_days' = 'hourly'
|
||||
): FinanceTableColumnsType[] => {
|
||||
return financeTableColumns.filter(column => {
|
||||
// Always show these columns
|
||||
if (
|
||||
[
|
||||
FinanceTableColumnKeys.TASK,
|
||||
FinanceTableColumnKeys.MEMBERS,
|
||||
FinanceTableColumnKeys.TOTAL_TIME_LOGGED,
|
||||
FinanceTableColumnKeys.ESTIMATED_COST,
|
||||
FinanceTableColumnKeys.COST,
|
||||
FinanceTableColumnKeys.FIXED_COST,
|
||||
FinanceTableColumnKeys.TOTAL_BUDGET,
|
||||
FinanceTableColumnKeys.TOTAL_ACTUAL,
|
||||
FinanceTableColumnKeys.VARIANCE,
|
||||
].includes(column.key)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Show hours column only for hourly calculation
|
||||
if (column.key === FinanceTableColumnKeys.HOURS) {
|
||||
return calculationMethod === 'hourly';
|
||||
}
|
||||
|
||||
// Show man days columns only for man days calculation
|
||||
if ([FinanceTableColumnKeys.MAN_DAYS].includes(column.key)) {
|
||||
return calculationMethod === 'man_days';
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
import RateCardSettings from '@/pages/settings/rate-card-settings/RateCardSettings';
|
||||
import {
|
||||
BankOutlined,
|
||||
FileZipOutlined,
|
||||
@@ -12,20 +13,33 @@ import {
|
||||
UserOutlined,
|
||||
UserSwitchOutlined,
|
||||
BulbOutlined,
|
||||
DollarCircleOutlined
|
||||
} from '@/shared/antd-imports';
|
||||
import React, { ReactNode, lazy } from 'react';
|
||||
const ProfileSettings = lazy(() => import('../../pages/settings/profile/profile-settings'));
|
||||
const NotificationsSettings = lazy(() => import('../../pages/settings/notifications/notifications-settings'));
|
||||
const NotificationsSettings = lazy(
|
||||
() => import('../../pages/settings/notifications/notifications-settings')
|
||||
);
|
||||
const ClientsSettings = lazy(() => import('../../pages/settings/clients/clients-settings'));
|
||||
const JobTitlesSettings = lazy(() => import('@/pages/settings/job-titles/job-titles-settings'));
|
||||
const LabelsSettings = lazy(() => import('../../pages/settings/labels/labels-settings'));
|
||||
const CategoriesSettings = lazy(() => import('../../pages/settings/categories/categories-settings'));
|
||||
const ProjectTemplatesSettings = lazy(() => import('@/pages/settings/project-templates/project-templates-settings'));
|
||||
const TaskTemplatesSettings = lazy(() => import('@/pages/settings/task-templates/task-templates-settings'));
|
||||
const TeamMembersSettings = lazy(() => import('@/pages/settings/team-members/team-members-settings'));
|
||||
const CategoriesSettings = lazy(
|
||||
() => import('../../pages/settings/categories/categories-settings')
|
||||
);
|
||||
const ProjectTemplatesSettings = lazy(
|
||||
() => import('@/pages/settings/project-templates/project-templates-settings')
|
||||
);
|
||||
const TaskTemplatesSettings = lazy(
|
||||
() => import('@/pages/settings/task-templates/task-templates-settings')
|
||||
);
|
||||
const TeamMembersSettings = lazy(
|
||||
() => import('@/pages/settings/team-members/team-members-settings')
|
||||
);
|
||||
const TeamsSettings = lazy(() => import('../../pages/settings/teams/teams-settings'));
|
||||
const ChangePassword = lazy(() => import('@/pages/settings/change-password/change-password'));
|
||||
const LanguageAndRegionSettings = lazy(() => import('@/pages/settings/language-and-region/language-and-region-settings'));
|
||||
const LanguageAndRegionSettings = lazy(
|
||||
() => import('@/pages/settings/language-and-region/language-and-region-settings')
|
||||
);
|
||||
const AppearanceSettings = lazy(() => import('@/pages/settings/appearance/appearance-settings'));
|
||||
|
||||
// type of menu item in settings sidebar
|
||||
@@ -132,6 +146,13 @@ export const settingsItems: SettingMenuItems[] = [
|
||||
element: React.createElement(TeamMembersSettings),
|
||||
adminOnly: true,
|
||||
},
|
||||
{
|
||||
key: 'ratecard',
|
||||
name: 'Rate Card',
|
||||
endpoint: 'ratecard',
|
||||
icon: React.createElement(DollarCircleOutlined),
|
||||
element: React.createElement(RateCardSettings),
|
||||
},
|
||||
{
|
||||
key: 'teams',
|
||||
name: 'teams',
|
||||
|
||||
Reference in New Issue
Block a user