AngularJS Application Structure¶
The Stratpoint Timesheet Application frontend is built using AngularJS 1.x with a modular, scalable architecture that promotes maintainability and code reusability. This section details the application structure, module organization, and architectural patterns.
Application Module Structure¶
Main Application Module¶
// Actual module definition from codebase
var StratpointTSApp = angular.module('StratpointTSApp', [
// Core Angular modules
'ngRoute', // Client-side routing
'ngResource', // RESTful resource handling
'ngSanitize', // HTML sanitization
'ngCookies', // Cookie management
'ngAnimate', // Animation support
// Third-party modules
'ngTable', // Data table functionality
'ngTableExport', // Table export capabilities
'ngCsv', // CSV export functionality
'ui.bootstrap', // Bootstrap UI components
'ui.calendar', // Calendar integration
'ui.select', // Enhanced select dropdowns
'ui.sortable', // Drag and drop sorting
'nsPopover', // Popover components
'ngTagsInput', // Tag input functionality
'ngLoadingSpinner', // Loading indicators
'angularFileUpload', // File upload handling
// Custom application modules
'stratpointtsCrud', // CRUD operations module
'StratpointTSControllers' // Controllers module
// Note: Services, directives, and filters are defined within the main app module
]);
Actual Module Dependencies¶
graph TB
A[StratpointTSApp] --> B[Core Angular Modules]
A --> C[Third-party Modules]
A --> D[Custom Modules]
B --> E[ngRoute]
B --> F[ngResource]
B --> G[ngSanitize]
B --> H[ngCookies]
B --> I[ngAnimate]
C --> J[ui.bootstrap]
C --> K[ngTable]
C --> L[ui.calendar]
C --> M[angularFileUpload]
C --> N[ui.select]
C --> O[ui.sortable]
C --> P[ngTagsInput]
D --> Q[StratpointTSControllers]
D --> R[stratpointtsCrud]
A --> S[Services - Defined in main module]
A --> T[Directives - Defined in main module]
A --> U[Filters - Defined in main module]
File Organization¶
Actual Directory Structure¶
public/
├── app/
│ ├── js/
│ │ ├── lib/ # Third-party libraries
│ │ │ ├── angular.min.js
│ │ │ ├── angular-route.min.js
│ │ │ ├── ui-bootstrap.min.js
│ │ │ └── ...
│ │ └── mine/ # Application code (flat structure)
│ │ ├── app.module.js # Main application module
│ │ ├── services.js # All services in one file
│ │ ├── navmenu.js # Navigation menu logic
│ │ ├── controllers_auth.js # Authentication controllers
│ │ ├── controllers_timelogs.js # Timelog controllers
│ │ ├── controllers_projects.js # Project controllers
│ │ ├── controllers_dashboard.js # Dashboard controllers
│ │ ├── controllers_users.js # User controllers
│ │ ├── controllers_reports.js # Report controllers
│ │ ├── controllers_revenues.js # Revenue controllers
│ │ ├── controllers_profits.js # Profit controllers
│ │ ├── controllers_leaves.js # Leave controllers
│ │ ├── controllers_deals.js # Deal controllers
│ │ └── ... (other controller files)
│ ├── css/
│ │ ├── lib/ # Third-party CSS
│ │ └── mine/ # Custom styles
│ └── partials/ # HTML templates
│ ├── login.html
│ ├── dashboard/
│ ├── tasks/
│ ├── projects/
│ ├── users/
│ └── ... (organized by feature)
└── build/
└── js/
└── app.js # Minified/concatenated application
├── auth/
├── dashboard/
├── timelogs/
├── projects/
├── users/
├── reports/
└── common/
Application Configuration¶
Main Configuration¶
// app.config.js
StratpointTSApp.config([
'$routeProvider',
'$locationProvider',
'$httpProvider',
'$compileProvider',
function($routeProvider, $locationProvider, $httpProvider, $compileProvider) {
// Configure HTML5 mode
$locationProvider.html5Mode({
enabled: true,
requireBase: false
});
// Configure HTTP interceptors
$httpProvider.interceptors.push('AuthInterceptor');
$httpProvider.interceptors.push('ErrorInterceptor');
$httpProvider.interceptors.push('LoadingInterceptor');
// Disable debug info in production
if (window.APP_ENV === 'production') {
$compileProvider.debugInfoEnabled(false);
$compileProvider.commentDirectivesEnabled(false);
$compileProvider.cssClassDirectivesEnabled(false);
}
// Configure default route
$routeProvider.otherwise({
redirectTo: '/dashboard'
});
}
]);
Route Configuration¶
// app.routes.js
StratpointTSApp.config(['$routeProvider', function($routeProvider) {
$routeProvider
// Authentication routes
.when('/', {
title: 'Login',
templateUrl: 'app/partials/auth/login.html',
controller: 'LoginController',
controllerAs: 'vm'
})
.when('/login', {
title: 'Login',
templateUrl: 'app/partials/auth/login.html',
controller: 'LoginController',
controllerAs: 'vm'
})
// Dashboard routes
.when('/dashboard', {
title: 'Dashboard',
templateUrl: 'app/partials/dashboard/index.html',
controller: 'DashboardController',
controllerAs: 'vm',
permission: 'view_dashboard',
resolve: {
dashboardData: ['DashboardService', function(DashboardService) {
return DashboardService.getDashboardData();
}]
}
})
// Timesheet routes
.when('/timelogs', {
title: 'Timelogs',
templateUrl: 'app/partials/timelogs/index.html',
controller: 'TimelogController',
controllerAs: 'vm',
permission: 'view_timelogs'
})
.when('/timelogs/add', {
title: 'Add Timelog',
templateUrl: 'app/partials/timelogs/form.html',
controller: 'TimelogFormController',
controllerAs: 'vm',
permission: 'create_timelogs'
})
.when('/timelogs/:id/edit', {
title: 'Edit Timelog',
templateUrl: 'app/partials/timelogs/form.html',
controller: 'TimelogFormController',
controllerAs: 'vm',
permission: 'edit_timelogs'
})
// Project routes
.when('/projects', {
title: 'Projects',
templateUrl: 'app/partials/projects/index.html',
controller: 'ProjectController',
controllerAs: 'vm',
permission: 'view_projects'
})
.when('/projects/:id', {
title: 'Project Details',
templateUrl: 'app/partials/projects/detail.html',
controller: 'ProjectDetailController',
controllerAs: 'vm',
permission: 'view_project_details'
})
// User management routes
.when('/users', {
title: 'Users',
templateUrl: 'app/partials/users/index.html',
controller: 'UserController',
controllerAs: 'vm',
permission: 'view_users'
})
.when('/profile', {
title: 'Profile',
templateUrl: 'app/partials/users/profile.html',
controller: 'ProfileController',
controllerAs: 'vm',
permission: 'view_profile'
})
// Report routes
.when('/reports', {
title: 'Reports',
templateUrl: 'app/partials/reports/index.html',
controller: 'ReportController',
controllerAs: 'vm',
permission: 'view_reports'
})
.when('/reports/:type', {
title: 'Report',
templateUrl: 'app/partials/reports/detail.html',
controller: 'ReportDetailController',
controllerAs: 'vm',
permission: 'view_reports'
})
// Error routes
.when('/unauthorized', {
title: 'Unauthorized',
templateUrl: 'app/partials/errors/unauthorized.html'
})
.when('/not-found', {
title: 'Not Found',
templateUrl: 'app/partials/errors/not-found.html'
});
}]);
Application Bootstrap¶
Application Initialization¶
// app.run.js
StratpointTSApp.run([
'$rootScope',
'$location',
'$route',
'AuthService',
'PermissionService',
'ConfigService',
function($rootScope, $location, $route, AuthService, PermissionService, ConfigService) {
// Initialize application
$rootScope.appName = 'Stratpoint Timesheet';
$rootScope.appVersion = '2.1.0';
$rootScope.currentUser = null;
$rootScope.permissions = [];
// Load application configuration
ConfigService.loadConfig().then(function(config) {
$rootScope.appConfig = config;
});
// Route change start handler
$rootScope.$on('$routeChangeStart', function(event, next, current) {
// Check authentication
if (!AuthService.isAuthenticated() && next.templateUrl !== 'app/partials/auth/login.html') {
event.preventDefault();
$location.path('/login');
return;
}
// Check permissions
if (next.permission && !PermissionService.hasPermission(next.permission)) {
event.preventDefault();
$location.path('/unauthorized');
return;
}
// Show loading indicator
$rootScope.isLoading = true;
});
// Route change success handler
$rootScope.$on('$routeChangeSuccess', function(event, current, previous) {
// Update page title
$rootScope.pageTitle = current.title || 'Timesheet';
document.title = $rootScope.pageTitle + ' - ' + $rootScope.appName;
// Hide loading indicator
$rootScope.isLoading = false;
// Track page view
if (window.gtag) {
gtag('config', 'GA_TRACKING_ID', {
page_title: $rootScope.pageTitle,
page_location: $location.absUrl()
});
}
});
// Route change error handler
$rootScope.$on('$routeChangeError', function(event, current, previous, rejection) {
console.error('Route change error:', rejection);
$rootScope.isLoading = false;
$location.path('/not-found');
});
// Global error handler
$rootScope.$on('$error', function(event, error) {
console.error('Application error:', error);
// Send error to monitoring service
if (window.Sentry) {
Sentry.captureException(error);
}
});
// Initialize user session
if (AuthService.isAuthenticated()) {
AuthService.getCurrentUser().then(function(user) {
$rootScope.currentUser = user;
$rootScope.permissions = PermissionService.getUserPermissions(user);
});
}
}
]);
Module Organization Patterns¶
Controller Module Structure¶
// controllers/timesheet/timesheet.module.js
angular.module('StratpointTSControllers.Timesheet', [])
.controller('TimelogController', TimelogController)
.controller('TimelogFormController', TimelogFormController)
.controller('TimelogApprovalController', TimelogApprovalController);
// Register with main controllers module
angular.module('StratpointTSControllers', [
'StratpointTSControllers.Auth',
'StratpointTSControllers.Dashboard',
'StratpointTSControllers.Timesheet',
'StratpointTSControllers.Project',
'StratpointTSControllers.User',
'StratpointTSControllers.Report'
]);
Service Module Structure¶
// services/data/data.module.js
angular.module('StratpointTSServices.Data', [])
.factory('ApiService', ApiService)
.factory('CacheService', CacheService)
.factory('SyncService', SyncService);
// Register with main services module
angular.module('StratpointTSServices', [
'StratpointTSServices.Auth',
'StratpointTSServices.Data',
'StratpointTSServices.Utility',
'StratpointTSServices.Integration'
]);
Directive Module Structure¶
// directives/common/common.module.js
angular.module('StratpointTSDirectives.Common', [])
.directive('tsLoading', LoadingDirective)
.directive('tsPermission', PermissionDirective)
.directive('tsDatePicker', DatePickerDirective);
// Register with main directives module
angular.module('StratpointTSDirectives', [
'StratpointTSDirectives.Common',
'StratpointTSDirectives.Forms',
'StratpointTSDirectives.UI'
]);
Build and Deployment Structure¶
Gulp Build Configuration¶
// gulpfile.js
var gulp = require('gulp');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var ngAnnotate = require('gulp-ng-annotate');
// Application JavaScript files in dependency order
var appFiles = [
'public/app/js/mine/app.module.js',
'public/app/js/mine/app.config.js',
'public/app/js/mine/app.routes.js',
'public/app/js/mine/app.run.js',
'public/app/js/mine/controllers/**/*.js',
'public/app/js/mine/services/**/*.js',
'public/app/js/mine/directives/**/*.js',
'public/app/js/mine/filters/**/*.js',
'public/app/js/mine/factories/**/*.js'
];
// Build task
gulp.task('build-js', function() {
return gulp.src(appFiles)
.pipe(concat('app.min.js'))
.pipe(ngAnnotate())
.pipe(uglify())
.pipe(gulp.dest('public/dist/js/'));
});
Development vs Production Structure¶
Development Mode: - Individual file loading for debugging - Source maps enabled - Debug information available - Hot reloading support
Production Mode: - Concatenated and minified files - Template caching - Debug information disabled - Optimized for performance
This AngularJS structure provides a scalable, maintainable foundation for the Stratpoint Timesheet Application frontend, ensuring clear separation of concerns and efficient development workflows.