Overview
UpdatedEasyLaunchpad is a comprehensive .NET Core boilerplate designed to accelerate your SaaS application development. It provides a solid foundation with pre-built features, modern architecture, and clean code practices.
Key Features
- Complete user authentication and authorization system
- Modular architecture, easy to extend and customize
- Subscription management with Stripe and Paddle integration
- Responsive admin dashboard with Tailwind CSS and DaisyUI components
- Email templating and notification system
- Background job processing with Hangfire
- System logs by Serilog
- Centralized application settings to manage all app areas
Architecture Overview
EasyLaunchpad follows a clean, modular architecture that separates concerns and promotes maintainability:
Backend Architecture
- Clean Architecture pattern
- Domain-driven design principles
- Repository pattern for data access
- Entity Framework Core ORM
- Scheduled jobs with Hangfire & System logs
Frontend Architecture
- Razor Pages for server-rendered UI
- Tailwind CSS for styling
- DaisyUI component library
- Responsive design principles
- Clean separation of view components and partials
Technology Stack
Core Features
- Automatic database creation as per the server provider
- Custom branding options
- Social media integration
- Email configuration
- Payment gateway support
Security Features
- Google OAuth integration
- reCAPTCHA protection
- Email confirmation
- Secure payment processing
- Admin-only configuration
EasyLaunchpad Quick Setup
5 minsEasyLaunchpad is a comprehensive, production-ready solution designed to get your business up and running in minimal time. This complete platform comes with all essential features pre-integrated, requiring only simple configuration to launch your branded service.
With your valid license, you can deploy a fully functional SAAS platform by following our streamlined setup process. The intuitive configuration workflow allows you to:
- Establish core branding (app name, logo, color scheme)
- Configure essential business operations (payments, email, authentication)
- Enable advanced features as needed (social integration, security enhancements)
Setup Organization
The setup is organized into mandatory and optional sections:
Essential configurations
(required for basic operation)
- Server (SQL, MySQL or PostgreSQL)
- Branding (app name, logo etc)
- Email account for messages and updates
- Stripe/Paddle configuration to accept app purchases
Premium features
(activate as your business needs evolve)
- Google OAuth support
- Captcha
- Email confirmation
Most features work immediately after configuration - no additional development required. Our sensible defaults minimize setup time while maintaining flexibility for customization.
Implementation Tip
For optimal results, we recommend completing all basic configurations before launching, then revisiting optional features as your user base grows. The entire setup can typically be completed in under 10 minutes for basic deployments.
Further Reading
For comprehensive technical specifications and advanced configuration options, please refer to the dedicated module documentation. Each feature section contains detailed implementation guides, best practices, and troubleshooting resources to support your deployment.
Follow these steps to quickly configure and customize your application within few minutes.
1. Database Configuration
Connection String Setup
Locate appsettings.json
Find the configuration file in your project root
Update Connection String for choosen provider
Modify the Provider and ConnectionStrings with your database details
Run Application
The database will be created automatically on first run
"DatabaseSettings": {
"Provider": "SqlServer", // Options: "SqlServer", "MySQL", "PostgreSQL"
"ConnectionStrings": {
"SqlServer": "Server=;Database=easylaunchpad_db;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True;",
"MySQL": "Server=;Port=;Database=easylaunchpad_db;Uid=;Pwd=;charset=utf8mb4;",
"PostgreSQL": "Host=;Port=;Database=easylaunchpad_db;Username=;Password=;Pooling=true;"
}
}
2. Branding Configuration
Customize Application Appearance
| Setting | Description | Location |
|---|---|---|
| Application Name | The name displayed throughout the app | Admin → System Settings → Branding |
| Favicon | Browser tab icon (32x32px, .ico) | Admin → System Settings → Branding |
| Application Logo | Main logo displayed in header (150x50px, .png) | Admin → System Settings → Branding |
3. Footer Configuration
Social Links & Copyright
Navigate to Footer Settings
Go to Admin → System Settings → Social & Footer
Add Social Media Links
Enter URLs for Facebook, Twitter, LinkedIn
Set Copyright Notice
Add your copyright text (e.g., "© 2023 Your Company")
Save Changes
Click Save to apply
4. Email Configuration
Email Account Setup
Navigate to Email System
Go to Admin → Email System → Email Accounts Add Account
Add new Email Account and Emails will be functional and working after the successful configuration - nothing else is required.
Next, Test the newly added email account, Open Emial Account list, and click on 3 dots menu in the grid, click on Test Email
5. Payment Gateway Setup
Stripe/Paddle Integration
Creating Subscription Packages
Navigate to Packages
Go to Admin → Payment System → Packages
Add New Package
Click "Add New Package" button
Configure Package
Set name, price, features, and duration
Save Package
Package will be available for purchase immediately
6. Google OAuth Setup
Google Authentication
Before You Begin
You'll need to create OAuth credentials in the Google Cloud Console.
Navigate to Authentication Settings
Go to Admin → System Settings → Authentication
7. reCAPTCHA Setup
Google reCAPTCHA
Navigate to Authentication Settings
Go to Admin → System Settings → Security
Note
Get your keys from the reCAPTCHA admin console.
8. Email Confirmation
Navigate to Authentication Settings
Go to Admin → System Settings → Authentication
Require Email Confirmation
When enabled, new users will receive a confirmation email with a verification link before they can log in.
Important
Ensure your email configuration is working properly before enabling this feature.
9. Theme Customization
Customize UI Elements for your Business/SaaS
Navigate to Landing Page
Open Views/Home/Index.cshtml
Locate Partial Views
Find partial views in Views/Shared/ or Views/Home/Partials/
Modify Components
Edit these files to customize different sections:
_Header.cshtml- Navigation and header_Hero.cshtml- Main banner section_Features.cshtml- Features listing_Footer.cshtml- Footer content
CSS Customization
Modify CSHTML files directly with TailwindCSS utility classes according to your need, clean and rebuild soltution and site.csss and admin.css files will be updated automaticaly.
💡 Pro Tip
Want to match the design with your brand? Need more flexibility?
Explore our pre-built variants for the Hero section, Features, Navigation, CTA, and Footer inside the UI documentation.
🎨 Mix & match components and apply your own branding — no need to start from scratch.
Installation Guide
UpdatedGet started with EasyLaunchpad in just a few minutes. Follow these steps to set up your development environment and run the application locally.
Prerequisites
Make sure you have the following installed before proceeding:
- .NET 8.0 SDK
- Node.js (v22+ recommended) – for Tailwind CSS, frontend assets, and package management
- Visual Studio 2022+ or VS Code with C# extension
-
At least one supported database engine:
- SQL Server 2019+ (Express edition supported)
- MySQL 8.0+
- PostgreSQL 15+
- Git for version control
- IIS (Internet Information Services)
Step 1: Download the source code
Make sure you have proper license to use the code
Step 2: Configure Database Connection
Update the database provider and connection strings in
appsettings.json.
You can switch between SQL Server, MySQL, or PostgreSQL by changing the
Provider value.
{
"DatabaseSettings": {
"Provider": "SqlServer", // Options: "SqlServer", "MySQL", "PostgreSQL"
"ConnectionStrings": {
"SqlServer": "Server=localhost;Database=easylaunchpad_db;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True;",
"MySQL": "Server=localhost;Port=3306;Database=easylaunchpad_db;Uid=root;Pwd=yourpassword;charset=utf8mb4;",
"PostgreSQL": "Host=localhost;Port=5432;Database=easylaunchpad_db;Username=postgres;Password=yourpassword;Pooling=true;"
}
}
}
Example: To use MySQL, set "Provider": "MySQL"
and update the MySQL connection string.
Security Warning
Never commit sensitive connection strings to source control. Use environment variables or user secrets for production environments.
Step 3: Restore NuGet Packages
dotnet restore
This will download all required NuGet packages for the project
Step 4: Restoring Node.js Packages
npm install
It is recommended to have the Node v22+ installed for Tailwind CSS, frontend assets, and package management.
Open terminal in your Easylaunchpad.Website root
Step 5: Build the Application
dotnet build
Compiles the application and checks for build errors
Step 6: Run the Application
dotnet run
The application will automatically:
- Apply database migrations
- Start the web server
Success!
You've successfully installed EasyLaunchpad. Default admin credentials are:
- Email: admin@easylaunchpad.com
- Password: Admin@123
Folder Structure
Understanding the project structure is essential for efficient development. EasyLaunchpad adopts a clean, modular architecture that promotes separation of concerns and scalability.
EasyLaunchpad/
├── DataAccess/ # Handles database context and EF Core migrations
│ ├── Context/ # Provider-specific DbContexts
│ │ ├── SqlServerApplicationDbContext.cs
│ │ ├── MySqlApplicationDbContext.cs
│ │ └── PostgreSqlApplicationDbContext.cs
│ │
│ ├── Factories/ # DbContext factories for providers
│ │ ├── SqlServerDbContextFactory.cs
│ │ ├── MySqlDbContextFactory.cs
│ │ └── PostgreSqlDbContextFactory.cs
│ │
│ ├── Migrations/ # Provider-specific migrations
│ │ ├── SqlServer/ # Migrations for SQL Server
│ │ ├── MySQL/ # Migrations for MySQL
│ │ └── PostgreSQL/ # Migrations for PostgreSQL
│ │
│ └── ApplicationDbContext.cs # Base application DbContext (shared config, conventions)
│
├── Infrastructure/ # Infrastructure-level implementations
│ ├── Extensions/ # Core utility and extension methods
│ ├── Filters/ # Custom action and authorization filters
│ ├── Services/ # Service interfaces and implementations
│ └── Utilities/ # Middleware, constants, and helper classes
│
├── Models/ # Domain models and shared DTOs
│ ├── Db/ # Entity framework domain entities
│ ├── Dto/ # Data Transfer Objects
│ ├── VM/ # View Models for UI binding
│ └── Utilities/ # Enums, constants, and shared types
│
└── Website/ # Main web application (MVC + Razor)
├── Areas/ # Modular feature areas (e.g., Admin)
├── Controllers/ # API and MVC controllers
├── Models/ # View-specific model classes
├── Core/ # Core configurations (e.g., AutoMapper, jobs)
├── Views/ # Razor views and layout templates
├── wwwroot/ # Static files (CSS, JS, images)
└── Program.cs # Application entry point
Configuration
EasyLaunchpad uses a configuration system that allows you to customize various aspects of the application without modifying code.
Application Settings
The main configuration file is appsettings.json. Here are the key sections:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"DatabaseSettings": {
"Provider": "SqlServer", // Options: "SqlServer", "MySQL", "PostgreSQL"
"ConnectionStrings": {
"SqlServer": "Server=;Database=easylaunchpad_db;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True;",
"MySQL": "Server=;Port=;Database=easylaunchpad_db;Uid=;Pwd=;charset=utf8mb4;",
"PostgreSQL": "Host=;Port=;Database=easylaunchpad_db;Username=;Password=;Pooling=true;"
}
}
}
Environment-Specific Configuration
For different environments, use environment-specific settings files:
appsettings.Development.json- Development environmentappsettings.Staging.json- Staging environmentappsettings.Production.json- Production environment
Security Best Practice
Store sensitive configuration values like API keys and connection strings in environment variables or user secrets. Never commit these values to source control.
Authentication & Authorization
EasyLaunchpad provides a complete authentication and authorization system built on ASP.NET Core Identity.
Authentication Options
The following authentication methods are supported out of the box:
Local Authentication
- Username/password authentication
- Email confirmation
- Password reset functionality
External Providers
- Google authentication
Configuring External Providers
To enable external authentication providers, enable the open authentication from admin > settings > authentication settings:
Role-Based Authorization
EasyLaunchpad includes a role-based authorization system with predefined roles:
| Role | Description | Permissions |
|---|---|---|
| Admin | System-wide administrator | Full access to all features |
| Registered | Standard user | Basic application access |
Using Authorization in Controllers
Apply authorization attributes to controllers or actions:
[Authorize(Roles = "Admin,Registered")]
public class UserManagementController : Controller
{
[HttpGet]
public IActionResult Index()
{
// Only TenantAdmin and Manager can access this
return View();
}
[Authorize(Roles = "Admin")]
[HttpPost]
public async Task DeleteUser(string userId)
{
// Only Admin can delete users
// Implementation...
return RedirectToAction("Index");
}
}
Multi-Database Provider Support
AdvancedSeamless support for SQL Server, MySQL, and PostgreSQL, with an extensible architecture for adding additional providers like Oracle, SQLite, and beyond. Switch between providers with simple configuration changes while maintaining a single codebase.
System Overview
The multi-database provider system isolates database-specific logic while keeping business code provider-agnostic. Applications can switch providers or add new ones with minimal changes. Each provider has its own DbContext, migrations, and connection strings, ensuring complete isolation and type safety.
Supported Providers
- SQL Server (Default)
- MySQL (Pomelo)
- PostgreSQL (Npgsql)
Architecture
- Base ApplicationDbContext
- Provider-specific DbContexts
- Design-time factories
- Isolated migration folders
Integrated Services
- Hangfire background jobs
- Serilog logging
- Identity authentication
- Automatic migrations
Project Structure
DataAccess/
├── Context/
│ ├── ApplicationDbContext.cs # Base context with shared configurations
│ ├── MySqlApplicationDbContext.cs # MySQL-specific configurations
│ ├── PostgreSQLApplicationDbContext.cs # PostgreSQL-specific configurations
│ └── SqlServerApplicationDbContext.cs # SQL Server (uses defaults)
├── Factories/
│ ├── MySqlDbContextFactory.cs # Design-time factory for MySQL
│ ├── PostgresDbContextFactory.cs # Design-time factory for PostgreSQL
│ └── SqlServerDbContextFactory.cs # Design-time factory for SQL Server
└── Migrations/
├── MySQL/ # MySQL-specific migrations
├── PostgreSQL/ # PostgreSQL-specific migrations
└── SqlServer/ # SQL Server-specific migrations
Configuration Setup
1. Configure appsettings.json
Add database settings with your chosen provider and connection strings for all supported databases:
{
"DatabaseSettings": {
"Provider": "SqlServer", // Options: SqlServer, MySQL, PostgreSQL
"ConnectionStrings": {
"SqlServer": "Server=(localdb)\\mssqllocaldb;Database=YourDb;Trusted_Connection=True;MultipleActiveResultSets=true",
"MySQL": "Server=localhost;Port=3306;Database=YourDb;Uid=root;Pwd=yourpassword;",
"PostgreSQL": "Host=localhost;Port=5432;Database=YourDb;Username=postgres;Password=yourpassword;"
}
}
}
2. Install Required NuGet Packages
Install packages based on your chosen provider:
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Hangfire.SqlServer
dotnet add package Serilog.Sinks.MSSqlServer
Provider-Specific Configurations
SQL Server
Uses default EF Core conventions with no special configuration needed.
Features:
- Native datetime types
- Full EF Core feature support
- Optimal for Windows
- No string length restrictions
MySQL
Requires explicit configuration for string keys and indexes.
Key Considerations:
- 255 char limit on key fields
- LONGTEXT for regular strings
- DATETIME(6) for precision
- TINYINT(1) for booleans
PostgreSQL
Normalizes identifiers and enforces UTC timezone handling.
Features:
- Lowercase table/column names
- Automatic UTC conversion
- TEXT type for strings
- timestamp with time zone
Working with Migrations
Each provider maintains separate migrations in isolated folders. This prevents conflicts and allows provider-specific optimizations.
Creating Migrations
SQL Server
dotnet ef migrations add MigrationName \
--context SqlServerApplicationDbContext \
--output-dir Migrations/SqlServer
MySQL
dotnet ef migrations add MigrationName \
--context MySqlApplicationDbContext \
--output-dir Migrations/MySQL
PostgreSQL
dotnet ef migrations add MigrationName \
--context PostgreSQLApplicationDbContext \
--output-dir Migrations/PostgreSQL
Applying Migrations
The application automatically applies migrations on startup through the EnsureDatabaseAsync() method. You can also manually apply them:
SQL Server:
dotnet ef database update --context SqlServerApplicationDbContext
MySQL:
dotnet ef database update --context MySqlApplicationDbContext
PostgreSQL:
dotnet ef database update --context PostgreSQLApplicationDbContext
EnsureDatabaseAsync()
Switching Database Providers
Switching between database providers is straightforward and requires minimal configuration changes.
-
1
Update Configuration
Change the Provider value in appsettings.json:
{ "DatabaseSettings": { "Provider": "MySQL" // Change from SqlServer to MySQL } } -
2
Verify Connection String
Ensure the connection string for your chosen provider is configured correctly in appsettings.json
-
3
Ensure Required Packages
Verify that the NuGet packages for your chosen provider are installed
-
4
Restart Application
The Program.cs automatically configures everything based on your provider choice. Migrations apply automatically on startup.
Core Services
IDatabaseConfigurationService
Manages database provider configuration and connection strings.
public interface IDatabaseConfigurationService
{
DatabaseProvider GetDatabaseProvider();
string GetConnectionString();
void ConfigureDbContext(
DbContextOptionsBuilder options,
string connectionString,
DatabaseProvider provider);
}
Usage Example:
var dbService = serviceProvider
.GetRequiredService<IDatabaseConfigurationService>();
var provider = dbService.GetDatabaseProvider();
var connectionString = dbService.GetConnectionString();
IMigrationService
Handles database creation and migration application.
public interface IMigrationService
{
Task EnsureDatabaseAsync();
}
Usage Example:
var migrationService = serviceProvider
.GetRequiredService<IMigrationService>();
await migrationService.EnsureDatabaseAsync();
Extending to New Database Providers
Want to add support for Oracle, SQLite, or another database? Follow this comprehensive step-by-step guide:
Add Provider to Enum
Update the DatabaseProvider enum to include your new provider:
public enum DatabaseProvider
{
SqlServer,
MySQL,
PostgreSQL,
Oracle // Your new provider
}
Create Provider-Specific Context
Create a new context class that inherits from ApplicationDbContext:
public class OracleApplicationDbContext : ApplicationDbContext
{
public OracleApplicationDbContext(
DbContextOptions<OracleApplicationDbContext> options,
IConfiguration configuration)
: base(options, configuration)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Add Oracle-specific configurations here
// Example: Configure sequences, NVARCHAR2 types, etc.
foreach (var entityType in builder.Model.GetEntityTypes())
{
foreach (var property in entityType.GetProperties())
{
// Oracle-specific type mappings
if (property.ClrType == typeof(string)
&& property.GetMaxLength() == null)
{
property.SetColumnType("NVARCHAR2(2000)");
}
}
}
}
}
Create Design-Time Factory
Implement IDesignTimeDbContextFactory for EF Core tooling:
public class OracleDbContextFactory
: IDesignTimeDbContextFactory<OracleApplicationDbContext>
{
public OracleApplicationDbContext CreateDbContext(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddJsonFile("appsettings.Development.json", optional: true)
.Build();
var connectionString = configuration
.GetSection("DatabaseSettings:ConnectionStrings:Oracle").Value;
if (string.IsNullOrEmpty(connectionString))
throw new InvalidOperationException(
"Oracle connection string not found in configuration.");
var optionsBuilder = new DbContextOptionsBuilder<OracleApplicationDbContext>();
optionsBuilder.UseOracle(connectionString, oracleOptions =>
{
oracleOptions.EnableRetryOnFailure(
maxRetryCount: 3,
maxRetryDelay: TimeSpan.FromSeconds(5),
errorCodesToAdd: null);
});
return new OracleApplicationDbContext(optionsBuilder.Options, configuration);
}
}
Update DatabaseConfigurationService
Add your provider case to the ConfigureDbContext method:
public void ConfigureDbContext(
DbContextOptionsBuilder options,
string connectionString,
DatabaseProvider provider)
{
switch (provider)
{
// ... existing cases ...
case DatabaseProvider.Oracle:
options.UseOracle(connectionString, oracleOptions =>
{
oracleOptions.EnableRetryOnFailure(
maxRetryCount: 3,
maxRetryDelay: TimeSpan.FromSeconds(5),
errorCodesToAdd: null);
});
break;
default:
throw new NotSupportedException(
$"Database provider {provider} is not supported");
}
}
Update DatabaseExtensions
Add registration in AddEasyLaunchpadDatabase method:
case DatabaseProvider.Oracle:
services.AddDbContext<ApplicationDbContext, OracleApplicationDbContext>(
(serviceProvider, options) =>
{
var dbConfigService = serviceProvider
.GetRequiredService<IDatabaseConfigurationService>();
var connectionString = dbConfigService.GetConnectionString();
dbConfigService.ConfigureDbContext(
options, connectionString, DatabaseProvider.Oracle);
if (environment.IsDevelopment())
{
options.EnableSensitiveDataLogging();
options.LogTo(Console.WriteLine, LogLevel.Information);
}
});
break;
Update Program.cs for Hangfire
Add Hangfire storage configuration for your provider:
case DatabaseProvider.Oracle:
config.UseOracleStorage(connectionString, new OracleStorageOptions
{
QueuePollInterval = TimeSpan.FromSeconds(15),
SchemaName = "HANGFIRE"
});
break;
Update Program.cs for Serilog
Add Serilog sink configuration for your provider:
case DatabaseProvider.Oracle:
loggerConfig.WriteTo.Logger(lc => lc
.Filter.ByIncludingOnly(e =>
e.Level == LogEventLevel.Warning ||
e.Level == LogEventLevel.Error ||
(e.Level == LogEventLevel.Information &&
e.Properties.ContainsKey("SourceContext") &&
e.Properties["SourceContext"].ToString().StartsWith("\"Easylaunchpad"))
)
.WriteTo.Oracle(
connectionString: connString,
tableName: appIdentityService.LogTableName,
autoCreateSqlTable: true));
break;
Create Migration Directory
Create a new folder for provider-specific migrations:
DataAccess/
└── Migrations/
└── Oracle/ <-- Create this folder
Update Configuration
Add connection string to appsettings.json:
{
"DatabaseSettings": {
"Provider": "Oracle",
"ConnectionStrings": {
"Oracle": "Data Source=localhost:1521/ORCL;User Id=system;Password=oracle;"
}
}
}
Best Practices
Follow these guidelines to ensure smooth operation across all database providers and maintain code quality.
Connection Strings
- • Development: Store in appsettings.Development.json
- • Production: Use environment variables or Azure Key Vault
- • Security: Never commit credentials to source control
- • Validation: Test connections before deployment
Provider-Specific Migrations
Never mix migrations from different providers
- ✓ Use separate folders for each provider
- ✓ Never manually modify generated migrations
- ✓ Test on dev database first
- ✓ Keep migration history in source control
DateTime Handling
Consistent datetime handling across providers:
- SQL: Works with local and UTC times
- MY: Stores as-is; manage UTC in code
- PG: Auto converts to/from UTC
entity.CreatedAt = DateTime.UtcNow;
String Length Limits
Always specify max length for indexed string properties:
builder.Entity<YourEntity>()
.Property(e => e.Email)
.HasMaxLength(255);
Prevents "key too long" errors in MySQL
Testing Migrations
Before deploying, test on all providers:
# Test each provider
dotnet ef database update \
--context SqlServerApplicationDbContext
dotnet ef database update \
--context MySqlApplicationDbContext
dotnet ef database update \
--context PostgreSQLApplicationDbContext
Performance
Connection pooling enabled by default:
- SQL: Min Pool Size=5; Max=100
- MY: MinimumPoolSize=5; Max=100
- PG: Minimum Pool Size=5; Max=100
Pro Tips
💡 Entity Configuration: Configure shared logic in the base ApplicationDbContext, override in provider-specific contexts for optimizations
💡 LINQ Queries: Some LINQ queries may not translate identically across providers - always test queries when switching databases
💡 Centralize Enums: Maintain DatabaseProvider enum and update DI in one place for clean, extensible code
💡 Integration Tests: Run comprehensive tests on all target providers before production deployment
Troubleshooting
Solution:
- Ensure you're in the correct project directory
- Verify the migration folder exists
- Check that you're using the correct context name
cd /path/to/DataAccess
dotnet ef migrations list --context MySqlApplicationDbContext
Solution:
- Verify database server is running
- Check firewall settings allow connections
- Validate connection string format
- Test connection with database management tool (SSMS, MySQL Workbench, pgAdmin)
Solution:
Drop the database or roll back migrations, then reapply:
# Roll back all migrations
dotnet ef database update 0 --context YourDbContext
# Then reapply
dotnet ef database update --context YourDbContext
Solution:
- Ensure correct NuGet packages are installed for your provider
- Check package versions are compatible with your EF Core version
- Restore NuGet packages:
dotnet restore
Solution:
The MySqlApplicationDbContext automatically applies 255 character limits to key fields. If you still encounter this error:
- Check for custom string properties that are indexed
- Manually set
HasMaxLength(255)on these properties - Ensure the ConfigureKeyStringProperties method is being called
Solution:
Disable this MySQL setting or ensure Hangfire tables have primary keys:
SET GLOBAL sql_require_primary_key = 0;
This allows Hangfire to auto-create its schema tables properly.
Solution:
PostgreSQL context automatically converts all identifiers to lowercase. If you encounter issues:
- Use lowercase in all raw SQL queries
- Double-check table and column names in your queries
- Avoid mixing case in entity property names
Solution:
- Some LINQ queries may not translate identically across providers
- Test queries individually when switching providers
- Avoid provider-specific SQL functions in LINQ queries
- Use EF Core's built-in functions that work across all providers
Summary
What You Get with Multi-Database Support
Key Features
- ✓ Flexibility: Switch providers via simple configuration
- ✓ Extensibility: Clear pattern for adding new providers
- ✓ Separation: Provider logic isolated from business code
- ✓ Type Safety: Compile-time checking for each provider
- ✓ Automation: Migrations apply automatically on startup
Integrated Services
- ► Hangfire: Background jobs configured per provider
- ► Serilog: Database logging for each provider
- ► Identity: ASP.NET Identity fully integrated
- ► Migrations: Automatic detection and application
- ► Retry Logic: Built-in transient failure handling
Single Codebase, Multiple Databases
Your application adapts to different deployment environments while maintaining a unified business logic layer. Whether you're deploying to Azure SQL, AWS RDS MySQL, or Google Cloud PostgreSQL, the same code works seamlessly.
Admin Dashboard
UpdatedThe Admin Dashboard provides real-time monitoring of your application's key metrics and operations.
Dashboard Features
- Quick status overview of all system components
- Visual indicators for critical issues
- Historical data tracking and trends
System Requirements
- Modern web browser (Chrome, Firefox, Edge, Safari)
- Admin privileges to access the dashboard
- ASP.NET Core 8.0 runtime
Dashboard Widgets
Sync
All widgets showing quick data overview. Click any widget for more detailed information.
Users & Roles Widget
Description
Displays statistics about user accounts and their roles in the system. Shows total users, active users.
Technical Details
- Endpoint:
/dashboard/user-role-stats - Data Source: User service
Email Notification Widget
Description
Tracks email notification status including queued emails, emails sent today, failed deliveries, and scheduled emails with color-coded severity indicators.
Technical Details
- Endpoint:
/dashboard/email-notification - Data Source: QueuedEmail database table
-
Metrics:
- Queued: Emails waiting to be sent
- Sent Today: Successful deliveries today
- Failed: Emails with >3 failed attempts
Cron Jobs Widget
Description
Monitors background jobs and scheduled tasks, showing currently running jobs, recurring jobs, and success/failure rates.
Technical Details
- Endpoint:
/dashboard/cron-job - Data Source: ScheduledJob database table
-
Metrics:
- Recurring Jobs: Total scheduled jobs
- Running Now: Currently executing jobs
- Success/Fail: Last 24 hours stats
Logs Widget
Description
Provides insight into system logging activity, showing total logs today broken down by severity level (info, warning, error) with trend analysis.
Technical Details
- Endpoint:
/dashboard/logs - Data Source: Logs database table
-
Metrics:
- Total Logs: Today's log entries
- Info: Informational messages
- Warning: Potential issues
- Error: Critical errors
- Logs/Hour: Average logging rate
Sitemap Widget
Description
Tracks the status of your website's sitemap, showing total pages, indexed pages, and hidden pages with last update timestamp.
Technical Details
- Endpoint:
/dashboard/sitemap - Data Source: SitemapEntry database table
-
Metrics:
- Total Entries: All sitemap entries
- Indexed: Pages marked for search engines
- Hidden: Pages with noindex
- Last Update: Most recent change
Payment & Subscriptions Widget
Description
Provides financial metrics including today's payments, active subscriptions, failed payments, and upcoming renewals with revenue breakdowns.
Technical Details
- Endpoint:
/dashboard/payments-subscription - Data Source: Subscription database table
-
Metrics:
- Payments Today: Total revenue today
- Active Subs: Currently active subscriptions
- Failed Payments: Today's failures
- Upcoming Renewals: Next 7 days
- MRR (Monthly Recurring Revenue): Current monthly revenue
Extending the Dashboard
Important
These extensions require developer access and knowledge of ASP.NET Core and Razor views.
Adding New Widgets
To add a new widget to the dashboard:
- Create a new partial view in
Views/Sharedfolder - Add the widget to the dashboard grid in
Views/Dashboard/Index.cshtml - Create a new endpoint in
DashboardController - Implement the service method in
DashboardService - Create DTO classes for the response data
Customizing Widgets
Existing widgets can be customized by:
- Modifying the partial view HTML/CSS
- Adding new metrics to service methods
- Extending DTO classes with additional properties
- Changing visualization types (charts, gauges, etc.)
Example Widget Implementation
// 1. Add Controller Endpoint
[HttpGet("new-widget")]
public async Task<IActionResult> GetNewWidgetData()
{
var data = await _dashboardService.GetNewWidgetDataAsync();
return Ok(data);
}
// 2. Add Service Method
public async Task<NewWidgetDto> GetNewWidgetDataAsync()
{
// Fetch data from database or external API
var metrics = await _repository.GetWidgetMetricsAsync();
return new NewWidgetDto
{
TotalItems = metrics.Count,
SuccessRate = metrics.SuccessPercentage,
LastUpdated = DateTime.UtcNow
};
}
// 3. Create DTO
public class NewWidgetDto
{
public int TotalItems { get; set; }
public double SuccessRate { get; set; }
public DateTime LastUpdated { get; set; }
}
// 4. Add partial view to dashboard
<partial name="_NewWidget" model="Model.NewWidgetData" />
Widget Customization Example
public class EmailNotificationDto
{
public int Queued { get; set; }
public int SentToday { get; set; }
public int Failed { get; set; }
// Added properties
public double DeliveryRate { get; set; }
public TimeSpan AvgProcessingTime { get; set; }
public Dictionary<string, int> ByProvider { get; set; }
[JsonIgnore]
public bool IsHealthy => DeliveryRate > 0.95;
}
User & Roles Management
UpdatedManage users and roles, control access, and define responsibilities across your application.
User Management Features
- Create, update, and delete user accounts
- Assign multiple roles to users
- Activate/deactivate accounts
- Advanced search & filtering
- Pagination support
- Profile management
Role Management Features
- Create, update, and delete roles
- Search & filter capabilities
- Pagination support
- Default role protection
User Management
Important
All user management endpoints require admin privileges.
User Statuses
| Status | Value | Description |
|---|---|---|
| Active | 1 | User can log in and access the system |
| Inactive | 2 | User account deactivated/inactive and cannot login and access the system |
| Pending | 3 | User account created but not yet activated |
| Deleted | 4 | Soft-deleted user (retained for auditing) |
User's Controller Endpoints
| Endpoint | Method | Description | Parameters |
|---|---|---|---|
/user |
GET | Main user management page | None |
/user/list |
POST | Get paginated user list | start, draw, role, status, search, length |
/user/add |
GET | Get add user form | None |
/user/add |
POST | Create new user | UserVM model |
/user/update/{id} |
GET | Get edit user form | User ID |
/user/update/{id} |
PUT | Update user | User ID, UserVM model |
/user/delete/{userId} |
DELETE | Delete user | User ID |
User Data Models
UserVM (View Model)
public class UserVM {
public string Email { get; set; }
public string Password { get; set; }
public string FullName { get; set; }
public string PhoneNumber { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Country { get; set; }
public string ZipCode { get; set; }
public IFormFile ProfilePicture { get; set; }
public string[] Role { get; set; }
public bool IsActive { get; set; }
}
UserUpdateDto
public class UserUpdateDto {
public string Id { get; set; }
public string Email { get; set; }
public string FullName { get; set; }
public string PhoneNumber { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Country { get; set; }
public string ZipCode { get; set; }
public string ProfilePicture { get; set; }
public string[] RoleIds { get; set; }
public SelectListItem[] RolesList { get; set; }
public bool IsActive { get; set; }
}
Role Management
Security Note
Role modifications can affect system security. Changes are logged and require admin privileges.
Role's Controller Endpoints
| Endpoint | Method | Description | Parameters |
|---|---|---|---|
/role |
GET | Main role management page | None |
/role/list |
POST | Get paginated role list | start, draw, search, length |
/role/add |
GET | Get add role form | None |
/role/add |
POST | Create new role | RoleVM model |
/role/update/{id} |
GET | Get edit role form | Role ID |
/role/update/{roleId} |
PUT | Update role | Role ID, RoleVM model |
Role Data Models
RoleVM (View Model)
public class RoleVM {
public string Name { get; set; }
}
Default System Roles
| Role | Description | Permissions |
|---|---|---|
| Admin | Application administrator | Full access within the application |
| Registered | Standard user | Basic application access |
Extending Functionality
Developer Note
These extensions require developer access and knowledge of ASP.NET Core and Razor views.
Adding New User Fields
To add new fields to the user model:
- Add the property to
ApplicationUserentity - Update the
UserVMandUserUpdateDtomodels - Modify the
AddandUpdatemethods in UserService - Update the relevant views (add/edit forms)
- Add any necessary validation rules
Customizing User Search
To add new search criteria to the user list:
// In UserService.GetAllUsersAsync()
if (!string.IsNullOrEmpty(searchPage.NewCriteria))
{
query = query.Where(u => u.NewProperty.Contains(searchPage.NewCriteria));
}
Implementing User Import
To add bulk user import functionality:
// In UserController
[HttpPost("import")]
public async Task ImportUsers(IFormFile file)
{
if (file == null || file.Length == 0)
return BadRequest("No file uploaded");
using var stream = file.OpenReadStream();
var result = await _userService.ImportUsersAsync(stream);
if (result.Success)
return Ok(result);
return BadRequest(result);
}
Security Considerations
- Always validate and sanitize user input when managing users and roles
- Implement proper audit logging for all user and role modifications
- Consider implementing rate limiting on user management endpoints
- Protect sensitive operations with additional authentication factors
- Regularly review role assignments
Payment & Subscription
UpdatedComprehensive documentation for managing payment gateways, packages, and subscriptions
These modules allow administrators to configure payment gateways, create subscription packages, and manage customer subscriptions.
Payment Gateways
- Stripe and Paddle integration
- Secure credential storage
- Default gateway selection
- No-code configuration
Packages
- Multiple package types
- Flexible durations
- Gateway association
- Easy management
Subscriptions
- Active/inactive tracking
- Searchable records
- Pagination support
- Detailed monitoring
Payment Gateway
Controller: PaymentGatewayController.cs
Handles all operations related to payment gateways including listing, adding, updating, and deleting gateways. It also allows for configuring gateway-specific settings such as API credentials.
Key Actions
| Action | Description |
|---|---|
Index() |
Returns the payment gateway overview page |
GetList() |
Retrieves a paginated list of gateways |
Add()/Update() |
Shows the form to add or edit a gateway |
SetDefault() |
Marks a payment gateway as default |
Delete() |
Deletes a payment gateway |
The configuration interface for payment methods is intuitive and secure. Simply paste your credentials and save - no code changes required!
Configuration Process
Stripe Configuration
- Publishable Key
- Secret Key
- Webhook Secret
- Customer Portal Url
Paddle Configuration
- API Key
- Client Token
- Success Url
- Error Url
- Customer Portal Url
Packages
Controller: PackageController.cs
Manages subscription packages, each linked to a payment gateway with specific types and frequencies.
Key Actions
| Action | Description |
|---|---|
Index() |
View for all packages |
GetList() |
Retrieves paginated package list |
Add()/Update() |
Opens views for package editing |
Adding packages automatically update the public pricing page.
Package Configuration
Package Properties
- Associated Gateway
- Name and Description
- Actual Amount (To show the original pricing)
- Current Amount(Show discounted pricing)
- Pricing Header (Pricing Heading inside the pricing card)
- Currency symbol selection
- Billing Frequency (One time or Subscription)
- Product Id (from Paddle/Stripe)
- Price Id (from Paddle/Stripe)
- Display order (used to show the packages 1st, 2nd or the 3rd place)
- Package Type (Basic, Best Valued, Most Popular) Shows the outer lines highlighted and packge is prominantly shown
- Active (show the package on public site or not)
- Description (Package features to be added here)
UI Elements
- Gateway dropdown
- Type selection
- Duration options
- Price input with validation
Important Notes
- Package features must be manually configured in the Pricing section
- All payment credentials are encrypted before storage
- Test your new payment gateway in sandbox mode before going live
Email Templates
UpdatedDynamic email templating, queuing, and sending system for Easylaunchpad using DotLiquid templates and SMTP configurations.
Email Accounts
- SMTP configuration
- Multiple account support
- AES encrypted credentials
- Default account selection
Templates
- DotLiquid templating
- Dynamic placeholders
- Subject/Body customization
- Recipient fields
Queue System
- Asynchronous sending
- Bulk email support
- Retry mechanism
- Background processing
Email Account Configuration
Security Note
SMTP credentials are encrypted using AES-256 before storage in the database.
SMTP Account Management
The EmailAccount model represents SMTP credentials and settings for sending emails.
Key Features
- Multiple SMTP account support
- Default account selection
- SSL/TLS encryption
- Credential encryption using AES
- Test email functionality
Message Templates with DotLiquid
Dynamic Email Templating
MessageTemplates are stored email blueprints that use DotLiquid placeholders for dynamic content rendering.
Template Fields
| Field | Description | Example Placeholder |
|---|---|---|
| Subject | Email subject line | {{OrderNumber}} |
| Body | HTML/Text email content | {{CustomerName}} |
| To | Primary recipient | {{UserEmail}} |
| CC/BCC | Carbon copy recipients | {{ManagerEmail}} |
Rendering Process
Retrieve template from database by name
Create dictionary with placeholder values
DotLiquid replaces placeholders with actual values
Add rendered email to queue for sending
Queued Email System
Performance Note
Emails are processed asynchronously via background service for better performance.
Asynchronous Email Processing
Emails are inserted into the QueuedEmail table and sent by a background service.
Queue Benefits
Reliability
Emails persist in queue until successfully sent
Performance
Non-blocking sending process
Retry Mechanism
Automatic retries for failed sends
Bulk Sending
Efficient processing of multiple emails
Queue Status Tracking
| Status | Description |
|---|---|
| Pending | Email waiting to be sent |
| Sent | Successfully delivered |
| Retrying | Failed but will retry |
| Failed | Permanently failed after retries |
EmailEngineService
Primary Email Interface
The EmailEngineService.Execute() method is the main entry point for sending emails.
Method Signature
Task Execute(string templateName, Dictionary<string, string> keyValuePairs);
Required Dictionary Keys
Subject- Email subject lineToEmail- Primary recipient emailReplyTo- (Optional) Reply-to addressCC/BCC- (Optional) Carbon copy recipients
Adding New Templates
Important
Ensure all required placeholders are provided when executing templates to avoid rendering errors.
Template Creation Process
-
Add template to database
Create new record in MessageTemplate table with placeholders
-
Define required placeholders
Document all variables needed for the template
// Required placeholders: // - CustomerName // - OrderNumber // - CustomerEmail // - OrderTotal -
Test the template
Use sample data to verify rendering works correctly
-
Implement in code
Call EmailEngineService.Execute() with proper data
Important Notes
-
SMTP credentials are encrypted using AES-256 before storage
-
Emails are sent asynchronously via background processing
-
Test templates thoroughly before production use
Scheduled Jobs
UpdatedComprehensive solution for managing background jobs in ASP.NET Core with Hangfire integration.
User Interface
- Job listing with sorting/filtering
- Advance Control Panel (Hangfire)
- Quick Run action
- Server-side pagination
Service Layer
- Job initialization
- Type synchronization
- Automatic discovery
Job Interfaces
- IScheduledJob
- IAsyncScheduledJob
- AsyncScheduledJob base
- Flexible implementation
User Interface Components
Note
All UI components are responsive and work across desktop and mobile devices.
Index View (Index.cshtml)
| Feature | Description |
|---|---|
| Job Listing | Datatable with all scheduled jobs |
| Columns | Name, Type, Schedule, Status, Dates |
| Actions | Quick Run |
| Pagination | Server-side with sorting/searching |
Service Layer (SchedulerJobService)
Important
Service layer handles all job management logic and synchronization with Hangfire.
Core Functionality
Job Initialization
public async Task InitializeSchedulerJobsAsync()
{
// Setup all active jobs with Hangfire
// RecurringJob.AddOrUpdate(...)
}
Key Features
- Automatic discovery of job implementations
- Support for sync/async job types
- Database persistence of configurations
- Automatic reinitialization on updates
Job Interfaces
IScheduledJob
Base interface for all scheduled jobs
public interface IScheduledJob
{
void Execute();
}
IAsyncScheduledJob
Extends IScheduledJob for async operations
public interface IAsyncScheduledJob : IScheduledJob
{
Task ExecuteAsync();
}
AsyncScheduledJob
Abstract base class for async jobs
public abstract class AsyncScheduledJob : IAsyncScheduledJob
{
public abstract Task ExecuteAsync();
public void Execute() =>
throw new NotSupportedException();
}
Extending the Functionality
Developer Note
These extensions require developer access and knowledge of ASP.NET Core and Hangfire.
Adding New Job Types
- Implement IScheduledJob or IAsyncScheduledJob
- System auto-discovers & registers the types to DI
- Sync with database types (optional)
public class MyNewJob : IAsyncScheduledJob
{
public async Task ExecuteAsync()
{
// Job logic here
await Task.Delay(1000);
}
public void Execute() =>
throw new NotSupportedException();
}
// In Startup.cs
services.AddTransient();
Customizing Job Behavior
- Change default schedule in InitializeSchedulerJobsAsync Or SyncJobTypesWithDatabaseAsync (if not depending on hanfgire storage)
- Extend ScheduledJob entity for parameters
- Update DTOs and views
// Example: Changing default schedule
var newJob = new ScheduledJob
{
JobName = jobType.Name,
JobType = typeName,
CronExpression = Cron.Hourly(), // Default
IsActive = false,
CreatedAt = DateTime.UtcNow
};
Usage Instructions
| Action | Instructions |
|---|---|
| Viewing Jobs | Navigate to Scheduled Jobs page to see all configured jobs |
| Advance Configuration | Navigate to the Hangfire console panel |
| Quick Run | Use action button to immediately execute the job |
| Adding New Jobs | Implement interface - system auto-discovers new jobs |
Best Practices
Job Implementation
- Use IAsyncScheduledJob for IO-bound work
- Keep CPU-bound jobs short
- Implement robust error handling
- Design jobs to be idempotent
Monitoring
- Add detailed execution logging
- Set up alerts for failures
- Review Hangfire dashboard
Troubleshooting
| Issue | Solution |
|---|---|
| Job not appearing |
|
| Job not executing |
|
| Changes not effective |
|
System Summary
This system provides a flexible foundation for scheduled job management that can be extended to meet specific application needs while maintaining a consistent interface for administration.
Sitemaps
UpdatedAutomated website crawling and XML sitemap generation solution
System Overview
Note
This system automatically crawls your website and generates standards-compliant XML sitemaps through scheduled background jobs.
Scheduled Job
- Runs on configurable schedule (daily)
- Controlled by SEO settings
- Uses application's root as the base URL
- Automatic execution
Crawler Service
- Starts from root URL
- Follows internal links
- Excludes admin paths
- Normalizes URLs
Data Management
- CRUD operations
- Paginated listing
- Duplicate detection
- Search/sort capabilities
Key Components
CrawlAndGenerateSitemapJob
Responsibilities
- Executes sitemap generation process
- Checks auto-generation setting
- Initiates crawling process
- Generates and update sitemap.xml
Configuration
SeoSettings.GenerateAutoSitemapflag- Root Address as Base URL and from
SeoSettings.SitemapBaseUrl primarily from Application Constants - Cron schedule configuration
SitemapCrawlerService
Website Crawling
- Starts from root URL (/)
- Follows internal links
- Excludes admin/account paths
- Processes only HTML with 200 status
URL Normalization
- Converts to absolute URLs
- Removes fragments/queries
- Filters external links
- Detects duplicates
Key Features
SitemapService
Core Operations
- Create/Read/Update/Delete
- Paginated listing
- Search & sort
- Existence checking
Example: Add Entry
await _sitemapService.AddAsync(new SitemapEntry
{
Url = "https://example.com/page",
LastModified = DateTime.UtcNow,
ChangeFrequency = "weekly",
Priority = 0.8
});
Example: Get All
var entries = await _sitemapService
.GetAllAsync(pageNumber, pageSize);
How It Works
Job runs periodically based on cron schedule, checks if auto-generation is enabled
Starts from root URL, discovers internal links, skips excluded paths
Valid URLs stored in database SitemapEntries table with metadata
Generates XML sitemap with all URLs, saves to sitemap.xml
Sitemap XML Structure
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://example.com/</loc>
<lastmod>2023-06-30</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<!-- Additional URLs -->
</urlset>
Extending the Functionality
Customizing Crawling Behavior
Exclusion List
// Add to excludedPaths in SitemapCrawlerService
private readonly List<string> excludedPaths = new() {
"/admin",
"/login",
"/account",
"/dashboard",
"/new-path-to-exclude" // Add your paths
};
Enhancing Sitemap Content
Dynamic Priority
// Dynamic priority calculation
await _sitemapService.AddAsync(new SitemapEntry
{
Url = fullUrl,
LastModified = DateTime.UtcNow,
ChangeFrequency = "weekly",
Priority = CalculatePriority(fullUrl) // Custom logic
});
Adding Manual Triggers
[HttpPost]
public async Task<IActionResult> GenerateNow()
{
var job = new CrawlAndGenerateSitemapJob(...);
job.Execute();
return RedirectToAction(nameof(Index));
}
Multi-Language Support
// In CrawlAsync method
if (urlContainsLanguageParameter(fullUrl))
{
// Handle language-specific URL
// Add hreflang tags
}
Monitoring & Reporting
public class CrawlStats {
public int TotalPages { get; set; }
public int NewUrlsAdded { get; set; }
public DateTime LastRun { get; set; }
}
Usage Instructions
| Action | Instructions |
|---|---|
| Enable Auto-Generation | Set SeoSettings.GenerateAutoSitemap to true and configure AppSettings:BaseUrl |
| Manual Generation | Manual trigger or wait for scheduled execution |
| Viewing Results | Access generated sitemap at /sitemap.xml |
| Admin Interface | View crawled URLs and manage settings through admin UI |
Best Practices
Schedule Frequency
- Dynamic sites: Daily or weekly
- Static sites: Monthly
- E-commerce: Consider hourly for inventory
Performance
- Schedule during low-traffic periods
- Add delays between requests for large sites
- Monitor memory usage
- Consider distributed crawling
SEO Recommendations
- Ensure important pages are reachable
- Verify excluded paths
- Check sitemap completeness
- Submit to search consoles
Troubleshooting
| Issue | Solution |
|---|---|
| Missing Pages |
|
| Sitemap Not Updating |
|
| Performance Issues |
|
System Summary
This system provides a flexible foundation for automated sitemap generation that can be customized to meet specific website requirements while following SEO best practices.
Settings
UpdatedCentralized configuration system for managing application settings
System Overview
Note
This system provides a centralized way to manage application settings through a web interface with role-based access control.
Configuration
- Key-value storage
- Dynamic loading
- Type-safe access
- Cache integration
Management
- Admin web interface
- Category organization
Key Components
AppSettingsService
Core Operations
- Load settings by type
- Save settings changes
- Cache management
Key Features
SettingsController
Core Actions
- Index - List all settings
- Details/Edit - View & Update settings
Example: Get Settings
[HttpGet("branding")]
public IActionResult Branding()
{
var model = _settingsService
.LoadSettings<BrandingSettings>();
return View(model);
}
Example: Save Settings
[HttpPost("branding")]
public IActionResult Branding(
BrandingSettings model)
{
_settingsService.SaveSettings(model);
_cache.Refresh();
return RedirectToAction(nameof(Branding));
}
SettingsBase
Example Implementation
public class BrandingSettings : SettingsBase
{
public string SiteName { get; set; }
public string LogoUrl { get; set; }
public string PrimaryColor { get; set; }
// Additional properties...
}
How It Works
Application requests settings through AppSettingsService
Service checks cache, then database, returns typed settings
Changes made through UI are validated and saved
Cache is invalidated and settings are reloaded on next request
Database Structure
CREATE TABLE AppSettings (
Id INT PRIMARY KEY,
Name NVARCHAR(255) NOT NULL,
Value NVARCHAR(MAX) NOT NULL
);
Extending the Functionality
Adding New Settings
Create Settings Class
public class NotificationSettings : SettingsBase
{
public bool EnableEmail { get; set; }
public string AdminEmail { get; set; }
public int MaxNotifications { get; set; }
// Additional properties
}
Settings UI
@model BrandingSettings
<form method="post">
<div class="form-group">
<label asp-for="SiteName"></label>
<input asp-for="SiteName" class="form-control" />
</div>
<!-- Additional fields -->
<button type="submit" class="btn btn-primary">Save</button>
</form>
Validation
public class BrandingSettings : SettingsBase
{
[Required]
[StringLength(100)]
public string SiteName { get; set; }
[Url]
public string LogoUrl { get; set; }
[RegularExpression("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$")]
public string PrimaryColor { get; set; }
}
Audit Logging
public async Task SaveSettingsAsync<T>(T settings)
where T : SettingsBase
{
var oldSettings = await LoadSettingsAsync<T>();
settings.UpdatedAt = DateTime.UtcNow;
settings.UpdatedBy = _userService.GetCurrentUserId();
// Log changes
_auditLogger.LogChange(
typeof(T).Name,
oldSettings,
settings
);
// Save to database
await _repository.SaveAsync(settings);
}
Usage Instructions
| Action | Instructions |
|---|---|
| Access Settings | Use _settingsService.LoadSettings<T>() in your application code |
| Modify Settings | Use admin UI or call _settingsService.SaveSettings<T>() |
| Add New Category | Create new Settings class and implement UI |
Best Practices
Organization
- Group related settings: Keep similar settings together
- Use clear names: Self-documenting property names
- Default values: Provide sensible defaults
Security
- Encrypt sensitive data
- Implement proper access controls
- Validate all inputs
Performance
- Cache settings appropriately
- Batch related updates
- Minimize database calls
- Consider distributed caching
System Summary
This settings module provides a robust, secure foundation for managing all application configuration and role-based access control.
Stripe Payment Gateway Integration
UpdatedSeamlessly integrate Stripe as a payment gateway to accept credit card payments and manage subscriptions.
Stripe Features
- Secure credit card processing
- Recurring subscription billing
- PCI compliant payment handling
- Support for multiple currencies
- Webhook integration for real-time updates
Security Features
- Credentials encrypted at rest
- Token-based payment processing
- No sensitive data stored on your servers
- Secure API communication
- Automatic PCI compliance
- Two-factor authentication support
Stripe Setup Guide
Before You Begin
You'll need an active Stripe account. Create one at stripe.com if you don't have one already.
Configuration Steps
Navigate to Payment Gateways
Go to Settings → Payment Gateways in the admin panel
Change default Payment Gateway
Click action and select "Default"
Enter Credentials
Copy your API keys etc. from Stripe Dashboard and paste them on configuration form.
Save Configuration
Click Save to activate your Stripe integration
Required Stripe Credentials
| Field | Description | Where to Find |
|---|---|---|
Publishable Key |
Used for client-side operations | Stripe Dashboard → Developers → API Keys |
Secret Key |
Used for server-side operations | Stripe Dashboard → Developers → API Keys |
Webhook Secret |
Verifies webhook requests from Stripe | Stripe Dashboard → Developers → Webhooks |
Customer Portal Url |
A secure link provided by Stripe that allows customers to view and manage their subscriptions, billing details, and payment methods directly. |
Webhook Configuration
To receive real-time payment events from Stripe:
Endpoint URL: https://yourdomain.com/webhook/stripe
Events to listen for:
- checkout.session.completed
- checkout.session.expired
- customer.subscription.updated
- customer.subscription.deleted
- invoice.payment_failed
Important
Webhooks are essential for subscription management. Without proper webhook setup, subscription statuses may not update correctly.
Testing Your Integration
Test Mode
Use Stripe's test mode to verify your integration without processing real payments. Toggle between live and test mode in the payment gateway configuration.
Test Cards
Use these test card numbers:
4242 4242 4242 4242- Successful payment4000 0000 0000 0002- Failed payment5555 5555 5555 4444- Mastercard (success)
Troubleshooting
Verify your API keys are correct and in the right mode (test/live). Check your server logs for any Stripe API errors.
Ensure your webhook endpoint is accessible from the internet and the secret key matches what's in your Stripe dashboard.
Check that all required webhook events are enabled and your webhook handler is processing them correctly.
Best Practices
- Always use HTTPS for your payment pages and webhook endpoints
- Regularly rotate your API keys and webhook secrets
- Monitor your Stripe dashboard for failed payments and disputes
- Implement proper error handling for payment failures
- Keep your Stripe SDK updated to the latest version
Paddle Payment Gateway Integration
UpdatedIntegrate Paddle as a payment gateway to accept payments and manage subscriptions with built-in tax compliance and global payment methods.
Paddle Features
- Unified payments and subscriptions
- Global payment methods support
- Sandbox environment for testing
- Webhook integration for real-time updates
Security Features
- API key authentication
- Sandbox and production environments
- Webhook signature verification
- PCI compliant payment handling
- Role-based access control
Paddle Setup Guide
Before You Begin
You'll need an active Paddle account. Create one at paddle.com if you don't have one already.
Configuration Steps
Navigate to Payment Gateways
Go to Settings → Payment Gateways in the admin panel
Change default Payment Gateway
Click action and select "Default"
Enter Credentials
Copy your API keys from Paddle Dashboard and paste them on configuration form
Save Configuration
Click Save to activate your Paddle integration
Required Paddle Credentials
| Field | Description | Where to Find |
|---|---|---|
API Key |
Used for server-side API calls | Paddle Dashboard → Authentication → API Keys |
Client Token |
Used for client-side operations | Paddle Dashboard → Authentication → Client Tokens |
Success Url |
Used to redirect the user once payment is successful | |
Error Url |
Used to redirect the user once payment is failed | |
Customer Portal Url |
A secure link provided by Paddle that allows customers to view and manage their subscriptions, billing details, and payment methods directly. | |
Sandbox Mode |
Toggle between test and production environments | Configuration setting in admin UI |
Webhook Configuration
To receive real-time payment events from Paddle:
Endpoint URL: https://yourdomain.com/webhook/paddle
Events to listen for:
- subscription.created
- subscription.updated
- subscription.cancelled
- transaction.completed
- transaction.paid
Important
Webhooks are essential for subscription management. Without proper webhook setup, subscription statuses may not update correctly.
Testing Your Integration
Sandbox Mode
Use Paddle's sandbox environment to test your integration without processing real payments. Toggle sandbox mode in the payment gateway configuration.
Test Cards
Use these test card numbers in sandbox mode:
4242 4242 4242 4242- Successful payment4000 0000 0000 0002- Failed payment5555 5555 5555 4444- Mastercard (success)
Troubleshooting
Verify your API key is correct and has the proper permissions. Ensure you're using the correct base URL (sandbox vs production).
Check that your webhook endpoint is publicly accessible and returns a 200 status code. Verify the webhook URL is correctly configured in Paddle Dashboard.
Ensure all required webhook events are enabled and your webhook handler is processing them correctly. Check your server logs for any processing errors.
Best Practices
- Always use HTTPS for your payment pages and webhook endpoints
- Regularly rotate your API keys
- Implement proper error handling for payment failures
- Test your integration thoroughly in sandbox mode before going live
- Monitor your Paddle dashboard for failed payments and disputes
Google OAuth Integration
UpdatedImplement secure authentication with Google OAuth 2.0, allowing users to sign in with their Google accounts.
OAuth Features
- Secure Google account authentication
- Configurable through admin UI
Google OAuth Setup Guide
Before You Begin
You'll need to create a project in the Google Cloud Console and configure OAuth credentials.
Admin UI Configuration
| Field | Description | Where to Find |
|---|---|---|
Client ID |
Google OAuth client identifier | Google Cloud Console → APIs & Services → Credentials |
Client Secret |
Google OAuth client secret (stored encrypted) | Google Cloud Console → APIs & Services → Credentials |
Configuration Steps:
- Navigate to
Admin → Settings → Authentication - Click on "Google OAuth Configuration"
- Enter your Google Client ID and Client Secret
- Save the configuration
Testing Your Integration
Test Mode
Use Google's test mode to verify your integration without affecting real users. You can configure test users in the Google Cloud Console under OAuth consent screen.
Test Accounts
For testing purposes:
- Use any Google account you control for basic testing
- For restricted scopes, add test users in Google Cloud Console
- Verify different scenarios (new users vs returning users)
Troubleshooting
Verify your Client ID and Client Secret are correctly entered in the admin UI and match what's in your Google Cloud Console. Ensure the Client Secret hasn't expired.
Ensure the authorized redirect URIs in Google Cloud Console exactly match your domain and the standard /signin-google endpoint.
Best Practices
- Always use HTTPS for your authentication endpoints
- Request only the scopes your application needs
- Regularly review authorized applications in your Google Cloud Console
- Implement proper session management and logout functionality
- Monitor your Google Cloud Console for any security alerts
Google reCAPTCHA Integration
UpdatedIntegrate Google reCAPTCHA to protect your application from spam and automated submissions on login, registration, and other sensitive forms.
reCAPTCHA Features
- Protects against spam and automated abuse
- Simple "I'm not a robot" checkbox
- Invisible reCAPTCHA options available
- Detailed analytics in Google admin console
Security Features
- Server-side token validation
- Configurable for specific forms
- Conditional rendering based on settings
- Graceful degradation if reCAPTCHA fails to load
reCAPTCHA Setup Guide
Before You Begin
You'll need to register your site at Google reCAPTCHA Admin to obtain your site key and secret key.
Configuration Steps
Register Your Site
Go to reCAPTCHA Admin and register your site
Configure Security Settings
Navigate to Settings → Security in the admin panel
Enter reCAPTCHA Keys
Add your site key and secret key from Google reCAPTCHA
Enable for Specific Forms
Toggle which forms should use reCAPTCHA (login, register, etc.)
Required reCAPTCHA Credentials
| Field | Description | Where to Find |
|---|---|---|
Site Key |
Used for client-side widget integration | Google reCAPTCHA Admin → Your Sites |
Secret Key |
Used for server-side token validation | Google reCAPTCHA Admin → Your Sites |
Enable for Forms |
Toggle which forms require reCAPTCHA | Security Settings in admin UI |
Implementation Details
Server-Side Validation
public class ValidateRecaptchaAttribute : TypeFilterAttribute
{
public ValidateRecaptchaAttribute() : base(typeof(ValidateRecaptchaFilter))
{
}
private class ValidateRecaptchaFilter : IAsyncActionFilter
{
// Validates reCAPTCHA token with Google's API
// Checks if reCAPTCHA is enabled for the specific action
// Returns appropriate error responses if validation fails
}
}
Apply this attribute to any action that requires reCAPTCHA validation:
[HttpPost]
[ValidateRecaptcha]
public async Task<IActionResult> Login(LoginModel model)
{
// Your login logic
}
Client-Side Implementation
Load reCAPTCHA Script
@section Scripts {
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
}
Add reCAPTCHA Widget
if (ViewBag.CaptchEnabled)
{
<div class="g-recaptcha" data-sitekey="@_securitySettings.CaptchaPublicKey"></div>
}
Validate Before Submission
if (isCaptchaEnabled) {
if (typeof grecaptcha === 'undefined') {
showToast('reCAPTCHA failed to load. Please refresh the page.', 'error');
return;
}
var token = grecaptcha.getResponse();
if (!token) {
showToast('Please verify you are a human.', 'error');
return;
}
}
Troubleshooting
Verify the site key is correct and the reCAPTCHA script is properly loaded. Check browser console for errors. Ensure the widget is only rendered when ViewBag.CaptchEnabled is true.
Check your secret key is correct. Verify your server can reach Google's reCAPTCHA API (no firewall restrictions). Ensure you're passing the correct token from the client-side.
Check your security settings to ensure reCAPTCHA is enabled for the specific form type (login, register, etc.). Verify the ValidateRecaptcha attribute is applied to the action.
Best Practices
- Always validate reCAPTCHA tokens server-side - client-side validation alone is not secure
- Monitor your reCAPTCHA analytics in the Google admin console
- Consider using reCAPTCHA v3 for less intrusive protection
- Provide clear error messages when reCAPTCHA validation fails
- Regularly review your security settings and adjust reCAPTCHA sensitivity as needed
Quick Rebranding Cheat Sheet
15 minsThe 15-Minute Rebrand Guide
Transform your EasyLaunchpad application to match your brand identity in just 15 minutes. This streamlined process allows you to update colors, typography, and visual elements without touching any component code.
Prerequisites Check (2 minutes)
Before you start, verify you have:
- Node.js installed on your machine
- Terminal/Command Prompt access
- Text editor (VS Code, Sublime, etc.)
- Access to your new brand colors (hex codes ready)
- The project files accessible
Phase 1: Preparation (5 minutes)
Gather Your New Brand Assets
Collect these specific items:
| Color Type | Purpose | Example Usage |
|---|---|---|
| Primary Brand Color | Your main brand color | Logo color, main buttons |
| Secondary Color | Supporting elements | Less prominent UI elements |
| Accent Color | Highlights & CTAs | Call-to-action buttons, links |
| Success Color | Positive actions | Success messages, confirmations |
| Error Color | Errors & warnings | Error messages, validation |
| Warning Color | Cautions | Warning messages, alerts |
| Info Color | Informational messages | Tips, helpful information |
Understand Color Variants Needed
For EACH brand color (primary, secondary, accent), you need:
DEFAULT State
The base color used everywhere
Hover State
Slightly different when user hovers (usually 10-15% darker/lighter)
Light Variant
Lighter version for backgrounds, badges, subtle elements
Dark Variant
Darker version for text on light backgrounds or emphasis
Pro Tip
Use online tools like ColorBox.io or Adobe Color to generate these variants automatically from your base color.
Create a Color Reference Document
Make a simple list like this:
Primary Color:
- Default: #1a73e8
- Hover: #1557b0
- Light: #e8f4fd
- Dark: #0d3c6e
Secondary Color:
- Default: #5f6368
- Hover: #3c4043
- Light: #f1f3f4
- Dark: #202124
Accent Color:
- Default: #34a853
- Hover: #2d8e47
- Light: #e6f4ea
- Dark: #1e7e34
(Continue for all colors...)
Phase 2: Theme File Configuration (5 minutes)
Open the Theme Configuration File
Navigate to EasyLaunchpad.Website's root and open:
File name: tailwind.theme.js
This is your ONLY editing point for colors - don't touch any other CSS files
Critical Note
Only edit tailwind.theme.js. Do NOT edit _theme-vars.css or site.css directly as these files are auto-generated.
Update Brand Colors Section
Locate the brandColors object and replace:
What to change:
- Find the
primarycolor group - Replace all four values (DEFAULT, hover, light, dark) with your new primary color variants
- Repeat for
secondarycolor group - Repeat for
accentcolor group
const brandColors = {
primary: {
DEFAULT: '#1a73e8', // Your new primary color
hover: '#1557b0', // Darker/lighter on hover
light: '#e8f4fd', // Light background variant
dark: '#0d3c6e' // Dark text variant
},
secondary: {
DEFAULT: '#5f6368',
hover: '#3c4043',
light: '#f1f3f4',
dark: '#202124'
},
accent: {
DEFAULT: '#34a853',
hover: '#2d8e47',
light: '#e6f4ea',
dark: '#1e7e34'
}
};
Critical notes:
- Use hex color format (starting with #)
- Keep the structure exactly the same
- Don't remove any properties
- Ensure quotes are properly closed
Update Semantic Colors Section (Optional)
Locate the semanticColors object and optionally update:
Success Colors
Keep green unless your brand specifically requires different
Error Colors
Keep red unless your brand specifically requires different
Warning Colors
Keep orange/yellow unless your brand specifically requires different
Info Colors
Keep blue unless your brand specifically requires different
When to change semantic colors:
- Your brand guideline specifically defines these
- Industry standards require different colors (e.g., finance, healthcare)
- Accessibility requirements demand specific contrast ratios
Customize Dark/Light Mode Appearance
Locate the modes object with dark and light sub-objects:
What to update in DARK mode:
textPrimary
Main text color in dark mode (usually off-white)
textSecondary
Secondary text color (slightly dimmer than primary)
cardBackground
Background for cards/panels (slightly lighter than page background)
border
Border color for elements (subtle, not too bright)
What to update in LIGHT mode:
pageBackground
The overall light background color (usually white or very light gray)
textPrimary
Main text color in light mode (usually very dark gray/black)
textSecondary
Secondary text color (lighter gray)
cardBackground
Background for cards/panels (usually white or slight off-white)
border
Border color for elements (light gray)
Important consideration:
- Ensure sufficient contrast ratios (4.5:1 for normal text, 3:1 for large text)
- Test readability in both modes
- Keep dark mode truly dark (don't make it too bright)
- Keep light mode comfortable (pure white can be harsh)
Phase 3: Typography & Fonts (3 minutes)
Update Font Families (If Changing)
Still in tailwind.theme.js, locate the typography section:
What to change:
fontFamily.sans
Your primary body font (used for most text)
fontFamily.heading
Font for headings (can be same as sans or different)
fontFamily.mono
Font for code snippets (usually monospace font)
Font format requirements:
- List fonts in priority order
- Include fallback system fonts
- Use exact font names as they appear in Google Fonts or your CSS
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
heading: ['Poppins', 'Inter', 'sans-serif'],
mono: ['Fira Code', 'monospace']
}
// Main brand font → Secondary option → System fallback
Adjust Font Sizes (If Needed)
Review the fontSize object:
When to change:
- Your brand requires larger/smaller base text
- Accessibility requirements
- Target audience preferences (older users need larger text)
What to consider:
baseis your default body text size (typically 1rem = 16px)- All other sizes scale relative to base
- Maintain proportional relationships between sizes
Note
Most applications work well with default font sizes. Only adjust if you have specific brand or accessibility requirements.
Update Spacing/Padding (Optional)
If your brand feels more "spacious" or "compact":
What to adjust:
- Component padding values
- Section spacing
- Container widths
Note
This is advanced customization - skip if unsure.
Phase 4: Compilation & Testing (5 minutes)
Generate CSS Variables
Open your terminal in the EasyLaunchpad.Website's root directory and run:
npm run generate:theme
✓ What this does:
Converts your theme.js file into CSS custom properties
✓ Output:
Creates/updates _theme-vars.css file
✓ Wait for:
"Theme variables generated successfully" message
If you see errors:
- Check for syntax errors in tailwind.theme.js
- Ensure all color values have # symbols
- Verify all quotes are properly closed
- Look for missing commas between properties
Build Final CSS
In the same terminal, run:
npm run build:css
✓ What this does:
Compiles everything into the final site.css file
✓ Output:
Updates site.css in wwwroot/css/
✓ Wait for:
Compilation completion message (usually shows file size)
Alternative For Compiling Tailwind
If you dont run any command, then Clean the Solution and Rebuild Solution, it is already added to .csproj as the prebuild command
Clear Browser Cache
Critical step before testing:
Windows/Linux
Press Ctrl + Shift + R
Mac
Press Cmd + Shift + R
Why this is important:
Browsers cache CSS files aggressively
Alternative: Open in Incognito/Private browsing mode
Visual Testing Checklist
Open your application and systematically check:
Color verification:
Typography testing:
Responsive testing:
Component-specific testing:
Cross-Browser Testing
If this is production-critical, test in:
Chrome/Edge
Chromium-based
Firefox
Mozilla
Safari
If targeting Mac/iOS
The Absolute Minimum Rebrand
If you only have 5 minutes:
Open tailwind.theme.js
Change ONLY these three colors in brandColors:
- primary.DEFAULT
- secondary.DEFAULT
- accent.DEFAULT
Run: npm run generate:theme
Run: npm run build:css
Hard refresh browser (Ctrl+Shift+R)
Result
Your app now uses new colors everywhere components exist.
Common Pitfalls & How to Avoid Them
Mistake 1: Editing Wrong Files
Don't edit: _theme-vars.css or site.css directly
Only edit: tailwind.theme.js
Why: Those files are auto-generated and will be overwritten
Mistake 2: Forgetting to Regenerate
Editing theme file and refreshing browser immediately
Always run both npm commands after editing
Why: Changes don't apply until CSS is rebuilt
Mistake 3: Poor Contrast Ratios
Using light text on light backgrounds
Use contrast checker tools before committing
Why: Accessibility and readability suffer
Mistake 4: Not Testing Dark Mode
Only checking light mode appearance
Always toggle and test both modes thoroughly
Why: Dark mode might have unreadable combinations
Mistake 5: Skipping Browser Cache Clear
Refreshing normally and thinking "it didn't work"
Always hard refresh (Ctrl+Shift+R)
Why: Browser serves cached CSS file
Mistake 6: Inconsistent Color Variants
Making hover state too different from default
Keep hover states subtle (10-15% darker/lighter)
Why: Jarring transitions confuse users
Mistake 7: Breaking JSON/JavaScript Syntax
Missing commas, quotes, or brackets
Use code editor with syntax highlighting
Why: Build command will fail with cryptic errors
Quick Reference Commands
Daily Development:
npm run watch:css
Automatically rebuilds CSS when you save changes - use this during active development.
After Theme Changes:
npm run build:all
Regenerates variables AND rebuilds CSS in one command.
Just CSS Compilation:
npm run build:css
Only rebuilds CSS without regenerating variables.
Understanding What Happens Behind the Scenes
The Theme Pipeline:
You edit tailwind.theme.js with new colors
generate-theme-vars.js reads your theme file
Converts your colors to CSS custom properties (variables)
Writes those variables to _theme-vars.css
Tailwind compiler reads both _theme-vars.css and tailwind.css
Combines everything into final site.css
Your HTML uses site.css to display with new colors
Why This Architecture Exists:
Single source of truth
One file controls all colors
Automatic propagation
Change once, updates everywhere
Theme switching
Dark/light modes work automatically
Maintainability
Easy to update without touching components
Consistency
Impossible to have mismatched colors
Post-Rebrand Checklist
After completing your rebrand, verify:
Emergency Rollback
If something goes terribly wrong:
Option 1: Restore from Backup
Find your backup of tailwind.theme.js (you made one, right?)
Replace the current file with the backup
Run: npm run build:css
Hard refresh browser
If no backup exists:
- Check version control (Git) history
- Restore from last commit
- Or manually revert color values to previous brand
Pro Tips for Smooth Rebranding
Make a backup of tailwind.theme.js before starting
Change one section at a time (brand colors first, then modes, then typography)
Test after each major change rather than all at once
Use color palette generators to create harmonious variants
Keep a style guide document with all your color decisions
Take screenshots of the old design for comparison
Get stakeholder approval on colors BEFORE implementing
Consider accessibility from the start, not as an afterthought
Test on actual devices not just browser resize tools
Document your decisions for future reference
Time Investment
Basic Rebrand
15-30 min
Colors only
Comprehensive
1-2 hours
Colors + Typography + Testing
Emergency
5 min
Absolute minimum
File Reference
Files You Should Edit:
tailwind.theme.js
- Theme colors
tailwind.css
- Custom classes
tailwind.config.js
- Advanced
Files You Should NOT Edit:
_theme-vars.css
- Auto-generated
site.css
- Compiled output
Helpful Resources
Congratulations!
You now have all the knowledge needed to rebrand your EasyLaunchpad application quickly and efficiently.
Last Updated: 2025 | Version: 1.0
Theming System Comprehensive Documentation
UpdatedComprehensive CSS/Tailwind-based theming architecture for rapid UI development.
System Features
- Dynamic dark/light mode switching
- Pre-built, reusable UI components
- Centralized color management
- CSS variable-based theming
- Automatic theme compilation
Development Benefits
- Rapid UI development workflow
- Consistent styling across application
- Easy customization and maintenance
- Production-ready components
- Responsive design system
Goal
Enable rapid UI development with consistent, customizable styling across your entire application.
File Structure
wwwroot/
css/
tailwind.css - Component classes & utilities
_theme-vars.css - Auto-generated CSS variables
site.css - Compiled final stylesheet
scripts/
generate-theme-vars.js - Theme generator script
tailwind.public.config.js - Main Tailwind config
tailwind.theme.js - Theme definitions (EDIT THIS)
System Architecture
Theme Definition
Define all colors, modes (dark/light), typography, and spacing in tailwind.theme.js - your single source of truth.
// tailwind.theme.js
module.exports = {
colors: {
primary: '#3b82f6',
secondary: '#8b5cf6',
// ... more colors
}
}
Variable Generation
Script reads tailwind.theme.js and automatically generates _theme-vars.css with CSS custom properties.
npm run generate:theme
Component Classes
Imports _theme-vars.css and defines reusable component classes like .btn-primary, .card-base, etc.
Tailwind Configuration
Configures Tailwind to use the theme colors and extends utility classes in tailwind.public.config.js.
Final Compilation
Tailwind compiles everything into the final site.css file used in your HTML.
npm run build:css
Flow Summary
tailwind.theme.js
generate-theme-vars.js
_theme-vars.css
tailwind.css
tailwind.public.config.js
site.css
Using Pre-Built Components
All component classes are defined in tailwind.css and ready to use in your HTML.
Button Components
Available Classes:
.btn-primary- Primary action button.btn-secondary- Secondary button.btn-outline- Outlined button.btn-ghost- Transparent button.btn-sm- Small button.btn-gradient-primary- Gradient button
HTML Examples:
<!-- Primary Button -->
<button class="btn-primary">Click Me</button>
<!-- Secondary Button -->
<button class="btn-secondary">Cancel</button>
<!-- Small Outline Button -->
<button class="btn-outline btn-sm">Learn More</button>
<!-- Gradient Button -->
<button class="btn-gradient-primary">Sign Up</button>
Card Components
Available Classes:
.card-base- Basic card with padding and border.card-hover- Card with hover animation.card-elevated- Card with shadow elevation.auth-card- Authentication-specific card
HTML Example:
<div class="card-hover">
<h3 class="text-primary text-xl font-bold mb-2">Card Title</h3>
<p class="text-secondary">Card content goes here</p>
</div>
Form Components
Available Classes:
.input-base- Text input field.form-label- Form label.form-group- Form field container.form-error- Error message.checkbox-base- Checkbox input
HTML Example:
<div class="form-group">
<label class="form-label">Email Address</label>
<input type="email" class="input-base" placeholder="you@example.com">
<span class="form-error">Please enter a valid email</span>
</div>
Badge Components
Available Classes:
.badge-primary- Primary badge.badge-success- Success badge (green).badge-error- Error badge (red).badge-warning- Warning badge (orange).badge-info- Info badge (blue)
HTML Example:
<span class="badge-success">Active</span>
<span class="badge-error">Cancelled</span>
<span class="badge-warning">Pending</span>
Complete Component Reference
| Component Type | Class Name | Description |
|---|---|---|
| Button | .btn-primary |
Primary action button |
| Button | .btn-secondary |
Secondary button |
| Card | .card-base |
Basic card with border |
| Card | .card-hover |
Card with hover effect |
| Input | .input-base |
Styled text input |
| Badge | .badge-success |
Green success badge |
| Alert | .alert-error |
Red error alert box |
| Navigation | .nav-link |
Navigation link style |
Changing Theme Colors & Styles
Important
Always edit tailwind.theme.js - never manually edit _theme-vars.css or site.css as they are auto-generated.
Method 1: Changing Brand Colors
Step 1: Open tailwind.theme.js
// Located at project root
tailwind.theme.js
Step 2: Edit Brand Colors Section
brandColors: {
primary: {
DEFAULT: '#your-primary-color', // Change to your brand color
hover: '#your-hover-color', // Hover state
light: '#your-light-variant', // Light variant
dark: '#your-dark-variant', // Dark variant
},
secondary: {
DEFAULT: '#your-secondary-color', // Secondary color
hover: '#hover-state',
light: '#light-variant',
dark: '#dark-variant',
},
accent: {
DEFAULT: '#your-accent-color', // Accent color
hover: '#hover-state',
light: '#light-variant',
dark: '#dark-variant',
},
}
Step 3: Regenerate Theme Variables
npm run generate:theme
Step 4: Rebuild CSS
npm run build:css
Done!
Your new colors are now applied throughout the entire application.
Method 2: Customizing Dark/Light Modes
Edit Mode-Specific Colors
modes: {
dark: {
pageBackground: '#your-dark-bg', // Dark mode page background
textPrimary: '#your-dark-text', // Dark mode text
cardBackground: '#your-card-bg', // Dark mode cards
// ... more properties
},
light: {
pageBackground: '#your-light-bg', // Light mode page background
textPrimary: '#your-light-text', // Light mode text
cardBackground: '#your-card-bg', // Light mode cards
// ... more properties
},
}
Method 3: Changing Typography
typography: {
fontFamily: {
sans: ['Your-Font', 'system-ui', 'sans-serif'], // Body font
heading: ['Heading-Font', 'sans-serif'], // Heading font
mono: ['Mono-Font', 'monospace'], // Code font
},
fontSize: {
xs: '0.75rem',
sm: '0.875rem',
base: '1rem', // Default size
lg: '1.125rem',
xl: '1.25rem',
// ...
},
}
Example Color Palette Structure
Note
Replace these example colors with your own brand colors in tailwind.theme.js
Adding Custom Components
To add your own reusable component classes:
Open tailwind.css
// File: wwwroot/css/tailwind.css
Add Your Component in @layer components
@layer components {
/* Your custom component */
.my-custom-button {
background-color: rgb(var(--color-accent));
color: #000;
padding: 12px 24px;
border-radius: 8px;
font-weight: 600;
transition: all 0.3s;
}
.my-custom-button:hover {
transform: scale(1.05);
box-shadow: 0 4px 12px rgba(var(--color-accent), 0.3);
}
.my-custom-card {
background-color: rgb(var(--theme-card-background));
border: 2px solid rgb(var(--color-primary));
border-radius: 12px;
padding: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
}
Rebuild CSS
npm run build:css
Use in HTML
<button class="my-custom-button">Custom Button</button>
<div class="my-custom-card">
<h3>Custom Card</h3>
<p>With custom styling</p>
</div>
Pro Tip
Always use CSS variables like rgb(var(--color-primary)) in your custom components so they automatically adapt to theme changes.
Best Practices for Custom Components
- ✅ Use theme CSS variables instead of hardcoded colors
-
✅
Follow the naming convention:
.component-name - ✅ Add hover states for interactive elements
- ✅ Include transition effects for smooth animations
- ✅ Test in both dark and light modes
Advanced Usage
Creating a Complete Page Layout
Example: Landing Page with Components
<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My App</title>
<link rel="stylesheet" href="/css/site.css">
</head>
<body class="bg-page">
<!-- Hero Section -->
<section class="section-page">
<div class="container-narrow text-center">
<h1 class="title-hero text-primary">Welcome</h1>
<p class="subtitle-base text-secondary mb-8">
Build beautiful applications
</p>
<button class="btn-primary-lg">Get Started</button>
</div>
</section>
<!-- Features Section -->
<section class="section-page bg-section-alt">
<div class="container-base">
<h2 class="title-section text-center mb-12">Features</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="card-hover">
<h3 class="text-xl font-bold text-primary mb-3">Fast</h3>
<p class="text-secondary">Lightning-fast development</p>
</div>
<div class="card-hover">
<h3 class="text-xl font-bold text-primary mb-3">Flexible</h3>
<p class="text-secondary">Customize everything</p>
</div>
<div class="card-hover">
<h3 class="text-xl font-bold text-primary mb-3">Modern</h3>
<p class="text-secondary">Latest design trends</p>
</div>
</div>
</div>
</section>
</body>
</html>
Authentication Page Example
Login Page
<section class="section-auth">
<div class="container-narrow">
<div class="auth-card">
<div class="auth-header">
<h2 class="text-3xl font-bold text-primary mb-2">Welcome Back</h2>
<p class="text-secondary">Sign in to your account</p>
</div>
<div class="auth-body">
<form class="auth-form">
<div class="form-group">
<label class="form-label">Email</label>
<input type="email" class="auth-input"
placeholder="you@example.com">
</div>
<div class="form-group">
<label class="form-label">Password</label>
<input type="password" class="auth-input"
placeholder="••••••••">
</div>
<div class="auth-checkbox-wrapper">
<input type="checkbox" class="auth-checkbox" id="remember">
<label for="remember" class="auth-checkbox-label">
Remember me
</label>
</div>
<button type="submit" class="auth-button">Sign In</button>
</form>
</div>
<div class="auth-footer">
<p class="auth-footer-text">
Don't have an account?
<a href="/register" class="auth-footer-link">Sign up</a>
</p>
</div>
</div>
</div>
</section>
Theme Switching Implementation
JavaScript for Dark/Light Toggle
<!-- Theme Toggle Button -->
<button id="theme-toggle" class="btn-ghost">
<span id="theme-icon">🌙</span>
</button>
<script>
const themeToggle = document.getElementById('theme-toggle');
const themeIcon = document.getElementById('theme-icon');
const html = document.documentElement;
// Load saved theme or default to dark
const savedTheme = localStorage.getItem('theme') || 'dark';
html.setAttribute('data-theme', savedTheme);
updateIcon(savedTheme);
// Toggle theme on click
themeToggle.addEventListener('click', () => {
const currentTheme = html.getAttribute('data-theme');
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
html.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
updateIcon(newTheme);
});
function updateIcon(theme) {
themeIcon.textContent = theme === 'dark' ? '☀️' : '🌙';
}
</script>
Responsive Design
Built-in Breakpoints
Tailwind Breakpoints
sm:- 640px and upmd:- 768px and uplg:- 1024px and upxl:- 1280px and up
Responsive Examples
<!-- Responsive Grid -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<!-- Mobile: 1 column, Tablet: 2 columns, Desktop: 4 columns -->
<div class="card-base">Card 1</div>
<div class="card-base">Card 2</div>
<div class="card-base">Card 3</div>
<div class="card-base">Card 4</div>
</div>
<!-- Responsive Text -->
<h1 class="text-2xl sm:text-3xl lg:text-5xl font-bold">
Responsive Heading
</h1>
<!-- Hide/Show on Different Screens -->
<div class="block lg:hidden">Mobile Menu</div>
<div class="hidden lg:block">Desktop Menu</div>
Troubleshooting & FAQ
Solution:
- Run
npm run generate:theme - Run
npm run build:css - Clear browser cache (Ctrl+Shift+R)
- Check that
site.cssis loaded in your HTML
Checklist:
- Verify the class exists in
tailwind.css - Check for typos in class name
- Ensure CSS is compiled:
npm run build:css - Inspect element in browser DevTools to see applied styles
// In tailwind.theme.js, add to brandColors or semanticColors:
brandColors: {
// ...existing colors
tertiary: {
DEFAULT: '#your-color',
hover: '#your-hover-color',
light: '#your-light-color',
dark: '#your-dark-color',
}
}
// Then regenerate: npm run generate:theme
Check these:
- HTML should have
data-theme="dark"ordata-theme="light"attribute - JavaScript theme switcher is working
- Both dark and light modes are defined in
tailwind.theme.js
Common Build Commands
| Command | Purpose |
|---|---|
npm run generate:theme |
Generate CSS variables from theme config |
npm run build:css |
Compile Tailwind to site.css |
npm run watch:css |
Watch for changes and auto-compile |
npm run build:all |
Generate theme + build CSS in one command |
Files you SHOULD edit
tailwind.theme.js- Theme colors and configurationtailwind.css- Add custom component classestailwind.public.config.js- Tailwind settings (advanced)
Files you should NOT edit manually
_theme-vars.css- Auto-generated from theme.jssite.css- Compiled output file
CSS Variable Reference
All these variables are available for use in your custom CSS:
| Variable | Usage | Example |
|---|---|---|
--color-primary |
Primary brand color (RGB) | rgb(var(--color-primary)) |
--color-secondary |
Secondary color (RGB) | rgb(var(--color-secondary)) |
--theme-page-background |
Page background (RGB) | rgb(var(--theme-page-background)) |
--theme-text-primary |
Primary text color (RGB) | rgb(var(--theme-text-primary)) |
--theme-card-background |
Card background (RGB) | rgb(var(--theme-card-background)) |
--color-success |
Success state color (RGB) | rgb(var(--color-success)) |
--color-error |
Error state color (RGB) | rgb(var(--color-error)) |
Note
Variables are in RGB format without the rgb() wrapper, so always use rgb(var(--variable-name)) or rgba(var(--variable-name), opacity)
Getting Help
Common Issues & Solutions
Issue: "Cannot find module 'tailwindcss'"
Solution:
npm install tailwindcss --save-dev
Issue: "Scripts not found"
Solution: Ensure package.json has correct scripts:
{
"scripts": {
"generate:theme": "node scripts/generate-theme-vars.js",
"build:css": "tailwindcss -c tailwind.public.config.js -i wwwroot/css/tailwind.css -o wwwroot/css/site.css"
}
}
Issue: "Colors not showing"
Checklist:
- Run
npm run generate:theme - Run
npm run build:css - Check HTML has
<link rel="stylesheet" href="/css/site.css"> - Clear browser cache (Ctrl+Shift+R)
- Check DevTools for CSS loading errors
Debugging Checklist
When something doesn't work:
- ✅ Check browser console for errors
- ✅ Inspect element in DevTools
- ✅ Verify class name spelling
- ✅ Check if site.css is loaded
- ✅ Verify CSS was compiled recently
- ✅ Test in incognito mode (clear cache)
- ✅ Check if theme attribute is set on HTML tag
Need More Help?
Contact channels:
- Email: dev-support@yourcompany.com
- Slack: #frontend-help channel
- Internal Wiki: wiki.yourcompany.com/theme-system
- Create a ticket in Jira
Quick Summary
For New Developers - Start Here!
1️⃣ Understanding the System
- tailwind.theme.js = Source of truth for colors and theme
- generate-theme-vars.js = Converts theme to CSS variables
- tailwind.css = Pre-built component classes
- site.css = Final compiled CSS (use this in HTML)
2️⃣ Using Components in HTML
<!-- Just add the class names -->
<button class="btn-primary">Click Me</button>
<div class="card-hover">...</div>
<input class="input-base" />
<span class="badge-success">Active</span>
3️⃣ Changing Theme Colors
- Open
tailwind.theme.js - Edit the
brandColorssection - Run
npm run generate:theme - Run
npm run build:css - Done! Colors update everywhere automatically
4️⃣ Adding Custom Components
- Open
wwwroot/css/tailwind.css - Add your component in
@layer components { } - Use CSS variables:
rgb(var(--color-primary)) - Run
npm run build:css - Use your new class in HTML
5️⃣ Daily Development
# Start watch mode
npm run watch:css
# Edit HTML with component classes
# CSS auto-compiles on save
# When changing theme colors:
npm run generate:theme
npm run build:css
Key Takeaways
-
NEVER edit
_theme-vars.cssorsite.cssmanually -
ALWAYS edit colors in
tailwind.theme.js - USE pre-built component classes for faster development
-
ADD custom components to
tailwind.css - REGENERATE theme vars and rebuild CSS after theme changes