Centralized_Rreporting_System/.cursor/rules/coding_statndard.mdc
2025-09-05 18:09:40 +05:30

510 lines
13 KiB
Plaintext

---
description:
globs:
alwaysApply: true
---
# SME Centralized Reporting System - Coding Standards & Development Patterns
## OVERVIEW
This document defines coding standards, development patterns, and implementation guidelines for the SME Centralized Reporting System React Native application.
---
## 1. CODING STANDARDS & CONVENTIONS
### File Naming Conventions
- **Components**: PascalCase (`ZohoProjectsDashboardScreen.tsx`)
- **Services**: camelCase (`zohoProjectsAPI.ts`)
- **Store files**: camelCase (`zohoProjectsSlice.ts`)
- **Types**: PascalCase (`ProfileTypes.ts`)
- **Constants**: UPPER_SNAKE_CASE (`API_ENDPOINTS.ts`)
- **Utilities**: camelCase (`dateUtils.ts`)
- **Hooks**: camelCase with 'use' prefix (`useZohoProjectsData.ts`)
### Directory Naming Conventions
- **Modules**: camelCase (`auth`, `zohoProjects`, `hr`, `profile`)
- **Components**: camelCase (`widgets`, `forms`, `screens`)
- **Services**: camelCase (`integrations`, `analytics`)
### Module Directory Rules
- Place `screens`, `widgets`, and `forms` inside `components/` within each module.
- Keep `components/` for UI-only code. Do not add `utils` or `constants` under modules.
- Share cross-cutting helpers via `src/shared/utils` and global constants via `src/shared/constants`.
### Variable & Function Naming
```typescript
// Variables: camelCase
const projectList = [];
const isLoading = false;
const userPreferences = {};
// Functions: camelCase with descriptive verbs
const fetchZohoProjectsData = () => {};
const calculateOpenTasks = () => {};
const handleSubmitForm = () => {};
// Constants: UPPER_SNAKE_CASE
const API_BASE_URL = '';
const MAX_RETRY_ATTEMPTS = 3;
const DEFAULT_PAGE_SIZE = 20;
// Interfaces/Types: PascalCase
interface ZohoProject {
id: string;
name: string;
}
type LoadingState = 'idle' | 'loading' | 'succeeded' | 'failed';
```
---
### Inline Commenting Guidelines
- Always add clear, concise inline comments for non-obvious logic, complex conditions, side-effects, and business rules.
- Prefer sentence-case, developer-oriented comments that explain "why" not just "what".
- Document assumptions, edge cases, units, and non-trivial parameter/return semantics.
- Keep comments up-to-date when changing code; remove stale comments immediately.
- For components, briefly annotate major sections (effects, handlers, conditional rendering) and tricky UI logic.
Example:
```typescript
// Fetch projects on mount and when filters change to ensure fresh data
useEffect(() => {
// Guard: avoid duplicate fetch while loading
if (loading) return;
dispatch(fetchZohoProjects());
}, [dispatch, loading, filters]);
// Compute SLA breach percentage (0-100). Includes only active tasks.
const breachRate = useMemo(() => {
if (totalTasks === 0) return 0; // Avoid divide-by-zero
return Math.round((breachedTasks / totalTasks) * 100);
}, [breachedTasks, totalTasks]);
```
---
## 2. COMPONENT STRUCTURE TEMPLATES
### Screen Component Template
```typescript
import React, { useEffect, useState } from 'react';
import {
View,
Text,
ScrollView,
RefreshControl,
StyleSheet,
} from 'react-native';
import { useSelector, useDispatch } from 'react-redux';
import Icon from 'react-native-vector-icons/MaterialIcons';
// Shared imports
import { Container, LoadingSpinner, ErrorState } from '@/shared/components/ui';
import { COLORS, FONTS, SPACING } from '@/shared/styles/theme';
// Module imports
import { selectZohoProjects, selectZohoProjectsLoading } from '../store/selectors';
import { fetchZohoProjects } from '../store/zohoProjectsSlice';
// Types
import type { ZohoProjectsScreenProps } from '../types';
const ZohoProjectsDashboardScreen: React.FC<ZohoProjectsScreenProps> = ({
navigation,
route,
}) => {
// State
const [refreshing, setRefreshing] = useState(false);
// Redux
const dispatch = useDispatch();
const data = useSelector(selectZohoProjects);
const loading = useSelector(selectZohoProjectsLoading);
// Effects
useEffect(() => {
dispatch(fetchZohoProjects());
}, [dispatch]);
// Handlers
const handleRefresh = async () => {
setRefreshing(true);
await dispatch(fetchZohoProjects()).unwrap();
setRefreshing(false);
};
const handleNavigateToDetails = (id: string) => {
navigation.navigate('ProjectDetails', { id });
};
// Loading state
if (loading && !data.length) {
return <LoadingSpinner />;
}
// Error state
if (error) {
return <ErrorState onRetry={() => dispatch(fetchZohoProjects())} />;
}
return (
<Container>
<ScrollView
style={styles.container}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />
}
>
<View style={styles.header}>
<Text style={styles.title}>Zoho Projects</Text>
<Icon
name="insights"
size={24}
color={COLORS.primary}
/>
</View>
{/* Dashboard content */}
<View style={styles.content}>
{/* Widgets and components */}
</View>
</ScrollView>
</Container>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: COLORS.background,
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
padding: SPACING.md,
backgroundColor: COLORS.surface,
},
title: {
fontSize: 24,
fontFamily: FONTS.bold,
color: COLORS.text,
},
content: {
padding: SPACING.md,
},
});
export default ZohoProjectsDashboardScreen;
```
### Widget Component Template
```typescript
import React from 'react';
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import { COLORS, FONTS, SPACING, SHADOWS } from '@/shared/styles/theme';
interface RevenueChartProps {
data: RevenueData[];
period: 'month' | 'quarter' | 'year';
onPeriodChange: (period: 'month' | 'quarter' | 'year') => void;
onPress?: () => void;
}
const RevenueChart: React.FC<RevenueChartProps> = ({
data,
period,
onPeriodChange,
onPress,
}) => {
const totalRevenue = data.reduce((sum, item) => sum + item.amount, 0);
return (
<TouchableOpacity
style={styles.container}
onPress={onPress}
activeOpacity={0.8}
>
<View style={styles.header}>
<View style={styles.titleContainer}>
<Icon name="trending-up" size={20} color={COLORS.primary} />
<Text style={styles.title}>Revenue Analytics</Text>
</View>
<View style={styles.periodSelector}>
{['month', 'quarter', 'year'].map((p) => (
<TouchableOpacity
key={p}
style={[
styles.periodButton,
period === p && styles.periodButtonActive,
]}
onPress={() => onPeriodChange(p as any)}
>
<Text
style={[
styles.periodText,
period === p && styles.periodTextActive,
]}
>
{p.charAt(0).toUpperCase() + p.slice(1)}
</Text>
</TouchableOpacity>
))}
</View>
</View>
<View style={styles.content}>
<Text style={styles.amount}>
${totalRevenue.toLocaleString()}
</Text>
<Text style={styles.subtitle}>Total Revenue</Text>
{/* Chart component would go here */}
<View style={styles.chartPlaceholder}>
<Text style={styles.chartText}>Chart Component</Text>
</View>
</View>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: COLORS.surface,
borderRadius: 12,
padding: SPACING.md,
...SHADOWS.medium,
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: SPACING.md,
},
titleContainer: {
flexDirection: 'row',
alignItems: 'center',
},
title: {
fontSize: 16,
fontFamily: FONTS.medium,
color: COLORS.text,
marginLeft: SPACING.xs,
},
periodSelector: {
flexDirection: 'row',
backgroundColor: COLORS.background,
borderRadius: 6,
padding: 2,
},
periodButton: {
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 4,
},
periodButtonActive: {
backgroundColor: COLORS.primary,
},
periodText: {
fontSize: 12,
fontFamily: FONTS.regular,
color: COLORS.textLight,
},
periodTextActive: {
color: COLORS.surface,
},
content: {
alignItems: 'flex-start',
},
amount: {
fontSize: 28,
fontFamily: FONTS.bold,
color: COLORS.text,
},
subtitle: {
fontSize: 14,
fontFamily: FONTS.regular,
color: COLORS.textLight,
marginBottom: SPACING.md,
},
chartPlaceholder: {
width: '100%',
height: 120,
backgroundColor: COLORS.background,
borderRadius: 8,
justifyContent: 'center',
alignItems: 'center',
},
chartText: {
fontSize: 14,
fontFamily: FONTS.regular,
color: COLORS.textLight,
},
});
export default RevenueChart;
```
---
## 3. REDUX STATE MANAGEMENT PATTERNS
### Store Configuration
```typescript
// src/store/store.ts
import { configureStore } from '@reduxjs/toolkit';
import { persistStore, persistReducer } from 'redux-persist';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { combineReducers } from '@reduxjs/toolkit';
// Import slices
import authSlice from '@/modules/auth/store/authSlice';
import hrSlice from '@/modules/hr/store/hrSlice';
import zohoProjectsSlice from '@/modules/zohoProjects/store/zohoProjectsSlice';
import profileSlice from '@/modules/profile/store/profileSlice';
import uiSlice from '@/shared/store/uiSlice';
const rootReducer = combineReducers({
auth: authSlice.reducer,
hr: hrSlice.reducer,
zohoProjects: zohoProjectsSlice.reducer,
profile: profileSlice.reducer,
ui: uiSlice.reducer,
});
const persistConfig = {
key: 'root',
storage: AsyncStorage,
whitelist: ['auth', 'hr', 'zohoProjects', 'profile'],
blacklist: ['ui'], // Don't persist UI state
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
export const store = configureStore({
reducer: persistedReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: ['persist/FLUSH', 'persist/REHYDRATE', 'persist/PAUSE', 'persist/PERSIST', 'persist/PURGE', 'persist/REGISTER'],
},
}),
});
export const persistor = persistStore(store);
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
```
### Redux Slice Template (Zoho Projects example)
```typescript
// src/modules/zohoProjects/store/zohoProjectsSlice.ts
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
import { zohoProjectsAPI } from '../services/zohoProjectsAPI';
import type { ZohoProjectsState, ZohoProject, ZohoProjectsFilters } from './types';
// Initial state
const initialState: ZohoProjectsState = {
projects: [],
loading: false,
error: null,
filters: { owner: 'all', status: 'all' },
lastUpdated: null,
};
// Async thunks
export const fetchZohoProjects = createAsyncThunk(
'zohoProjects/fetch',
async () => {
const response = await zohoProjectsAPI.getProjects();
return response.data;
}
);
export const updateZohoProject = createAsyncThunk(
'zohoProjects/update',
async (project: Partial<ZohoProject>) => {
const response = await zohoProjectsAPI.updateProject(project);
return response.data;
}
);
// Slice
const zohoProjectsSlice = createSlice({
name: 'zohoProjects',
initialState,
reducers: {
setFilters: (state, action: PayloadAction<Partial<ZohoProjectsFilters>>) => {
state.filters = { ...state.filters, ...action.payload };
},
clearError: (state) => {
state.error = null;
},
resetState: () => initialState,
},
extraReducers: (builder) => {
// add async case reducers here
},
});
export default zohoProjectsSlice;
```
### Redux Slice Template
```typescript
// src/modules/finance/store/financeSlice.ts
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
import { financeAPI } from '../services/financeAPI';
import type { FinanceState, FinanceData, FinanceFilters } from './types';
// Initial state
const initialState: FinanceState = {
data: [],
loading: false,
error: null,
filters: {
period: 'month',
category: 'all',
dateRange: null,
},
lastUpdated: null,
};
// Async thunks
export const fetchFinanceData = createAsyncThunk(
'finance/fetchData',
async (params?: { refresh?: boolean }) => {
const response = await financeAPI.getData(params);
return response.data;
}
);
export const updateFinanceData = createAsyncThunk(
'finance/updateData',
async (data: Partial<FinanceData>) => {
const response = await financeAPI.updateData(data);
return response.data;
}
);
// Slice
const financeSlice = createSlice({
name: 'finance',
initialState,
reducers: {
setFilters: (state, action: PayloadAction<Partial<FinanceFilters>>) => {
state.filters = { ...state.filters, ...action.payload };
},
clearError: (state) => {
state.error = null;
},
resetState: () => initialState,
},
extraReducers: (builder) => {
builder
// Fetch data cases
.addCase(fetchFinanceData