Initial commit
This commit is contained in:
+546
@@ -0,0 +1,546 @@
|
||||
# Next Steps - Your Powder Coating Application is Ready!
|
||||
|
||||
## 🎉 Current Status: BUILD SUCCESSFUL! ✅
|
||||
|
||||
Congratulations! Your application builds without errors. Here's your roadmap to get it running and start building features.
|
||||
|
||||
---
|
||||
|
||||
## 🚀 IMMEDIATE ACTION: Get the App Running (Do This First!)
|
||||
|
||||
### Step 1: Create the Database (5 minutes)
|
||||
|
||||
```bash
|
||||
cd src/PowderCoating.Web
|
||||
|
||||
# Create the initial migration
|
||||
dotnet ef migrations add InitialCreate --project ../PowderCoating.Infrastructure
|
||||
|
||||
# Apply it to create the database
|
||||
dotnet ef database update --project ../PowderCoating.Infrastructure
|
||||
```
|
||||
|
||||
**✅ What This Does:**
|
||||
- Creates `PowderCoatingDb` database in SQL Express
|
||||
- Creates 25+ tables (Customers, Jobs, Quotes, Inventory, etc.)
|
||||
- Seeds 3 pricing tiers (Standard, Preferred, Premium)
|
||||
- Creates 5 roles (Administrator, Manager, Employee, ShopFloor, ReadOnly)
|
||||
- Creates admin user: `admin@powdercoating.com` / `Admin123!`
|
||||
|
||||
### Step 2: Run the Application (1 minute)
|
||||
|
||||
```bash
|
||||
# Still in src/PowderCoating.Web
|
||||
dotnet run
|
||||
```
|
||||
|
||||
**You should see:**
|
||||
```
|
||||
info: Now listening on: https://localhost:7001
|
||||
```
|
||||
|
||||
### Step 3: Login and Verify (2 minutes)
|
||||
|
||||
1. Open browser: **https://localhost:7001**
|
||||
2. Click **Login** (top right)
|
||||
3. Use credentials:
|
||||
- Email: `admin@powdercoating.com`
|
||||
- Password: `Admin123!`
|
||||
|
||||
**✅ Success!** You should be logged in as administrator!
|
||||
|
||||
---
|
||||
|
||||
## 📋 Next: Build Your First Feature (Customer Management)
|
||||
|
||||
Now let's add actual functionality. Start with **Customer Management** because every job needs a customer.
|
||||
|
||||
### Create the Customers Controller
|
||||
|
||||
Create file: `src/PowderCoating.Web/Controllers/CustomersController.cs`
|
||||
|
||||
```csharp
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using AutoMapper;
|
||||
using PowderCoating.Core.Interfaces;
|
||||
using PowderCoating.Core.Entities;
|
||||
using PowderCoating.Application.DTOs.Customer;
|
||||
|
||||
namespace PowderCoating.Web.Controllers;
|
||||
|
||||
[Authorize]
|
||||
public class CustomersController : Controller
|
||||
{
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly ILogger<CustomersController> _logger;
|
||||
|
||||
public CustomersController(
|
||||
IUnitOfWork unitOfWork,
|
||||
IMapper mapper,
|
||||
ILogger<CustomersController> logger)
|
||||
{
|
||||
_unitOfWork = unitOfWork;
|
||||
_mapper = mapper;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
// GET: Customers
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var customers = await _unitOfWork.Customers.GetAllAsync();
|
||||
var customerDtos = _mapper.Map<List<CustomerListDto>>(customers);
|
||||
return View(customerDtos);
|
||||
}
|
||||
|
||||
// GET: Customers/Details/5
|
||||
public async Task<IActionResult> Details(int id)
|
||||
{
|
||||
var customer = await _unitOfWork.Customers.GetByIdAsync(id);
|
||||
if (customer == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var customerDto = _mapper.Map<CustomerDto>(customer);
|
||||
return View(customerDto);
|
||||
}
|
||||
|
||||
// GET: Customers/Create
|
||||
public IActionResult Create()
|
||||
{
|
||||
return View(new CreateCustomerDto());
|
||||
}
|
||||
|
||||
// POST: Customers/Create
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Create(CreateCustomerDto dto)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(dto);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var customer = _mapper.Map<Customer>(dto);
|
||||
await _unitOfWork.Customers.AddAsync(customer);
|
||||
await _unitOfWork.SaveChangesAsync();
|
||||
|
||||
TempData["Success"] = "Customer created successfully!";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error creating customer");
|
||||
ModelState.AddModelError("", "An error occurred while creating the customer.");
|
||||
return View(dto);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: Customers/Edit/5
|
||||
public async Task<IActionResult> Edit(int id)
|
||||
{
|
||||
var customer = await _unitOfWork.Customers.GetByIdAsync(id);
|
||||
if (customer == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var dto = _mapper.Map<UpdateCustomerDto>(customer);
|
||||
return View(dto);
|
||||
}
|
||||
|
||||
// POST: Customers/Edit/5
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Edit(int id, UpdateCustomerDto dto)
|
||||
{
|
||||
if (id != dto.Id)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(dto);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var customer = await _unitOfWork.Customers.GetByIdAsync(id);
|
||||
if (customer == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
_mapper.Map(dto, customer);
|
||||
await _unitOfWork.Customers.UpdateAsync(customer);
|
||||
await _unitOfWork.SaveChangesAsync();
|
||||
|
||||
TempData["Success"] = "Customer updated successfully!";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error updating customer {CustomerId}", id);
|
||||
ModelState.AddModelError("", "An error occurred while updating the customer.");
|
||||
return View(dto);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: Customers/Delete/5
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
var customer = await _unitOfWork.Customers.GetByIdAsync(id);
|
||||
if (customer == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var dto = _mapper.Map<CustomerDto>(customer);
|
||||
return View(dto);
|
||||
}
|
||||
|
||||
// POST: Customers/Delete/5
|
||||
[HttpPost, ActionName("Delete")]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> DeleteConfirmed(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _unitOfWork.Customers.SoftDeleteAsync(id);
|
||||
await _unitOfWork.SaveChangesAsync();
|
||||
|
||||
TempData["Success"] = "Customer deleted successfully!";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error deleting customer {CustomerId}", id);
|
||||
TempData["Error"] = "An error occurred while deleting the customer.";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Add Missing AutoMapper Mappings
|
||||
|
||||
The `UpdateCustomerDto` mappings are missing. Update `CustomerProfile.cs`:
|
||||
|
||||
```csharp
|
||||
public class CustomerProfile : Profile
|
||||
{
|
||||
public CustomerProfile()
|
||||
{
|
||||
CreateMap<Customer, CustomerDto>()
|
||||
.ForMember(dest => dest.PricingTierName,
|
||||
opt => opt.MapFrom(src => src.PricingTier != null ? src.PricingTier.TierName : null));
|
||||
|
||||
CreateMap<CreateCustomerDto, Customer>();
|
||||
|
||||
CreateMap<UpdateCustomerDto, Customer>(); // ← ADD THIS
|
||||
|
||||
CreateMap<Customer, UpdateCustomerDto>(); // ← ADD THIS TOO
|
||||
|
||||
CreateMap<Customer, CustomerListDto>()
|
||||
.ForMember(dest => dest.ContactName,
|
||||
opt => opt.MapFrom(src => !string.IsNullOrEmpty(src.ContactFirstName) || !string.IsNullOrEmpty(src.ContactLastName)
|
||||
? $"{src.ContactFirstName} {src.ContactLastName}".Trim()
|
||||
: string.Empty));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Create a Basic Index View
|
||||
|
||||
Create file: `src/PowderCoating.Web/Views/Customers/Index.cshtml`
|
||||
|
||||
```html
|
||||
@model List<PowderCoating.Application.DTOs.Customer.CustomerListDto>
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Customers";
|
||||
}
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
<h2>Customers</h2>
|
||||
</div>
|
||||
<div class="col text-end">
|
||||
<a asp-action="Create" class="btn btn-primary">
|
||||
<i class="bi bi-plus-circle"></i> Add New Customer
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (TempData["Success"] != null)
|
||||
{
|
||||
<div class="alert alert-success alert-dismissible fade show">
|
||||
@TempData["Success"]
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
@if (!Model.Any())
|
||||
{
|
||||
<p class="text-muted">No customers found. Click "Add New Customer" to get started.</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Company Name</th>
|
||||
<th>Contact</th>
|
||||
<th>Email</th>
|
||||
<th>Phone</th>
|
||||
<th>Type</th>
|
||||
<th>Balance</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var customer in Model)
|
||||
{
|
||||
<tr>
|
||||
<td>@customer.CompanyName</td>
|
||||
<td>@customer.ContactName</td>
|
||||
<td>@customer.Email</td>
|
||||
<td>@customer.Phone</td>
|
||||
<td>
|
||||
@if (customer.IsCommercial)
|
||||
{
|
||||
<span class="badge bg-primary">Commercial</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="badge bg-secondary">Non-Commercial</span>
|
||||
}
|
||||
</td>
|
||||
<td>@customer.CurrentBalance.ToString("C")</td>
|
||||
<td>
|
||||
@if (customer.IsActive)
|
||||
{
|
||||
<span class="badge bg-success">Active</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="badge bg-danger">Inactive</span>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
<a asp-action="Details" asp-route-id="@customer.Id" class="btn btn-sm btn-info">Details</a>
|
||||
<a asp-action="Edit" asp-route-id="@customer.Id" class="btn btn-sm btn-warning">Edit</a>
|
||||
<a asp-action="Delete" asp-route-id="@customer.Id" class="btn btn-sm btn-danger">Delete</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Add to Navigation Menu
|
||||
|
||||
Edit `src/PowderCoating.Web/Views/Shared/_Layout.cshtml` and add the Customers link:
|
||||
|
||||
```html
|
||||
<ul class="navbar-nav flex-grow-1">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-controller="Customers" asp-action="Index">Customers</a>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
### Test It!
|
||||
|
||||
1. Run the app: `dotnet run`
|
||||
2. Navigate to: https://localhost:7001/Customers
|
||||
3. You should see an empty customer list with "Add New Customer" button
|
||||
|
||||
---
|
||||
|
||||
## 📋 Recommended Development Order
|
||||
|
||||
Build features in this sequence for maximum value:
|
||||
|
||||
### Week 1: Core Data Management
|
||||
1. ✅ **Customers** (you just started this!)
|
||||
- Complete Create/Edit forms
|
||||
- Add validation
|
||||
- Test CRUD operations
|
||||
|
||||
2. **Inventory Items**
|
||||
- Powder colors and materials
|
||||
- SKU tracking
|
||||
- Reorder alerts
|
||||
|
||||
3. **Suppliers**
|
||||
- Manage powder suppliers
|
||||
- Contact information
|
||||
|
||||
### Week 2: Job Quoting
|
||||
4. **Quotes**
|
||||
- Create quotes for customers
|
||||
- Line items with pricing
|
||||
- Quote templates
|
||||
|
||||
5. **Quote to Job Conversion**
|
||||
- Approve quote → Create job
|
||||
- Transfer all details
|
||||
|
||||
### Week 3: Job Management
|
||||
6. **Jobs**
|
||||
- Job creation
|
||||
- Status workflow (15 stages)
|
||||
- Job items
|
||||
|
||||
7. **Job Assignment**
|
||||
- Assign to employees
|
||||
- Track progress
|
||||
|
||||
8. **Job Photos & Notes**
|
||||
- Upload before/after photos
|
||||
- Internal and customer notes
|
||||
|
||||
### Week 4: Shop Floor
|
||||
9. **Shop Floor Display**
|
||||
- Real-time job board
|
||||
- Color-coded by priority
|
||||
- TV-optimized view
|
||||
|
||||
10. **SignalR Integration**
|
||||
- Real-time status updates
|
||||
- Auto-refresh displays
|
||||
|
||||
### Week 5+: Advanced Features
|
||||
11. **Equipment Management**
|
||||
12. **Maintenance Tracking**
|
||||
13. **Reporting & Analytics**
|
||||
14. **AI-Powered Quoting**
|
||||
15. **Customer Portal**
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Essential Commands Reference
|
||||
|
||||
### Database Commands:
|
||||
```bash
|
||||
# Add migration after changing entities
|
||||
dotnet ef migrations add MigrationName --project src/PowderCoating.Infrastructure --startup-project src/PowderCoating.Web
|
||||
|
||||
# Update database
|
||||
dotnet ef database update --project src/PowderCoating.Infrastructure --startup-project src/PowderCoating.Web
|
||||
|
||||
# Rollback one migration
|
||||
dotnet ef database update PreviousMigration --project src/PowderCoating.Infrastructure --startup-project src/PowderCoating.Web
|
||||
|
||||
# List all migrations
|
||||
dotnet ef migrations list --project src/PowderCoating.Infrastructure --startup-project src/PowderCoating.Web
|
||||
```
|
||||
|
||||
### Development:
|
||||
```bash
|
||||
# Run with auto-reload
|
||||
dotnet watch run
|
||||
|
||||
# Build
|
||||
dotnet build
|
||||
|
||||
# Clean
|
||||
dotnet clean
|
||||
|
||||
# Run tests
|
||||
dotnet test
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 Development Tips
|
||||
|
||||
### 1. Use `dotnet watch run`
|
||||
Auto-reloads when you save files - huge time saver!
|
||||
|
||||
### 2. Check the Logs
|
||||
Serilog writes to:
|
||||
- Console (see terminal)
|
||||
- File: `logs/powdercoating-YYYYMMDD.txt`
|
||||
|
||||
### 3. Bootstrap is Ready
|
||||
Use Bootstrap 5 classes:
|
||||
- Tables: `.table`, `.table-striped`, `.table-hover`
|
||||
- Buttons: `.btn`, `.btn-primary`, `.btn-sm`
|
||||
- Cards: `.card`, `.card-body`
|
||||
- Forms: `.form-control`, `.form-label`
|
||||
|
||||
### 4. Test Incrementally
|
||||
Test each feature immediately after building it. Don't wait!
|
||||
|
||||
### 5. Commit Often
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "Add customer management"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Your Immediate To-Do List
|
||||
|
||||
- [ ] Create database (Step 1 above)
|
||||
- [ ] Run the app and login (Steps 2-3)
|
||||
- [ ] Create CustomersController
|
||||
- [ ] Add missing AutoMapper mappings
|
||||
- [ ] Create Index view
|
||||
- [ ] Add navigation menu item
|
||||
- [ ] Test viewing empty customer list
|
||||
- [ ] Create the Create.cshtml form
|
||||
- [ ] Test adding your first customer
|
||||
- [ ] Create Edit and Details views
|
||||
- [ ] Test full CRUD operations
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Success Criteria
|
||||
|
||||
You'll know you're on track when:
|
||||
|
||||
✅ Database created successfully
|
||||
✅ Can login as admin
|
||||
✅ Can navigate to /Customers
|
||||
✅ See empty list with "Add New Customer" button
|
||||
✅ Can create a customer
|
||||
✅ Customer appears in the list
|
||||
✅ Can edit the customer
|
||||
✅ Can view customer details
|
||||
✅ Can delete (soft delete) the customer
|
||||
|
||||
---
|
||||
|
||||
## 🚀 You're Ready to Build!
|
||||
|
||||
You have:
|
||||
- ✅ Solid architecture (Clean Architecture pattern)
|
||||
- ✅ Database ready (Entity Framework Core)
|
||||
- ✅ Authentication working (ASP.NET Identity)
|
||||
- ✅ AutoMapper configured
|
||||
- ✅ Both Web and API projects
|
||||
- ✅ Repository pattern implemented
|
||||
- ✅ Logging set up (Serilog)
|
||||
|
||||
**Start with the Customer Management module and you'll be up and running in no time!**
|
||||
|
||||
Need help with specific features? Just ask! 🎉
|
||||
Reference in New Issue
Block a user