Implement Phases 2-5: Frontend, AI/RAG, PDF Generation (#22)
* Fix NuGet versions and add InitialCreate EF Core migration
- Update AWSSDK.SQS and AWSSDK.SecretsManager to 3.7.500.0 (actual available versions)
- Update AWSSDK.Extensions.NETCore.Setup to 3.7.400
- Generate InitialCreate migration for PostgreSQL (all 8 entities)
- Build verified: 0 errors, 0 warnings
* Implement Dispatcher Frontend (Phase 2)
React 19 + MUI v7 + TypeScript + Vite SPA matching SHOC patterns:
Redux Toolkit (auth/ui slices), TanStack React Query, axios interceptors,
react-toastify, Cognito OAuth PKCE login flow, paginated proposal list,
new proposal form with customer autocomplete and vendor PDF upload,
read-only proposal detail with status stepper timeline.
* Add AuthController for Cognito code exchange and .env.example
Backend endpoint POST /api/auth/callback exchanges the OAuth
authorization code with Cognito's token endpoint, auto-provisions
the user in the DB, and returns the access token to the frontend.
* Implement Admin Frontend Experience (Phase 3)
Three-panel admin workspace: left reference panel (submission details,
vendor data), center editor (refined scope, inline line item table with
reorder/add/remove/pricing), right similar proposals panel (KB results
with pull-to-editor). Admin dashboard with stats cards and proposal
queue table. Approval flow with confirmation dialog, mark-as-sent,
and create-revision actions. Role-based sidebar navigation.
* Implement backend dev mode, internal API auth, and service layer enhancements
- Add dev-login endpoint with local JWT signing for local development
- Add InternalApiKeyMiddleware with timing-safe comparison for Lambda-to-API auth
- Add DevS3Service and NoOpJobPublisher for running without AWS services
- Implement CurrentUserService cascading user resolution (ID → sub → email → create)
- Add async ResolveAsync() to avoid synchronous DB calls in request pipeline
- Add /proposals/stats endpoint for efficient server-side status counts
- Guard status transitions: only allow Draft → InReview via update endpoint
- Add vendor proposals, generated PDFs, and similar proposals controllers
- Add ISimilarProposalService and SimilarProposalService
- Add [Authorize] to AddSimilarReference endpoint
* Implement Lambda functions for PDF processing, suggestions, and library ingest
- pdf-extract: Parse vendor PDFs with pdfplumber, fallback to Claude multimodal
- pdf-generate: Generate branded proposal PDFs with reportlab Platypus
- library-ingest: Format approved proposals as markdown and sync to Bedrock KB
- suggestions: Query KB for similar proposals, generate line items via Claude
- All Lambdas use internal API key auth and cold-start secret caching
- Fix pdf_path unbound variable in pdf-extract error handling
* Add Bedrock Knowledge Base, OpenSearch Serverless, and SQS message filtering
- Provision OpenSearch Serverless collection for vector search
- Create Bedrock Knowledge Base with Titan embedding model
- Configure S3 data source with fixed-size chunking (512 tokens, 20% overlap)
- Add suggestions Lambda with SQS event source filtering
- Scope bedrock:InvokeModel IAM to specific model ARN patterns
- Add internal API key secret in Secrets Manager
- Add log retention (2 months) to all Lambda functions
- Add docker-compose.yml for local PostgreSQL
* Apply SHOC design system styling across frontend
- Rewrite theme with SHOC palette (#0c4f6f primary, Nunito font, 4px radius)
- Add global CSS with Google Fonts import for Nunito
- Redesign Topbar with avatar initials, role subtitle, gradient header
- Redesign Sidebar with 220px width, section headers, active state border
- Restyle LoginPage with SHOC branded card and dev-mode role selector
- Update AdminDashboard KPI cards to centered SHOC style
- Add devLogin API method for local development auth flow
* Fix frontend navigation bugs, differentiate Dashboard from Proposals list
- Fix double nav selection by adding isNavActive() with ALL_NAV_PATHS set
- Fix /admin/users routing to placeholder instead of redirect to /
- Fix ProposalDetailPage Back button navigating to / instead of /proposals
- Differentiate Dashboard (KPI cards + recent 5) from ProposalListPage (full paginated table)
- Dashboard now uses dedicated /proposals/stats endpoint for accurate counts
- Fix adminApi.getPdf dead code (axios rejects before status check)
- Wire up PDF generation button in AdminWorkspace
- Adjust layout: 220px drawer, 10px content padding, 64px toolbar height
* Add appsettings.Development.json to gitignore
Prevent dev-only signing keys and connection strings from being committed.
* Fix CI failures: unused Python imports and CDK synth asset path
CDK synth job needs the .NET API published first so the Lambda asset
path exists. Python lint had 3 unused imports in pdf-generate.
* Apply ruff formatting to all Lambda Python files
2026-05-17 13:06:23 -04:00
|
|
|
|
// <auto-generated />
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
|
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
|
|
|
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
|
|
|
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
|
|
|
|
using ProposalSystem.Infrastructure.Data;
|
|
|
|
|
|
|
|
|
|
|
|
#nullable disable
|
|
|
|
|
|
|
|
|
|
|
|
namespace ProposalSystem.Infrastructure.Data.Migrations
|
|
|
|
|
|
{
|
|
|
|
|
|
[DbContext(typeof(ProposalDbContext))]
|
|
|
|
|
|
partial class ProposalDbContextModelSnapshot : ModelSnapshot
|
|
|
|
|
|
{
|
|
|
|
|
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
|
|
|
|
|
{
|
|
|
|
|
|
#pragma warning disable 612, 618
|
|
|
|
|
|
modelBuilder
|
|
|
|
|
|
.HasAnnotation("ProductVersion", "8.0.11")
|
|
|
|
|
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
|
|
|
|
|
|
|
|
|
|
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
|
|
|
|
|
|
|
|
|
|
|
modelBuilder.Entity("ProposalSystem.Domain.Entities.AuditLog", b =>
|
|
|
|
|
|
{
|
|
|
|
|
|
b.Property<Guid>("Id")
|
|
|
|
|
|
.ValueGeneratedOnAdd()
|
|
|
|
|
|
.HasColumnType("uuid");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("Action")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("Details")
|
|
|
|
|
|
.HasColumnType("jsonb");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("IpAddress")
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<Guid?>("ProposalId")
|
|
|
|
|
|
.HasColumnType("uuid");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<DateTime>("Timestamp")
|
|
|
|
|
|
.HasColumnType("timestamp with time zone");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<Guid>("UserId")
|
|
|
|
|
|
.HasColumnType("uuid");
|
|
|
|
|
|
|
|
|
|
|
|
b.HasKey("Id");
|
|
|
|
|
|
|
|
|
|
|
|
b.HasIndex("ProposalId");
|
|
|
|
|
|
|
|
|
|
|
|
b.HasIndex("Timestamp");
|
|
|
|
|
|
|
|
|
|
|
|
b.HasIndex("UserId");
|
|
|
|
|
|
|
|
|
|
|
|
b.ToTable("AuditLogs");
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
modelBuilder.Entity("ProposalSystem.Domain.Entities.Customer", b =>
|
|
|
|
|
|
{
|
|
|
|
|
|
b.Property<Guid>("Id")
|
|
|
|
|
|
.ValueGeneratedOnAdd()
|
|
|
|
|
|
.HasColumnType("uuid");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("Addresses")
|
|
|
|
|
|
.HasColumnType("jsonb");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<DateTime>("CreatedAt")
|
|
|
|
|
|
.HasColumnType("timestamp with time zone");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("Name")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<DateTime>("UpdatedAt")
|
|
|
|
|
|
.HasColumnType("timestamp with time zone");
|
|
|
|
|
|
|
|
|
|
|
|
b.HasKey("Id");
|
|
|
|
|
|
|
|
|
|
|
|
b.ToTable("Customers");
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
modelBuilder.Entity("ProposalSystem.Domain.Entities.GeneratedPdf", b =>
|
|
|
|
|
|
{
|
|
|
|
|
|
b.Property<Guid>("Id")
|
|
|
|
|
|
.ValueGeneratedOnAdd()
|
|
|
|
|
|
.HasColumnType("uuid");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<DateTime>("GeneratedAt")
|
|
|
|
|
|
.HasColumnType("timestamp with time zone");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<Guid>("GeneratedById")
|
|
|
|
|
|
.HasColumnType("uuid");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<Guid>("ProposalId")
|
|
|
|
|
|
.HasColumnType("uuid");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<int>("Revision")
|
|
|
|
|
|
.HasColumnType("integer");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("S3Key")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.HasKey("Id");
|
|
|
|
|
|
|
|
|
|
|
|
b.HasIndex("GeneratedById");
|
|
|
|
|
|
|
|
|
|
|
|
b.HasIndex("ProposalId");
|
|
|
|
|
|
|
|
|
|
|
|
b.ToTable("GeneratedPdfs");
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
modelBuilder.Entity("ProposalSystem.Domain.Entities.LineItem", b =>
|
|
|
|
|
|
{
|
|
|
|
|
|
b.Property<Guid>("Id")
|
|
|
|
|
|
.ValueGeneratedOnAdd()
|
|
|
|
|
|
.HasColumnType("uuid");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<DateTime>("CreatedAt")
|
|
|
|
|
|
.HasColumnType("timestamp with time zone");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("Description")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("PricingMode")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<Guid>("ProposalId")
|
|
|
|
|
|
.HasColumnType("uuid");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<decimal>("Quantity")
|
|
|
|
|
|
.HasPrecision(18, 4)
|
|
|
|
|
|
.HasColumnType("numeric(18,4)");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<int>("SortOrder")
|
|
|
|
|
|
.HasColumnType("integer");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("Source")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<decimal>("TotalPrice")
|
|
|
|
|
|
.HasPrecision(18, 2)
|
|
|
|
|
|
.HasColumnType("numeric(18,2)");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("Unit")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<decimal?>("UnitPrice")
|
|
|
|
|
|
.HasPrecision(18, 2)
|
|
|
|
|
|
.HasColumnType("numeric(18,2)");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<DateTime>("UpdatedAt")
|
|
|
|
|
|
.HasColumnType("timestamp with time zone");
|
|
|
|
|
|
|
|
|
|
|
|
b.HasKey("Id");
|
|
|
|
|
|
|
|
|
|
|
|
b.HasIndex("ProposalId");
|
|
|
|
|
|
|
|
|
|
|
|
b.ToTable("LineItems");
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
modelBuilder.Entity("ProposalSystem.Domain.Entities.Proposal", b =>
|
|
|
|
|
|
{
|
|
|
|
|
|
b.Property<Guid>("Id")
|
|
|
|
|
|
.ValueGeneratedOnAdd()
|
|
|
|
|
|
.HasColumnType("uuid");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<DateTime?>("ApprovedAt")
|
|
|
|
|
|
.HasColumnType("timestamp with time zone");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<Guid?>("ApprovedById")
|
|
|
|
|
|
.HasColumnType("uuid");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<Guid?>("AssignedAdminId")
|
|
|
|
|
|
.HasColumnType("uuid");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<DateTime>("CreatedAt")
|
|
|
|
|
|
.HasColumnType("timestamp with time zone");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<int>("CurrentRevision")
|
|
|
|
|
|
.HasColumnType("integer");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("CustomerAddress")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("CustomerName")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("Notes")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<Guid?>("ParentProposalId")
|
|
|
|
|
|
.HasColumnType("uuid");
|
|
|
|
|
|
|
2026-05-22 16:38:37 -04:00
|
|
|
|
b.Property<string>("PoNumber")
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
Implement Phases 2-5: Frontend, AI/RAG, PDF Generation (#22)
* Fix NuGet versions and add InitialCreate EF Core migration
- Update AWSSDK.SQS and AWSSDK.SecretsManager to 3.7.500.0 (actual available versions)
- Update AWSSDK.Extensions.NETCore.Setup to 3.7.400
- Generate InitialCreate migration for PostgreSQL (all 8 entities)
- Build verified: 0 errors, 0 warnings
* Implement Dispatcher Frontend (Phase 2)
React 19 + MUI v7 + TypeScript + Vite SPA matching SHOC patterns:
Redux Toolkit (auth/ui slices), TanStack React Query, axios interceptors,
react-toastify, Cognito OAuth PKCE login flow, paginated proposal list,
new proposal form with customer autocomplete and vendor PDF upload,
read-only proposal detail with status stepper timeline.
* Add AuthController for Cognito code exchange and .env.example
Backend endpoint POST /api/auth/callback exchanges the OAuth
authorization code with Cognito's token endpoint, auto-provisions
the user in the DB, and returns the access token to the frontend.
* Implement Admin Frontend Experience (Phase 3)
Three-panel admin workspace: left reference panel (submission details,
vendor data), center editor (refined scope, inline line item table with
reorder/add/remove/pricing), right similar proposals panel (KB results
with pull-to-editor). Admin dashboard with stats cards and proposal
queue table. Approval flow with confirmation dialog, mark-as-sent,
and create-revision actions. Role-based sidebar navigation.
* Implement backend dev mode, internal API auth, and service layer enhancements
- Add dev-login endpoint with local JWT signing for local development
- Add InternalApiKeyMiddleware with timing-safe comparison for Lambda-to-API auth
- Add DevS3Service and NoOpJobPublisher for running without AWS services
- Implement CurrentUserService cascading user resolution (ID → sub → email → create)
- Add async ResolveAsync() to avoid synchronous DB calls in request pipeline
- Add /proposals/stats endpoint for efficient server-side status counts
- Guard status transitions: only allow Draft → InReview via update endpoint
- Add vendor proposals, generated PDFs, and similar proposals controllers
- Add ISimilarProposalService and SimilarProposalService
- Add [Authorize] to AddSimilarReference endpoint
* Implement Lambda functions for PDF processing, suggestions, and library ingest
- pdf-extract: Parse vendor PDFs with pdfplumber, fallback to Claude multimodal
- pdf-generate: Generate branded proposal PDFs with reportlab Platypus
- library-ingest: Format approved proposals as markdown and sync to Bedrock KB
- suggestions: Query KB for similar proposals, generate line items via Claude
- All Lambdas use internal API key auth and cold-start secret caching
- Fix pdf_path unbound variable in pdf-extract error handling
* Add Bedrock Knowledge Base, OpenSearch Serverless, and SQS message filtering
- Provision OpenSearch Serverless collection for vector search
- Create Bedrock Knowledge Base with Titan embedding model
- Configure S3 data source with fixed-size chunking (512 tokens, 20% overlap)
- Add suggestions Lambda with SQS event source filtering
- Scope bedrock:InvokeModel IAM to specific model ARN patterns
- Add internal API key secret in Secrets Manager
- Add log retention (2 months) to all Lambda functions
- Add docker-compose.yml for local PostgreSQL
* Apply SHOC design system styling across frontend
- Rewrite theme with SHOC palette (#0c4f6f primary, Nunito font, 4px radius)
- Add global CSS with Google Fonts import for Nunito
- Redesign Topbar with avatar initials, role subtitle, gradient header
- Redesign Sidebar with 220px width, section headers, active state border
- Restyle LoginPage with SHOC branded card and dev-mode role selector
- Update AdminDashboard KPI cards to centered SHOC style
- Add devLogin API method for local development auth flow
* Fix frontend navigation bugs, differentiate Dashboard from Proposals list
- Fix double nav selection by adding isNavActive() with ALL_NAV_PATHS set
- Fix /admin/users routing to placeholder instead of redirect to /
- Fix ProposalDetailPage Back button navigating to / instead of /proposals
- Differentiate Dashboard (KPI cards + recent 5) from ProposalListPage (full paginated table)
- Dashboard now uses dedicated /proposals/stats endpoint for accurate counts
- Fix adminApi.getPdf dead code (axios rejects before status check)
- Wire up PDF generation button in AdminWorkspace
- Adjust layout: 220px drawer, 10px content padding, 64px toolbar height
* Add appsettings.Development.json to gitignore
Prevent dev-only signing keys and connection strings from being committed.
* Fix CI failures: unused Python imports and CDK synth asset path
CDK synth job needs the .NET API published first so the Lambda asset
path exists. Python lint had 3 unused imports in pdf-generate.
* Apply ruff formatting to all Lambda Python files
2026-05-17 13:06:23 -04:00
|
|
|
|
b.Property<string>("Priority")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("ProposalNumber")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("RefinedScope")
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("ScopeOfWork")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<DateTime?>("SentAt")
|
|
|
|
|
|
.HasColumnType("timestamp with time zone");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("ServiceCategory")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("Status")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<DateTime>("SubmittedAt")
|
|
|
|
|
|
.HasColumnType("timestamp with time zone");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<Guid>("SubmittedById")
|
|
|
|
|
|
.HasColumnType("uuid");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<decimal>("TotalBidAmount")
|
|
|
|
|
|
.HasPrecision(18, 2)
|
|
|
|
|
|
.HasColumnType("numeric(18,2)");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<DateTime>("UpdatedAt")
|
|
|
|
|
|
.HasColumnType("timestamp with time zone");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<decimal?>("VendorTotalCost")
|
|
|
|
|
|
.HasPrecision(18, 2)
|
|
|
|
|
|
.HasColumnType("numeric(18,2)");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("WorkOrderNumber")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.HasKey("Id");
|
|
|
|
|
|
|
|
|
|
|
|
b.HasIndex("ApprovedById");
|
|
|
|
|
|
|
|
|
|
|
|
b.HasIndex("AssignedAdminId");
|
|
|
|
|
|
|
|
|
|
|
|
b.HasIndex("ParentProposalId");
|
|
|
|
|
|
|
|
|
|
|
|
b.HasIndex("ProposalNumber")
|
|
|
|
|
|
.IsUnique();
|
|
|
|
|
|
|
|
|
|
|
|
b.HasIndex("SubmittedById");
|
|
|
|
|
|
|
|
|
|
|
|
b.ToTable("Proposals");
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
modelBuilder.Entity("ProposalSystem.Domain.Entities.SimilarProposalReference", b =>
|
|
|
|
|
|
{
|
|
|
|
|
|
b.Property<Guid>("Id")
|
|
|
|
|
|
.ValueGeneratedOnAdd()
|
|
|
|
|
|
.HasColumnType("uuid");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<Guid>("ProposalId")
|
|
|
|
|
|
.HasColumnType("uuid");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<DateTime>("ReferencedAt")
|
|
|
|
|
|
.HasColumnType("timestamp with time zone");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<Guid>("ReferencedById")
|
|
|
|
|
|
.HasColumnType("uuid");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("ReferencedLibraryItemId")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<float>("SimilarityScore")
|
|
|
|
|
|
.HasColumnType("real");
|
|
|
|
|
|
|
|
|
|
|
|
b.HasKey("Id");
|
|
|
|
|
|
|
|
|
|
|
|
b.HasIndex("ProposalId");
|
|
|
|
|
|
|
|
|
|
|
|
b.HasIndex("ReferencedById");
|
|
|
|
|
|
|
|
|
|
|
|
b.ToTable("SimilarProposalReferences");
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
modelBuilder.Entity("ProposalSystem.Domain.Entities.User", b =>
|
|
|
|
|
|
{
|
|
|
|
|
|
b.Property<Guid>("Id")
|
|
|
|
|
|
.ValueGeneratedOnAdd()
|
|
|
|
|
|
.HasColumnType("uuid");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("CognitoSub")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<DateTime>("CreatedAt")
|
|
|
|
|
|
.HasColumnType("timestamp with time zone");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("DisplayName")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("Email")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<bool>("IsActive")
|
|
|
|
|
|
.HasColumnType("boolean");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("Role")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<DateTime>("UpdatedAt")
|
|
|
|
|
|
.HasColumnType("timestamp with time zone");
|
|
|
|
|
|
|
|
|
|
|
|
b.HasKey("Id");
|
|
|
|
|
|
|
|
|
|
|
|
b.HasIndex("CognitoSub")
|
|
|
|
|
|
.IsUnique();
|
|
|
|
|
|
|
|
|
|
|
|
b.HasIndex("Email")
|
|
|
|
|
|
.IsUnique();
|
|
|
|
|
|
|
|
|
|
|
|
b.ToTable("Users");
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
modelBuilder.Entity("ProposalSystem.Domain.Entities.VendorProposal", b =>
|
|
|
|
|
|
{
|
|
|
|
|
|
b.Property<Guid>("Id")
|
|
|
|
|
|
.ValueGeneratedOnAdd()
|
|
|
|
|
|
.HasColumnType("uuid");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("ExtractedData")
|
|
|
|
|
|
.HasColumnType("jsonb");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("FileName")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("ProcessingStatus")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<Guid>("ProposalId")
|
|
|
|
|
|
.HasColumnType("uuid");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("S3Key")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<decimal>("TotalVendorCost")
|
|
|
|
|
|
.HasPrecision(18, 2)
|
|
|
|
|
|
.HasColumnType("numeric(18,2)");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<DateTime>("UploadedAt")
|
|
|
|
|
|
.HasColumnType("timestamp with time zone");
|
|
|
|
|
|
|
|
|
|
|
|
b.Property<string>("VendorName")
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
.HasColumnType("text");
|
|
|
|
|
|
|
|
|
|
|
|
b.HasKey("Id");
|
|
|
|
|
|
|
|
|
|
|
|
b.HasIndex("ProposalId");
|
|
|
|
|
|
|
|
|
|
|
|
b.ToTable("VendorProposals");
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
modelBuilder.Entity("ProposalSystem.Domain.Entities.AuditLog", b =>
|
|
|
|
|
|
{
|
|
|
|
|
|
b.HasOne("ProposalSystem.Domain.Entities.Proposal", "Proposal")
|
|
|
|
|
|
.WithMany()
|
|
|
|
|
|
.HasForeignKey("ProposalId");
|
|
|
|
|
|
|
|
|
|
|
|
b.HasOne("ProposalSystem.Domain.Entities.User", "User")
|
|
|
|
|
|
.WithMany()
|
|
|
|
|
|
.HasForeignKey("UserId")
|
|
|
|
|
|
.OnDelete(DeleteBehavior.Cascade)
|
|
|
|
|
|
.IsRequired();
|
|
|
|
|
|
|
|
|
|
|
|
b.Navigation("Proposal");
|
|
|
|
|
|
|
|
|
|
|
|
b.Navigation("User");
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
modelBuilder.Entity("ProposalSystem.Domain.Entities.GeneratedPdf", b =>
|
|
|
|
|
|
{
|
|
|
|
|
|
b.HasOne("ProposalSystem.Domain.Entities.User", "GeneratedBy")
|
|
|
|
|
|
.WithMany()
|
|
|
|
|
|
.HasForeignKey("GeneratedById")
|
|
|
|
|
|
.OnDelete(DeleteBehavior.Cascade)
|
|
|
|
|
|
.IsRequired();
|
|
|
|
|
|
|
|
|
|
|
|
b.HasOne("ProposalSystem.Domain.Entities.Proposal", "Proposal")
|
|
|
|
|
|
.WithMany()
|
|
|
|
|
|
.HasForeignKey("ProposalId")
|
|
|
|
|
|
.OnDelete(DeleteBehavior.Cascade)
|
|
|
|
|
|
.IsRequired();
|
|
|
|
|
|
|
|
|
|
|
|
b.Navigation("GeneratedBy");
|
|
|
|
|
|
|
|
|
|
|
|
b.Navigation("Proposal");
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
modelBuilder.Entity("ProposalSystem.Domain.Entities.LineItem", b =>
|
|
|
|
|
|
{
|
|
|
|
|
|
b.HasOne("ProposalSystem.Domain.Entities.Proposal", "Proposal")
|
|
|
|
|
|
.WithMany("LineItems")
|
|
|
|
|
|
.HasForeignKey("ProposalId")
|
|
|
|
|
|
.OnDelete(DeleteBehavior.Cascade)
|
|
|
|
|
|
.IsRequired();
|
|
|
|
|
|
|
|
|
|
|
|
b.Navigation("Proposal");
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
modelBuilder.Entity("ProposalSystem.Domain.Entities.Proposal", b =>
|
|
|
|
|
|
{
|
|
|
|
|
|
b.HasOne("ProposalSystem.Domain.Entities.User", "ApprovedBy")
|
|
|
|
|
|
.WithMany()
|
|
|
|
|
|
.HasForeignKey("ApprovedById")
|
|
|
|
|
|
.OnDelete(DeleteBehavior.SetNull);
|
|
|
|
|
|
|
|
|
|
|
|
b.HasOne("ProposalSystem.Domain.Entities.User", "AssignedAdmin")
|
|
|
|
|
|
.WithMany()
|
|
|
|
|
|
.HasForeignKey("AssignedAdminId")
|
|
|
|
|
|
.OnDelete(DeleteBehavior.SetNull);
|
|
|
|
|
|
|
|
|
|
|
|
b.HasOne("ProposalSystem.Domain.Entities.Proposal", "ParentProposal")
|
|
|
|
|
|
.WithMany()
|
|
|
|
|
|
.HasForeignKey("ParentProposalId")
|
|
|
|
|
|
.OnDelete(DeleteBehavior.SetNull);
|
|
|
|
|
|
|
|
|
|
|
|
b.HasOne("ProposalSystem.Domain.Entities.User", "SubmittedBy")
|
|
|
|
|
|
.WithMany()
|
|
|
|
|
|
.HasForeignKey("SubmittedById")
|
|
|
|
|
|
.OnDelete(DeleteBehavior.Restrict)
|
|
|
|
|
|
.IsRequired();
|
|
|
|
|
|
|
|
|
|
|
|
b.Navigation("ApprovedBy");
|
|
|
|
|
|
|
|
|
|
|
|
b.Navigation("AssignedAdmin");
|
|
|
|
|
|
|
|
|
|
|
|
b.Navigation("ParentProposal");
|
|
|
|
|
|
|
|
|
|
|
|
b.Navigation("SubmittedBy");
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
modelBuilder.Entity("ProposalSystem.Domain.Entities.SimilarProposalReference", b =>
|
|
|
|
|
|
{
|
|
|
|
|
|
b.HasOne("ProposalSystem.Domain.Entities.Proposal", "Proposal")
|
|
|
|
|
|
.WithMany()
|
|
|
|
|
|
.HasForeignKey("ProposalId")
|
|
|
|
|
|
.OnDelete(DeleteBehavior.Cascade)
|
|
|
|
|
|
.IsRequired();
|
|
|
|
|
|
|
|
|
|
|
|
b.HasOne("ProposalSystem.Domain.Entities.User", "ReferencedBy")
|
|
|
|
|
|
.WithMany()
|
|
|
|
|
|
.HasForeignKey("ReferencedById")
|
|
|
|
|
|
.OnDelete(DeleteBehavior.Cascade)
|
|
|
|
|
|
.IsRequired();
|
|
|
|
|
|
|
|
|
|
|
|
b.Navigation("Proposal");
|
|
|
|
|
|
|
|
|
|
|
|
b.Navigation("ReferencedBy");
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
modelBuilder.Entity("ProposalSystem.Domain.Entities.VendorProposal", b =>
|
|
|
|
|
|
{
|
|
|
|
|
|
b.HasOne("ProposalSystem.Domain.Entities.Proposal", "Proposal")
|
|
|
|
|
|
.WithMany("VendorProposals")
|
|
|
|
|
|
.HasForeignKey("ProposalId")
|
|
|
|
|
|
.OnDelete(DeleteBehavior.Cascade)
|
|
|
|
|
|
.IsRequired();
|
|
|
|
|
|
|
|
|
|
|
|
b.Navigation("Proposal");
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
modelBuilder.Entity("ProposalSystem.Domain.Entities.Proposal", b =>
|
|
|
|
|
|
{
|
|
|
|
|
|
b.Navigation("LineItems");
|
|
|
|
|
|
|
|
|
|
|
|
b.Navigation("VendorProposals");
|
|
|
|
|
|
});
|
|
|
|
|
|
#pragma warning restore 612, 618
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|