using System.ComponentModel.DataAnnotations;
using PowderCoating.Core.Enums;
namespace PowderCoating.Application.DTOs.Wizard;
///
/// Tracks the current user's wizard progress (read from CompanyPreferences)
///
public class WizardProgressDto
{
public bool Started { get; set; }
public bool Completed { get; set; }
public List DoneSteps { get; set; } = new();
public List SkippedSteps { get; set; } = new();
public const int TotalSteps = 5;
public bool IsStepDone(int step) => DoneSteps.Contains(step);
public bool IsStepSkipped(int step) => SkippedSteps.Contains(step);
public bool IsStepTouched(int step) => IsStepDone(step) || IsStepSkipped(step);
// Capped at TotalSteps so old step data from a larger wizard doesn't overflow the display.
public int CompletedCount => Math.Min(DoneSteps.Count + SkippedSteps.Count, TotalSteps);
public int ProgressPercent => TotalSteps == 0 ? 0 : (int)Math.Round((double)CompletedCount / TotalSteps * 100);
}
// ─── Step 1: Company Profile ────────────────────────────────────────────────
public class WizardStep1Dto
{
[Required, MaxLength(200)]
[Display(Name = "Company Name")]
public string CompanyName { get; set; } = string.Empty;
[MaxLength(200)]
[Display(Name = "Primary Contact Name")]
public string? PrimaryContactName { get; set; }
[EmailAddress, MaxLength(200)]
[Display(Name = "Primary Contact Email")]
public string? PrimaryContactEmail { get; set; }
[Phone, MaxLength(50)]
[Display(Name = "Phone")]
public string? Phone { get; set; }
[MaxLength(300)]
[Display(Name = "Address")]
public string? Address { get; set; }
[MaxLength(100)]
[Display(Name = "City")]
public string? City { get; set; }
[MaxLength(100)]
[Display(Name = "State")]
public string? State { get; set; }
[MaxLength(20)]
[Display(Name = "ZIP Code")]
public string? ZipCode { get; set; }
[MaxLength(100)]
[Display(Name = "Time Zone")]
public string? TimeZone { get; set; }
[MaxLength(10)]
[Display(Name = "Default Currency")]
public string DefaultCurrency { get; set; } = "USD";
[Display(Name = "Use Metric System (metres / kilograms)")]
public bool UseMetricSystem { get; set; } = false;
}
// ─── Step 2: QuickBooks Migration ───────────────────────────────────────────
public class WizardStep2QbDto
{
public bool MigratingFromQuickBooks { get; set; } = false;
}
// ─── Step 3: Operating Costs (was Step 2) ───────────────────────────────────
public class WizardStep2Dto
{
[Range(0, 10000)]
[Display(Name = "Standard Labor Rate ($/hr)")]
public decimal StandardLaborRate { get; set; }
[Range(0, 10000)]
[Display(Name = "Sandblaster Cost ($/hr)")]
public decimal SandblasterCostPerHour { get; set; }
[Range(0, 10000)]
[Display(Name = "Coating Booth Cost ($/hr)")]
public decimal CoatingBoothCostPerHour { get; set; }
[Range(0, 10000)]
[Display(Name = "Oven Operating Cost ($/hr)")]
public decimal OvenOperatingCostPerHour { get; set; }
[Range(0, 1000)]
[Display(Name = "Powder Coating Cost ($/sq ft)")]
public decimal PowderCoatingCostPerSqFt { get; set; }
[Range(0, 100)]
[Display(Name = "General Markup (%)")]
public decimal GeneralMarkupPercentage { get; set; }
[Range(0, 100)]
[Display(Name = "Tax Rate (%)")]
public decimal TaxPercent { get; set; }
[Range(0, 100000)]
[Display(Name = "Shop Minimum Charge ($)")]
public decimal ShopMinimumCharge { get; set; }
[Display(Name = "Shop Capability Tier")]
public ShopCapabilityTier ShopCapabilityTier { get; set; } = ShopCapabilityTier.Small;
// Facility Overhead
[Range(0, 1000000)]
[Display(Name = "Monthly Rent ($)")]
public decimal MonthlyRent { get; set; } = 0m;
[Range(0, 1000000)]
[Display(Name = "Monthly Utilities ($)")]
public decimal MonthlyUtilities { get; set; } = 0m;
[Range(1, 10000)]
[Display(Name = "Billable Hours/Month")]
public int MonthlyBillableHours { get; set; } = 160;
}
// ─── Step 3: Branding & Numbering ───────────────────────────────────────────
public class WizardStep3Dto
{
[MaxLength(10)]
[Display(Name = "Quote Number Prefix")]
public string QuoteNumberPrefix { get; set; } = "QT";
[MaxLength(10)]
[Display(Name = "Job Number Prefix")]
public string JobNumberPrefix { get; set; } = "JOB";
[MaxLength(10)]
[Display(Name = "Invoice Number Prefix")]
public string InvoiceNumberPrefix { get; set; } = "INV";
[MaxLength(7)]
[Display(Name = "Quote Accent Color")]
public string QtAccentColor { get; set; } = "#374151";
[MaxLength(7)]
[Display(Name = "Invoice Accent Color")]
public string InAccentColor { get; set; } = "#374151";
[MaxLength(7)]
[Display(Name = "Work Order Accent Color")]
public string WoAccentColor { get; set; } = "#374151";
}
// ─── Step 4: Invoice & Quote Defaults ───────────────────────────────────────
public class WizardStep4Dto
{
[MaxLength(100)]
[Display(Name = "Default Payment Terms")]
public string DefaultPaymentTerms { get; set; } = "Net 30";
[Range(1, 365)]
[Display(Name = "Quote Validity (days)")]
public int DefaultQuoteValidityDays { get; set; } = 30;
[Range(1, 365)]
[Display(Name = "Default Turnaround (days)")]
public int DefaultTurnaroundDays { get; set; } = 7;
[MaxLength(2000)]
[Display(Name = "Default Quote Terms & Conditions")]
public string? QtDefaultTerms { get; set; }
[MaxLength(500)]
[Display(Name = "Quote Footer Note")]
public string? QtFooterNote { get; set; }
}
// ─── Step 5: Job & Workflow ──────────────────────────────────────────────────
public class WizardStep5Dto
{
[Display(Name = "Default Job Priority")]
public string DefaultJobPriority { get; set; } = "Normal";
[Display(Name = "Require Customer PO Number")]
public bool RequireCustomerPO { get; set; } = false;
[Display(Name = "Allow Customer Approval via Link")]
public bool AllowCustomerApproval { get; set; } = true;
}
// ─── Step 6: Chart of Accounts (review only) ────────────────────────────────
public class WizardStep6Dto
{
public int AccountCount { get; set; }
public int RevenueAccounts { get; set; }
public int ExpenseAccounts { get; set; }
public int AssetAccounts { get; set; }
}
// ─── Step 7: Notification Preferences ───────────────────────────────────────
public class WizardStep7Dto
{
[Display(Name = "Enable Email Notifications")]
public bool EmailNotificationsEnabled { get; set; } = true;
[EmailAddress, MaxLength(200)]
[Display(Name = "Send Emails From Address")]
public string? EmailFromAddress { get; set; }
[MaxLength(200)]
[Display(Name = "From Display Name")]
public string? EmailFromName { get; set; }
[Display(Name = "Notify on New Job Created")]
public bool NotifyOnNewJob { get; set; } = true;
[Display(Name = "Notify on New Quote Created")]
public bool NotifyOnNewQuote { get; set; } = true;
[Display(Name = "Notify on Job Status Change")]
public bool NotifyOnJobStatusChange { get; set; } = true;
[Display(Name = "Notify on Quote Approval")]
public bool NotifyOnQuoteApproval { get; set; } = true;
[Display(Name = "Notify on Payment Received")]
public bool NotifyOnPaymentReceived { get; set; } = true;
[Display(Name = "Enable Automated Payment Reminders")]
public bool PaymentRemindersEnabled { get; set; } = false;
[MaxLength(50)]
[Display(Name = "Reminder Days (comma-separated, days past due)")]
public string PaymentReminderDays { get; set; } = "7,14,30";
// Alert Thresholds
[Range(0, 90)]
[Display(Name = "Quote Expiry Warning (days before expiry)")]
public int QuoteExpiryWarningDays { get; set; } = 3;
[Range(0, 90)]
[Display(Name = "Due Date Warning (days before due)")]
public int DueDateWarningDays { get; set; } = 2;
[Range(0, 90)]
[Display(Name = "Maintenance Alert (days before scheduled date)")]
public int MaintenanceAlertDays { get; set; } = 7;
}
// ─── Step 8: Inventory / Powder Colors ──────────────────────────────────────
public class WizardStep8Dto
{
/// JSON array of WizardInventoryItemDto submitted as a hidden field
public string? ItemsJson { get; set; }
}
public class WizardInventoryItemDto
{
public string Name { get; set; } = string.Empty;
public string? ColorName { get; set; }
public string? ColorCode { get; set; }
public string? Manufacturer { get; set; }
public string? Finish { get; set; }
public decimal UnitCost { get; set; }
public decimal QuantityOnHand { get; set; }
}
// ─── Step 9: Team Members ────────────────────────────────────────────────────
public class WizardStep9Dto
{
/// JSON array of WizardTeamMemberDto submitted as a hidden field
public string? MembersJson { get; set; }
}
public class WizardTeamMemberDto
{
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
public string CompanyRole { get; set; } = "Worker";
}
// ─── Step 10: Vendors & Suppliers ────────────────────────────────────────────
public class WizardStep10Dto
{
/// JSON array of WizardVendorDto submitted as a hidden field
public string? VendorsJson { get; set; }
}
public class WizardVendorDto
{
public string CompanyName { get; set; } = string.Empty;
public string? ContactName { get; set; }
public string? Email { get; set; }
public string? Phone { get; set; }
public string? Website { get; set; }
}
// ─── Step 14: Equipment / Named Ovens + Blast Setups ────────────────────────
public class WizardOvensStepDto
{
/// JSON array of WizardOvenDto submitted as a hidden field
public string? OvensJson { get; set; }
/// JSON array of WizardBlastSetupDto submitted as a hidden field
public string? BlastSetupsJson { get; set; }
}
public class WizardOvenDto
{
public int Id { get; set; }
public string Label { get; set; } = string.Empty;
public decimal CostPerHour { get; set; }
public decimal? MaxLoadSqFt { get; set; }
public int? DefaultCycleMinutes { get; set; }
}
public class WizardBlastSetupDto
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
/// Maps to BlastSetupType enum: 0=SiphonCabinet, 1=SiphonPot, 2=PressurePot, 3=WetBlasting
public int SetupType { get; set; } = 2;
public decimal CompressorCfm { get; set; }
/// Nozzle orifice number 2–8.
public int BlastNozzleSize { get; set; } = 5;
/// Maps to BlastSubstrateType enum: 0=Paint, 1=PowderCoat, 2=RustAndScale, 3=Mixed
public int PrimarySubstrate { get; set; } = 3;
public decimal? BlastRateSqFtPerHourOverride { get; set; }
public bool IsDefault { get; set; }
}
// ─── Step 15: Pricing Tiers ──────────────────────────────────────────────────
public class WizardPricingTiersStepDto
{
/// JSON array of WizardPricingTierDto submitted as a hidden field
public string? TiersJson { get; set; }
}
public class WizardPricingTierDto
{
public int Id { get; set; }
public string TierName { get; set; } = string.Empty;
public string? Description { get; set; }
public decimal DiscountPercent { get; set; }
}
// ─── Step 16: Service Catalog ────────────────────────────────────────────────
public class WizardCatalogStepDto
{
/// JSON array of WizardCatalogItemDto submitted as a hidden field
public string? ItemsJson { get; set; }
}
public class WizardCatalogItemDto
{
public string Name { get; set; } = string.Empty;
public string? Description { get; set; }
public decimal DefaultPrice { get; set; }
public decimal? ApproximateArea { get; set; }
}