Create a standalone, multi-chapter MDX document within the RMC Software Documentation site that serves as the authoritative reference for the Dam Screening Tool (DST) database schema. The document will use custom React components to present tables, relationships, and module structure in a visual, navigable, and impressive format — suitable for both technical developers and engineering stakeholders.
The DST application source code lives in a separate repository (DST-Mockup) which the implementing agent will not have access to. All schema data needed to write the MDX content is embedded in Appendix A of this plan. Do not invent or guess column definitions — use only what is provided in Appendix A.
Following the established Docusaurus convention in this repo (see LST user's guide as reference), the document lives at:
docs/web-applications/dst/database-schema/v1.0/
├── 00-document-info.mdx Chapter 0: Document metadata, version history
├── 01-overview.mdx Chapter 1: Interactive module map + design principles
├── 02-screening-management.mdx Chapter 2: Screenings table, codes, top-level structure
├── 03-dam-information.mdx Chapter 3: Dam metadata, reservoir, spillway, embankment tables
├── 04-hazards.mdx Chapter 4: Hydrologic and seismic hazard tables
├── 05-failure-mode-screening.mdx Chapter 5: FM screening and selection tables
├── 06-pfm-01.mdx Chapter 6: PFM-01 Overtopping Erosion (4 tables, fully implemented)
├── 07-pfm-02-through-09.mdx Chapter 7: PFM-02 through PFM-09 (planned, pattern reference)
├── 08-pfm-10-through-33.mdx Chapter 8: PFM-10 through PFM-33 (planned, pattern reference)
├── 09-consequences.mdx Chapter 9: Consequence estimation tables
├── 10-risk-analysis.mdx Chapter 10: Risk computation and results tables
├── 11-auth-and-system.mdx Chapter 11: Authentication, users, roles, dashboard tables
├── 12-documents.mdx Chapter 12: Document management / attachments tables
├── 13-design-patterns.mdx Chapter 13: Cross-cutting patterns (GUID PKs, JSONB, SRP 3-table, timestamps, naming conventions)
└── 14-appendix-full-table-list.mdx Chapter 14: Alphabetical index of every table with links
New components live in the existing component directory:
src/components/
├── SchemaTable.js New: Renders a single DB table as a styled card
├── SchemaGroup.js New: Groups related tables with visual relationship lines
├── ModuleMap.js New: Interactive SVG module-level overview diagram
└── SchemaTag.js New: Small inline badge for column metadata (PK, FK, type, etc.)
src/css/
└── schema.css New: All schema component styles (follows tables.css patterns)
A small inline badge/chip used within SchemaTable to annotate columns.
Purpose: Visually distinguish column metadata (primary key, foreign key, data type, nullable, indexed, JSONB) at a glance without cluttering the table with text.
Props:
SchemaTag({
type, // Required: "pk" | "fk" | "type" | "nullable" | "indexed" | "jsonb" | "unique" | "default" | "cascade"
children, // Required: Display text (e.g., "UUID", "→ Pfm01", "JSONB")
})Rendering:
┌──────────────────────────────────┐
│ PK FK → Pfm01 JSONB ? │ ← Small rounded pills, color-coded by type
└──────────────────────────────────┘
Color Scheme (light / dark mode):
| Type | Light Background | Dark Background | Text Color | Example |
|---|---|---|---|---|
pk |
amber-100 | amber-900 | amber-800 | PK |
fk |
blue-100 | blue-900 | blue-800 | → Pfm01 |
type |
gray-100 | gray-800 | gray-700 | UUID, VARCHAR |
nullable |
slate-100 | slate-800 | slate-600 | ? |
indexed |
violet-100 | violet-900 | violet-800 | IDX |
jsonb |
emerald-100 | emerald-900 | emerald-800 | JSONB |
unique |
orange-100 | orange-900 | orange-800 | UQ |
default |
stone-100 | stone-800 | stone-600 | = 'dst' |
cascade |
red-100 | red-900 | red-800 | CASCADE |
Styling: Tailwind utility classes. inline-flex items-center rounded-md px-2 py-0.5 text-xs font-medium. Dark mode via [data-theme="dark"] overrides, following the existing pattern in custom.css.
Renders a single database table as a styled, self-contained card. This is the primary building block of the document.
Purpose: Present one database table with its columns, types, constraints, and descriptions in a visually rich card format that's far more readable than a raw markdown table.
Props:
SchemaTable({
name, // Required: Table name (e.g., "Pfm01")
description, // Required: One-line table purpose
status, // Optional: "implemented" | "planned" | "partial" — default "implemented"
columns, // Required: Array of column definition objects (see below)
notes, // Optional: Array of strings — rendered as footnotes below the table
jsonSchemas, // Optional: Array of { name, description, structure } — expandable JSONB column docs
id, // Optional: HTML id for anchor linking
})Column Definition Object:
{
name: "Pfm01Id", // Column name as it appears in the database
type: "UUID", // PostgreSQL type
tags: ["fk", "unique", "indexed"], // Array of SchemaTag types to render
fkTarget: "Pfm01", // If FK: target table name (renders as link)
default: "'dst'", // Default value, if any
nullable: false, // Whether column is nullable
description: "Foreign key to parent PFM-01 location", // Human-readable description
}Visual Layout:
┌─────────────────────────────────────────────────────────────────┐
│ ┌─────────┐ │
│ │ TABLE │ Pfm01_DstSrp ● Implemented │
│ └─────────┘ │
│ Stores DST-calculated SRP curves and nodal probability tables │
│ │
│ ┬─────────────────┬───────────┬─────────────┬────────────────┐ │
│ │ Column │ Type │ Constraints │ Description │ │
│ ┼─────────────────┼───────────┼─────────────┼────────────────┤ │
│ │ Id │ UUID │ PK │ Primary key │ │
│ │ Pfm01Id │ UUID │ FK → Pfm01 │ Parent ref │ │
│ │ │ │ UQ IDX │ │ │
│ │ CurvesJson │ JSONB │ ? JSONB │ 50-pt curves │ │
│ │ NodalJson │ JSONB │ ? JSONB │ Nodal table │ │
│ │ LastCalculatedAt │ TIMESTAMP │ ? │ Last calc time │ │
│ │ LastNodalEditAt │ TIMESTAMP │ ? │ Last nodal ed. │ │
│ └─────────────────┴───────────┴─────────────┴────────────────┘ │
│ │
│ ▸ CurvesJson Structure (click to expand) │
│ ▸ NodalJson Structure (click to expand) │
│ │
│ Notes: │
│ 1. JSONB columns allow PostgreSQL to index and query JSON data │
│ 2. CurvesJson stores raw 50-point curves from System-Response │
└─────────────────────────────────────────────────────────────────┘
Key Design Decisions:
- The card has a subtle left border colored by status: teal (implemented), amber (partial), gray (planned)
- Column rows use zebra striping following
tables.csspattern - FK column names are clickable links that scroll to the target SchemaTable on the same page (or link to another chapter)
- JSONB columns get expandable detail sections (
<details>rendered as styled accordion) - The status badge uses the same SchemaTag component
- Responsive: on narrow screens the constraints column wraps tags vertically
Styling Approach:
- Outer card:
border rounded-lg shadow-smwith Tailwind, left border colored by status - Header: table name in
text-h3weight with description intext-normal text-font-color/70 - Column grid: CSS Grid (not
<table>) for better responsive control. 4 columns:[name 25%] [type 12%] [constraints auto] [description 1fr] - Tags rendered inline via SchemaTag
- Dark mode: card background
bg-[var(--ifm-background-surface-color)], borders from--border-color
Groups related tables (e.g., the 4 PFM-01 tables) with a header, description, and visual relationship indicators.
Purpose: Show how tables within a module relate to each other — the "mini ER diagram" for a specific module, rendered as connected SchemaTable cards rather than a separate diagram.
Props:
SchemaGroup({
title, // Required: Group name (e.g., "PFM-01: Overtopping Erosion")
description, // Optional: Group-level description
relationships, // Required: Array of relationship descriptors (see below)
children, // Required: SchemaTable components as children
id, // Optional: HTML id for anchor linking
})Relationship Descriptor:
{
from: "Pfm01", // Source table name
to: "Pfm01_DstSrp", // Target table name
type: "one-to-one", // "one-to-one" | "one-to-many" | "many-to-many"
label: "CASCADE DELETE", // Optional label on the relationship line
onDelete: "cascade", // Optional: "cascade" | "restrict" | "set-null"
}Visual Layout:
╔═══════════════════════════════════════════════════════════════════╗
║ PFM-01: Overtopping Erosion ║
║ Each row represents one PFM location. The three-table SRP ║
║ pattern preserves data when switching between DST and User mode. ║
╠═══════════════════════════════════════════════════════════════════╣
║ ║
║ ┌──────────────────────────┐ ║
║ │ Pfm01 │ ← Main entity card ║
║ └──────────┬───────────────┘ ║
║ │ ║
║ ┌───────┼───────┬───────────────┐ ║
║ │ 1:1 │ 1:1 │ 1:1 │ ← Relationship lines ║
║ ▼ ▼ ▼ │ ║
║ ┌────────┐ ┌────────┐ ┌──────────┐ │ ║
║ │ DstSrp │ │UserSrp │ │ActiveSrp │ │ ║
║ └────────┘ └────────┘ └──────────┘ │ ║
║ ║
╚═══════════════════════════════════════════════════════════════════╝
Key Design Decisions:
- Relationship lines are rendered with CSS (borders + pseudo-elements), not SVG. This keeps them responsive, theme-aware, and simple to maintain. Vertical/horizontal lines connect cards using CSS Grid positioning.
- The group wrapper uses a prominent header bar in
--ifm-color-primary(same teal as site header) - Relationship annotations (1:1, 1:N, CASCADE) appear as small labels on the lines
- On mobile, the relationship lines collapse and cards stack vertically with text-based relationship indicators instead
Styling Approach:
- Outer:
border-2 rounded-xlwith header in primary color - Layout: CSS Grid with named areas for positioning cards in the relationship diagram
- Lines:
border-l-2 border-t-2pseudo-elements withabsolutepositioning, colored with--border-color - Mobile fallback:
@media (max-width: 900px)switches to vertical stack with text labels
An interactive SVG-based overview diagram showing all application modules and their high-level relationships. This is the "hero" visualization on the overview page.
Purpose: Give readers an immediate, visual understanding of the entire DST database architecture — which modules exist, how many tables each contains, and how modules relate to each other. Clicking a module navigates to its chapter.
Props:
ModuleMap({
modules, // Required: Array of module definition objects (see below)
relationships, // Required: Array of module-level relationship lines
height, // Optional: SVG viewBox height (default: auto-calculated)
})Module Definition Object:
{
id: "pfm-01", // Unique module identifier
name: "PFM-01", // Display name
subtitle: "Overtopping Erosion", // Second line in the box
tables: 4, // Number of tables in this module
status: "implemented", // "implemented" | "planned" | "partial"
href: "./06-pfm-01", // Docusaurus link to the chapter page
group: "response", // Visual grouping: "core" | "hazards" | "response" | "risk" | "system"
}Module-Level Relationship:
{
from: "screening", // Source module id
to: "pfm-01", // Target module id
label: "1:N", // Cardinality
}Visual Layout:
The diagram follows the DST workflow left-to-right:
┌─────────────┐ ┌──────────────┐ ┌─────────────────────────────────────┐
│ Screening │────→│ Dam │ │ Failure Mode Response │
│ Management │ │ Information │ │ │
│ 1 table │ │ ~12 tables │ │ ┌───────┐ ┌───────┐ ┌───────┐ │
│ ● Implemented│ │ ○ Planned │ │ │PFM-01 │ │PFM-02 │ ... │PFM-33 │ │
└──────┬──────┘ └──────┬───────┘ │ │4 tbls │ │4 tbls │ │4 tbls │ │
│ │ │ │ ● │ │ ○ │ │ ○ │ │
│ │ │ └───────┘ └───────┘ └───────┘ │
│ │ └──────────────┬────────────────────┘ │
│ │ │ │
▼ ▼ ▼ │
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ Hazards │ │ Consequences │ │ Risk │ │
│ ~8 tables │ │ ~8 tables │ │ Analysis │ │
│ ○ Planned │ │ ○ Planned │ │ ~5 tables │ │
└──────────────┘ └──────────────┘ └──────────────┘ │
│
┌─────────────────────────────────────────────────────────────────────────────┐ │
│ System: Auth (~8 tables) ○ │ Dashboard (~3 tables) ○ │ Docs (~3) ○ │ │
└─────────────────────────────────────────────────────────────────────────────┘ │
Key Design Decisions:
- Pure SVG rendered in React (no external library dependency). This follows the EventTree pattern already established in this repo.
- Each module box is an
<a>wrapped<g>group — clicking navigates to the chapter - Status indicator: filled circle (●) = implemented, half circle (◐) = partial, empty circle (○) = planned
- Table count shown inside each box
- Color-coded by group: teal for core workflow, blue for hazards, green for response, orange for risk, gray for system
- Hover effect: subtle scale transform + shadow (CSS transition on SVG group)
- Dark mode: SVG inherits CSS custom properties for fill/stroke colors via
currentColorandvar()references - Responsive: SVG viewBox scales naturally. On narrow screens, the layout could optionally reflow to vertical (controlled by a CSS media query that sets
width: 100%on the SVG)
SVG Architecture:
<defs>block defines reusable elements: module box template, arrow markers, group background rects- Each module is a
<g>withtransformpositioning (absolute coordinates in viewBox space) - Relationship lines are
<path>elements withmarker-endarrows - Group backgrounds are semi-transparent rounded
<rect>elements behind grouped modules - Text uses
font-family: var(--ifm-font-family-base)for consistency with site typography
PNG Export (Stretch Goal):
- Follow the EventTree pattern: Canvas API rasterization with configurable scale
- Add a "Copy as PNG" button for easy insertion into reports/presentations
All schema component styles in a single stylesheet, following the pattern of tables.css.
/* === Schema Card === */
.schema-card { ... } /* Outer card with rounded corners, shadow, left border */
.schema-card--implemented { ... } /* Left border: var(--ifm-color-primary) */
.schema-card--partial { ... } /* Left border: amber */
.schema-card--planned { ... } /* Left border: gray */
.schema-card__header { ... } /* Table name + description area */
.schema-card__name { ... } /* Table name in bold, larger text */
.schema-card__description { ... } /* Description in muted text */
.schema-card__status { ... } /* Status badge positioning */
/* === Schema Column Grid === */
.schema-columns { ... } /* CSS Grid: 4 columns */
.schema-columns__header { ... } /* Column header row */
.schema-columns__row { ... } /* Data row with zebra striping */
.schema-columns__row:nth-child(even) { ... } /* Zebra stripe */
.schema-columns__cell { ... } /* Individual cell */
.schema-columns__cell--name { ... } /* Column name cell: monospace font */
.schema-columns__cell--type { ... } /* Type cell: monospace, muted */
.schema-columns__cell--tags { ... } /* Constraints cell: flex wrap for tags */
.schema-columns__cell--desc { ... } /* Description cell */
/* === Schema Tags (Badges) === */
.schema-tag { ... } /* Base badge: inline-flex, rounded, small text */
.schema-tag--pk { ... } /* Amber */
.schema-tag--fk { ... } /* Blue, clickable */
.schema-tag--jsonb { ... } /* Emerald */
.schema-tag--nullable { ... } /* Slate */
.schema-tag--indexed { ... } /* Violet */
.schema-tag--unique { ... } /* Orange */
.schema-tag--default { ... } /* Stone */
.schema-tag--cascade { ... } /* Red */
/* === Schema Group === */
.schema-group { ... } /* Outer wrapper with prominent border */
.schema-group__header { ... } /* Title bar in primary color */
.schema-group__diagram { ... } /* CSS Grid layout for relationship diagram */
.schema-group__line { ... } /* Relationship line (CSS borders) */
.schema-group__line-label { ... } /* "1:1", "1:N" label on line */
/* === Module Map (SVG) === */
.module-map { ... } /* SVG wrapper */
.module-map__box { ... } /* Module box hover effect */
.module-map__box:hover { ... } /* Scale + shadow on hover */
.module-map__group-bg { ... } /* Semi-transparent group background */
/* === JSONB Expander === */
.schema-json-expander { ... } /* Expandable JSONB detail section */
.schema-json-expander__toggle { ... } /* Click target */
.schema-json-expander__content { ... } /* Hidden by default, slides open */
/* === Dark Mode Overrides === */
[data-theme="dark"] .schema-card { ... }
[data-theme="dark"] .schema-tag--pk { ... }
[data-theme="dark"] .schema-columns__row:nth-child(even) { ... }
/* ... etc for all components ... */
/* === Responsive === */
@media (max-width: 900px) {
.schema-columns { ... } /* Stack or scroll horizontally */
.schema-group__diagram { ... } /* Vertical stack with text relationships */
.module-map { ... } /* Full-width SVG */
}Standard document metadata page following existing convention:
- DocumentMetadata component with title, authors, version, date
- TableVersionHistory component
- Brief introduction to the DST database architecture
- ModuleMap component — the hero interactive diagram
- Design principles summary:
- PFM-based table naming convention
- Three-table SRP pattern
- GUID primary keys
- JSONB for complex nested data
- Clean Architecture alignment
- How to read this document (legend for tags, status indicators)
- Link to the source code repository
- SchemaTable for the
Screeningstable - Discussion of screening codes, URL routing
- Future plans: multi-user screening management
- Status: Planned (no tables implemented yet)
- SchemaTable cards for each planned table, marked with
status="planned" - Expected tables: DamOverview, ReservoirData, Spillway, EmbankmentData, CrossSection, EmbankmentComponent, AbutmentSlope, etc.
- Notes on data sources (NID integration, manual entry)
- Status: Planned
- SchemaGroup for Hydrologic hazard tables
- SchemaGroup for Seismic hazard tables
- Discussion of hazard curve storage (stage-frequency, PGA-frequency)
- Status: Planned
- Tables for which failure modes are applicable to a given dam
- Tables for user's selection/prioritization of failure modes
This is the flagship chapter — fully implemented, most detailed.
- SchemaGroup wrapping all 4 tables with relationship lines:
- SchemaTable for
Pfm01(main entity — ~30 columns with distribution parameters) - SchemaTable for
Pfm01_DstSrp(with JSONB expanders for CurvesJson and NodalJson) - SchemaTable for
Pfm01_UserSrp - SchemaTable for
Pfm01_ActiveSrp
- SchemaTable for
- Detailed JSONB structure documentation (expandable sections)
- Enum value tables for EmbankmentErodibility and GrassCoverQuality
- Discussion of the three-table SRP sync pattern
- Concurrency handling notes (pessimistic locking)
- Status: Planned
- Discussion of the blueprint pattern — each PFM follows the same 4-table structure
- SchemaTable cards for PFM-02 as a representative example (marked
status="planned") - Table listing all PFMs in this range with their failure mode names
- Status: Planned (PFM-10 has partial frontend)
- Same pattern discussion
- Special notes for PFM-10 (Internal Erosion - Sand Boils) which has partial UI already
- Table listing all PFMs in this range
- Status: Planned
- Expected tables for consequence estimation (economic, life safety, environmental)
- Relationship to PFM results
- Status: Planned
- Expected tables for risk computation, APF calculation, results storage
- Relationship to hazards and consequences
- Status: Planned
- Expected tables: Users, Roles, UserRoles, Sessions, AuditLog, etc.
- Dashboard and screening management tables
- Notes on RBAC design
- Status: Planned
- Expected tables for document/file attachment management
- Storage strategy (file system vs blob storage)
A reference chapter explaining cross-cutting database design decisions:
- GUID Primary Keys — why, how generated (PostgreSQL
gen_random_uuid()) - JSONB Columns — when to use vs. normalized tables, indexing strategies
- Three-Table SRP Pattern — the DstSrp / UserSrp / ActiveSrp pattern explained with diagrams
- Timestamp Conventions —
CreatedAt,UpdatedAt,Last*Atpatterns - Naming Conventions — PFM-based prefixing, PascalCase entities → snake_case columns
- Soft Delete vs Hard Delete — current approach (hard delete with cascade)
- EF Core Configuration Pattern — Fluent API in separate
IEntityTypeConfiguration<T>classes - Migration Strategy — naming, ordering, rollback approach
- Alphabetical table of every database table (implemented + planned)
- Columns: Table Name, Module, Status, Column Count, Chapter Link
- Uses the standard
TableVerticalcomponent (no custom component needed) - Serves as a quick-reference index for the entire document
- Create
src/css/schema.csswith all style classes - Import
schema.cssindocusaurus.config.js(add tostylesheetsor import incustom.css) - Build
SchemaTag.js— smallest, no dependencies, test in isolation - Build
SchemaTable.js— depends on SchemaTag, test with mock data - Build
SchemaGroup.js— depends on SchemaTable, test with PFM-01 group - Build
ModuleMap.js— standalone SVG component, test with current module inventory
- Create the document folder structure at
docs/web-applications/dst/database-schema/v1.0/ - Write
00-document-info.mdx(metadata) - Write
01-overview.mdxwith ModuleMap (populate with all known modules) - Write
06-pfm-01.mdx— the flagship chapter, using all column data from Appendix A - Write
02-screening-management.mdx— the Screenings table - Write
13-design-patterns.mdx— cross-cutting patterns reference
- Write chapters 03-05, 07-12 — planned module chapters with
status="planned"tables - Write
14-appendix-full-table-list.mdx— master index
- Verify all dark mode rendering
- Test responsive behavior at all three breakpoints (sm/md/lg)
- Verify sidebar auto-generation picks up the new document
- Test all internal links (FK references, chapter cross-links)
- Run the Docusaurus build (
npm run build) to verify no errors - Verify counter/numbering system works for any TableVertical or Figure components used
The existing sidebars.js auto-generation script scans the docs/ folder structure. The new document at docs/web-applications/dst/database-schema/v1.0/ should be automatically picked up. Verify by running npm run sidebars after creating the files.
If any chapters use the existing TableVertical, Figure, or Equation components (which have automatic numbering), the counter generation script (npm run counters) must be run to produce the numbering JSON. The custom schema components (SchemaTable, SchemaGroup, ModuleMap) do NOT integrate with the counter system — they use their own visual identity.
Add schema.css import to src/css/custom.css:
@import './schema.css';Every chapter MDX file should include the NavContainer at the top:
<NavContainer
link="/web-applications/dst"
linkTitle="Dam Screening Tool"
document="web-applications/dst/database-schema"
/>-
Source of truth is Appendix A. Column definitions come from the schema data embedded in Appendix A of this plan (extracted from C# entity classes). When the DST schema changes, Appendix A and the MDX content should be updated to match.
-
Progressive disclosure. The ModuleMap gives the 10-second overview. Chapter pages give the module view. SchemaTable cards give column-level detail. JSONB expanders give nested structure. Each layer is opt-in.
-
Status transparency. Every table and module is clearly marked as implemented, partial, or planned. Readers always know what exists vs. what's aspirational.
-
Dark mode is not an afterthought. Every component must look good in both themes. Use CSS custom properties, not hardcoded colors.
-
No external runtime dependencies. All components use React + CSS + inline SVG. No chart libraries, no reactflow, no D3. This keeps the build simple and the bundle small, consistent with the existing component philosophy in this repo.
-
Follow existing patterns. Component props follow TableVertical conventions. CSS follows tables.css patterns. MDX follows LST chapter structure. Don't reinvent what already works.
This appendix contains all database table definitions, column details, constraints, JSONB structures, and enum values extracted from the DST source code. This is the sole source of truth for writing MDX content — do not invent or guess any schema details.
These tables exist in the database today with EF Core migrations applied.
PostgreSQL table name: Screenings
Purpose: Top-level entity representing a complete dam screening assessment. Each screening is for one dam.
Status: Implemented
| Column | PostgreSQL Type | Nullable | Constraints | Default | Description |
|---|---|---|---|---|---|
| Id | UUID | NO | PK | gen_random_uuid() |
Primary key |
| Code | VARCHAR(10) | NO | UNIQUE, INDEXED | — | Short alphanumeric code (6 chars) used in URLs. Example: "Xk9mP2" → /screening/Xk9mP2/... |
| Name | VARCHAR(200) | NO | — | — | Name of the dam being screened |
| CreatedAt | TIMESTAMP | NO | — | CURRENT_TIMESTAMP |
When the screening was created |
| UpdatedAt | TIMESTAMP | NO | — | CURRENT_TIMESTAMP |
When the screening was last modified |
Indexes:
- PK on
Id - Unique index on
Code
Relationships: None currently. Future: Screenings (1) → (N) Pfm01
PostgreSQL table name: Pfm01
Purpose: PFM-01: Overtopping Erosion location entity. Each row represents one PFM location being evaluated (e.g., "Main Embankment", "Left Abutment").
Status: Implemented
| Column | PostgreSQL Type | Nullable | Constraints | Default | Description |
|---|---|---|---|---|---|
| Id | UUID | NO | PK | gen_random_uuid() |
Primary key |
| ScreeningId | UUID | YES | INDEXED | — | For future Screening FK |
| Index | INT | NO | INDEXED | — | Ordering within this PFM |
| Name | VARCHAR(200) | NO | — | — | Location name (e.g., "Main Embankment", "Left Abutment") |
| CrossSection | VARCHAR(200) | YES | — | — | Cross-section identifier (e.g., "Sta 10+00") |
| SrpMode | VARCHAR(20) | NO | — | 'dst' |
SRP mode: 'dst' or 'user' |
| EmbankmentHeight | FLOAT8 | YES | — | — | Embankment height in feet |
| CrestWidth | FLOAT8 | YES | — | — | Crest width in feet |
| DownstreamSlope | FLOAT8 | YES | — | — | Downstream slope ratio (e.g., 2.5) |
| DownstreamShellMaterial | VARCHAR(100) | YES | — | — | Downstream shell material type |
| EmbankmentErodibility | VARCHAR(50) | YES | — | — | Erodibility category (see Enum: EmbankmentErodibility) |
| GrassCoverQuality | VARCHAR(50) | YES | — | — | Grass cover quality (see Enum: GrassCoverQuality) |
| KdDistributionType | VARCHAR(50) | YES | — | — | Distribution type for erodibility coefficient (kd) |
| KdMin | FLOAT8 | YES | — | — | kd lower bound (Uniform/Triangular/PERT) |
| KdMode | FLOAT8 | YES | — | — | kd mode value (Triangular/PERT) |
| KdMax | FLOAT8 | YES | — | — | kd upper bound (Uniform/Triangular/PERT) |
| KdMean | FLOAT8 | YES | — | — | kd mean value (Normal/Lognormal) |
| KdStd | FLOAT8 | YES | — | — | kd standard deviation (Normal/Lognormal) |
| TauCDistributionType | VARCHAR(50) | YES | — | — | Distribution type for critical shear stress (τc) |
| TauCMin | FLOAT8 | YES | — | — | τc lower bound |
| TauCMode | FLOAT8 | YES | — | — | τc mode value |
| TauCMax | FLOAT8 | YES | — | — | τc upper bound |
| TauCMean | FLOAT8 | YES | — | — | τc mean value |
| TauCStd | FLOAT8 | YES | — | — | τc standard deviation |
| ManningsNDistributionType | VARCHAR(50) | YES | — | — | Distribution type for Manning's roughness (n) |
| ManningsNMin | FLOAT8 | YES | — | — | Manning's n lower bound |
| ManningsNMode | FLOAT8 | YES | — | — | Manning's n mode value |
| ManningsNMax | FLOAT8 | YES | — | — | Manning's n upper bound |
| ManningsNMean | FLOAT8 | YES | — | — | Manning's n mean value |
| ManningsNStd | FLOAT8 | YES | — | — | Manning's n standard deviation |
| BaseDuration | FLOAT8 | YES | — | — | Base duration in hours at 0.5 ft overtopping depth |
| MaxHazardLevel | FLOAT8 | YES | — | — | Max overtopping depth in feet (max hazard stage − crest elevation) |
| ShowAssumptions | BOOLEAN | NO | — | — | UI state: show/hide assumptions panel |
| CreatedAt | TIMESTAMP | NO | — | CURRENT_TIMESTAMP |
When the location was created |
| UpdatedAt | TIMESTAMP | NO | — | CURRENT_TIMESTAMP |
When the location was last modified |
Indexes:
- PK on
Id - Index on
ScreeningId - Index on
Index
Relationships:
Pfm01 (1) → (0..1) Pfm01_DstSrpviaPfm01_DstSrp.Pfm01Id(CASCADE DELETE)Pfm01 (1) → (0..1) Pfm01_UserSrpviaPfm01_UserSrp.Pfm01Id(CASCADE DELETE)Pfm01 (1) → (0..1) Pfm01_ActiveSrpviaPfm01_ActiveSrp.Pfm01Id(CASCADE DELETE)
Column Groups (for visual organization in SchemaTable): The columns can be grouped into logical sections for readability:
- Identity: Id, ScreeningId, Index, Name, CrossSection
- SRP Mode: SrpMode
- Geometry: EmbankmentHeight, CrestWidth, DownstreamSlope
- Materials: DownstreamShellMaterial, EmbankmentErodibility, GrassCoverQuality
- Erodibility Coefficient (kd): KdDistributionType, KdMin, KdMode, KdMax, KdMean, KdStd
- Critical Shear Stress (τc): TauCDistributionType, TauCMin, TauCMode, TauCMax, TauCMean, TauCStd
- Manning's Roughness (n): ManningsNDistributionType, ManningsNMin, ManningsNMode, ManningsNMax, ManningsNMean, ManningsNStd
- Analysis: BaseDuration, MaxHazardLevel
- UI State: ShowAssumptions
- Timestamps: CreatedAt, UpdatedAt
PostgreSQL table name: Pfm01_DstSrp
Purpose: Stores System-Response calculation results and user's nodal probability adjustments. Created when DST-based SRP calculations are first run.
Status: Implemented
| Column | PostgreSQL Type | Nullable | Constraints | Default | Description |
|---|---|---|---|---|---|
| Id | UUID | NO | PK | gen_random_uuid() |
Primary key |
| Pfm01Id | UUID | NO | FK → Pfm01, UNIQUE, INDEXED | — | Foreign key to parent PFM-01 location (1:1) |
| CurvesJson | JSONB | YES | — | — | System-Response 50-point curves (see JSONB: CurvesJson) |
| NodalJson | JSONB | YES | — | — | Nodal probability tables (see JSONB: NodalJson) |
| LastCalculatedAt | TIMESTAMP | YES | — | — | When System-Response was last executed |
| LastNodalEditAt | TIMESTAMP | YES | — | — | When user last edited nodal values |
Indexes:
- PK on
Id - Unique index on
Pfm01Id
Relationships:
Pfm01_DstSrp.Pfm01Id → Pfm01.Id(1:1, CASCADE DELETE)
PostgreSQL table name: Pfm01_UserSrp
Purpose: Stores manually-entered SRP curves computed outside DST by the user. Created when user first enters a user-defined SRP curve.
Status: Implemented
| Column | PostgreSQL Type | Nullable | Constraints | Default | Description |
|---|---|---|---|---|---|
| Id | UUID | NO | PK | gen_random_uuid() |
Primary key |
| Pfm01Id | UUID | NO | FK → Pfm01, UNIQUE, INDEXED | — | Foreign key to parent PFM-01 location (1:1) |
| CurveJson | JSONB | YES | — | — | User-entered SRP curve (see JSONB: UserSrp CurveJson) |
| Notes | VARCHAR(4000) | YES | — | — | Optional user notes/justification |
| LastModifiedAt | TIMESTAMP | YES | — | — | When user last modified the curve |
Indexes:
- PK on
Id - Unique index on
Pfm01Id
Relationships:
Pfm01_UserSrp.Pfm01Id → Pfm01.Id(1:1, CASCADE DELETE)
PostgreSQL table name: Pfm01_ActiveSrp
Purpose: Stores the SRP curve actually used in risk calculations. Synced from either DstSrp or UserSrp based on the parent Pfm01's SrpMode. Acts as a denormalized cache.
Status: Implemented
| Column | PostgreSQL Type | Nullable | Constraints | Default | Description |
|---|---|---|---|---|---|
| Id | UUID | NO | PK | gen_random_uuid() |
Primary key |
| Pfm01Id | UUID | NO | FK → Pfm01, UNIQUE, INDEXED | — | Foreign key to parent PFM-01 location (1:1) |
| CurveJson | JSONB | YES | — | — | Active SRP curve for risk calculations (same format as UserSrp CurveJson) |
| SourceMode | VARCHAR(20) | YES | — | — | Which source the curve came from: 'dst' or 'user' |
| LastSyncedAt | TIMESTAMP | YES | — | — | When the curve was last synced from its source |
Indexes:
- PK on
Id - Unique index on
Pfm01Id
Relationships:
Pfm01_ActiveSrp.Pfm01Id → Pfm01.Id(1:1, CASCADE DELETE)
Sync Rules:
- When
SrpMode = 'dst': CopytotalSrp.revisedEstimatefromPfm01_DstSrp.NodalJson - When
SrpMode = 'user': CopyCurveJsonfromPfm01_UserSrp - Sync triggers: mode switch, DST calculation complete, user-defined curve save
These are the JSON structures stored in JSONB columns. These should be documented as expandable detail sections within the SchemaTable component.
Stores the System-Response calculation output curves. Contains three 50-point SRP curves plus a duration curve.
{
"systemResponseCurve": [
{ "headwater": 0.0, "probability": 0.0 },
{ "headwater": 0.1, "probability": 0.002 },
...
// 50 points total — overtopping depth (ft) vs probability of failure
],
"grassRemovalCurve": [
{ "headwater": 0.0, "probability": 0.0 },
...
// 50 points — Node 2: probability of grass cover removal
],
"conditionalBreachCurve": [
{ "headwater": 0.0, "probability": 0.0 },
...
// 50 points — Node 5: conditional probability of breach
],
"durationCurve": [
{ "depth": 0.5, "duration": 12.0 },
{ "depth": 1.0, "duration": 6.0 },
...
// Overtopping depth (ft) vs duration (hours)
]
}Point types:
CurvePointDto:{ headwater: double, probability: double }— used by systemResponseCurve, grassRemovalCurve, conditionalBreachCurveDurationPointDto:{ depth: double, duration: double }— used by durationCurve
Stores the nodal probability tables for PFM-01's 5-node event tree. Contains the original RDP-selected headwater values, per-node probability tables with DST estimates and user revisions, and the computed total SRP.
{
"originalHeadwaters": [0.1, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 5.0],
// Array of up to 8 RDP-selected headwater values from initial calculation.
// Set once by ProcessCurvesToNodal, never modified by user edits.
// Used as revert target for "Revert to DST Estimates".
"nodes": {
"node1": {
"name": "Overtopping Initiates",
"headwaters": [0.1, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 5.0],
"dstEstimate": [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
"revisedEstimate": [null, null, null, null, null, null, null, null],
// null = no user override, use dstEstimate
"justification": null
},
"node2": {
"name": "Grass Cover Removed",
"headwaters": [0.1, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 5.0],
"dstEstimate": [0.05, 0.12, 0.35, 0.58, 0.72, 0.88, 0.95, 0.98],
"revisedEstimate": [null, null, null, null, null, null, null, null],
"justification": null
},
"node3": {
"name": "Erosion Continues",
"headwaters": [0.1, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 5.0],
"dstEstimate": [0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8],
"revisedEstimate": [null, null, null, null, null, null, null, null],
"justification": null
},
"node4": {
"name": "Breach Mechanism Forms",
"headwaters": [0.1, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 5.0],
"dstEstimate": [0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9],
"revisedEstimate": [null, null, null, null, null, null, null, null],
"justification": null
},
"node5": {
"name": "Dam Breaches",
"headwaters": [0.1, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 5.0],
"dstEstimate": [0.01, 0.08, 0.22, 0.41, 0.55, 0.73, 0.85, 0.92],
"revisedEstimate": [null, null, null, null, null, null, null, null],
"justification": null
}
},
"totalSrp": {
"headwaters": [0.1, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 5.0],
// Union of all node headwaters (sorted, unique). May exceed 8 if nodes diverge.
"dstEstimate": [0.00036, 0.0069, 0.0554, 0.171, 0.285, 0.462, 0.581, 0.649],
// Interpolated from 50-point systemResponseCurve at these headwater values.
"revisedEstimate": [null, null, null, null, null, null, null, null]
// Computed from nodal products when user adjusts any node.
// null = no revision exists, use dstEstimate.
}
}Key details:
- PFM-01 has exactly 5 nodes in its event tree
- Each node has up to 8 headwater values (selected via Ramer-Douglas-Peucker algorithm)
dstEstimatevalues are read-only (set by System-Response calculation)revisedEstimatevalues are user-editable overrides;nullmeans "use DST estimate"totalSrpis computed as the product of all node probabilities at each headwaterjustificationis free-text user rationale for adjustments
Simple array of headwater/probability pairs defining an SRP curve.
[
{ "headwater": 0.0, "probability": 0.0 },
{ "headwater": 0.5, "probability": 0.01 },
{ "headwater": 1.0, "probability": 0.05 },
{ "headwater": 2.0, "probability": 0.15 },
{ "headwater": 3.0, "probability": 0.35 },
{ "headwater": 5.0, "probability": 0.70 }
]Point type: { headwater: double, probability: double }
headwater: Overtopping depth in feet (load level)probability: Probability of failure at this depth (0.0 to 1.0)- Variable number of points (user-defined)
These enums are stored as strings in the database. They should be documented as reference tables within the relevant chapter (Chapter 6: PFM-01).
Purpose: SRP (System Response Probability) definition mechanism.
Stored as: String in Pfm01.SrpMode
| Value | Database String | Description |
|---|---|---|
| Dst | "dst" |
DST-based SRP: System-Response library calculates probabilities |
| User | "user" |
User-defined SRP: User manually defines the complete SRP curve |
Purpose: Embankment erodibility categories based on erosion resistance. Maps to System-Response API values.
Stored as: String in Pfm01.EmbankmentErodibility
| Value | Database String | Description |
|---|---|---|
| VeryErodible | "veryErodible" |
Very erodible material |
| Erodible | "erodible" |
Erodible material |
| ModeratelyResistant | "moderatelyResistant" |
Moderately resistant material |
| Resistant | "resistant" |
Resistant material |
| VeryResistant | "veryResistant" |
Very resistant material |
Purpose: Grass cover quality for downstream slope protection. Maps to System-Response API values.
Stored as: String in Pfm01.GrassCoverQuality
| Value | Database String | Description |
|---|---|---|
| None | "noGrassCover" |
No grass cover |
| Poor | "poorCover" |
Poor grass cover |
| Average | "averageCover" |
Average grass cover |
| Good | "goodCover" |
Good grass cover |
These tables do not exist in the database yet. They are anticipated based on the DST application's module structure. Use status="planned" on all SchemaTable components for these tables. Column definitions are representative placeholders — they should be clearly marked as draft/anticipated in the MDX content.
The implementing agent should create reasonable placeholder columns based on the module descriptions below. Every planned table should include at minimum:
Id(UUID, PK,gen_random_uuid())ScreeningId(UUID, FK → Screenings)CreatedAt/UpdatedAt(TIMESTAMP,CURRENT_TIMESTAMP)
Chapter: 03-dam-information.mdx Status: Planned (frontend pages exist, no database tables)
Anticipated tables:
| Table Name | Purpose | Estimated Columns |
|---|---|---|
| DamOverview | General dam identification and metadata (NID ID, location, owner, type, purposes) | ~15 |
| ReservoirData | Pool elevations, storage volumes, area-capacity data | ~10 |
| Spillway | Spillway type, dimensions, capacity, crest elevation | ~12 |
| EmbankmentData | Embankment dimensions, materials, zoning for each embankment section | ~15 |
| CrossSection | Cross-section geometry definitions (multiple per dam) | ~10 |
| EmbankmentComponent | Foundation, core, shell material properties | ~10 |
| AbutmentSlope | Abutment geometry and material descriptions | ~8 |
Chapter: 04-hazards.mdx Status: Planned
Anticipated tables:
| Table Name | Purpose | Estimated Columns |
|---|---|---|
| HydrologicHazard | Stage-frequency relationships, inflow flood data, PMF | ~10 |
| HydrologicHazardCurve | Individual curve points for stage-frequency (JSONB or normalized) | ~6 |
| SeismicHazard | Peak ground acceleration (PGA) hazard data | ~10 |
| SeismicHazardCurve | PGA-frequency curve points | ~6 |
Chapter: 05-failure-mode-screening.mdx Status: Planned
Anticipated tables:
| Table Name | Purpose | Estimated Columns |
|---|---|---|
| FmScreening | Which failure modes are applicable to this dam (checkbox-style screening) | ~8 |
| FmSelection | User's prioritization/selection of applicable failure modes for evaluation | ~8 |
Chapters: 07-pfm-02-through-09.mdx, 08-pfm-10-through-33.mdx Status: Planned
Each PFM follows the same 4-table pattern as PFM-01:
| Table Pattern | Purpose |
|---|---|
Pfm{NN} |
Main entity with PFM-specific input parameters |
Pfm{NN}_DstSrp |
System-Response calculation results + nodal tables |
Pfm{NN}_UserSrp |
User-defined SRP curve |
Pfm{NN}_ActiveSrp |
Active SRP for risk calculations |
The child SRP tables (DstSrp, UserSrp, ActiveSrp) will have the same structure across all PFMs — only the main Pfm{NN} table differs because each failure mode has different input parameters.
Known PFM names:
| PFM | Name | Notes |
|---|---|---|
| PFM-01 | Overtopping Erosion | Fully implemented (see A.1) |
| PFM-02 | Internal Erosion - Backward Erosion Piping | Planned |
| PFM-03 | Internal Erosion - Concentrated Leak Erosion | Planned |
| PFM-04 | Internal Erosion - Contact Erosion | Planned |
| PFM-05 | Internal Erosion - Internal Migration | Planned |
| PFM-06 | Static Slope Instability | Planned |
| PFM-07 | Seismic Slope Instability | Planned |
| PFM-08 | Seismic Deformation | Planned |
| PFM-09 | Overtopping - Seiche/Wave | Planned |
| PFM-10 | Internal Erosion - Sand Boils | Partial frontend |
| PFM-11 through PFM-32 | Various (names TBD) | Planned |
| PFM-33 | User-Defined Failure Mode | Planned |
Chapter: 09-consequences.mdx Status: Planned
Anticipated tables:
| Table Name | Purpose | Estimated Columns |
|---|---|---|
| ConsequenceEstimate | Per-screening consequence summary (life safety, economic, environmental) | ~12 |
| PopulationAtRisk | Population at risk data for different breach scenarios | ~10 |
| EconomicConsequence | Direct and indirect economic damage estimates | ~10 |
Chapter: 10-risk-analysis.mdx Status: Planned
Anticipated tables:
| Table Name | Purpose | Estimated Columns |
|---|---|---|
| RiskAnalysis | Per-screening risk computation results | ~10 |
| RiskResult | Individual failure mode risk contributions (APF × consequence per PFM) | ~8 |
| AnnualProbabilityOfFailure | Computed APF values per failure mode and total | ~8 |
Chapter: 11-auth-and-system.mdx Status: Planned
Anticipated tables:
| Table Name | Purpose | Estimated Columns |
|---|---|---|
| Users | User accounts | ~10 |
| Roles | Role definitions (Admin, Reviewer, Analyst, etc.) | ~5 |
| UserRoles | Many-to-many join table for user-role assignments | ~4 |
| Sessions | Active user sessions | ~6 |
| AuditLog | Audit trail of significant actions (who changed what, when) | ~8 |
| DashboardConfig | Per-user dashboard preferences and saved views | ~6 |
Chapter: 12-documents.mdx Status: Planned
Anticipated tables:
| Table Name | Purpose | Estimated Columns |
|---|---|---|
| Document | File/document metadata attached to a screening | ~10 |
| DocumentVersion | Version history for uploaded documents | ~8 |
| Timestamp | Migration Name | Tables Affected |
|---|---|---|
| 20260204213854 | InitialCreate | Screenings, Pfm01, Pfm01_DstSrp, Pfm01_UserSrp, Pfm01_ActiveSrp |
| 20260205192548 | AddScreeningEntity | Screenings (restructured) |
| 20260206160000 | AddMaxHazardLevelToPfm01 | Pfm01 (added MaxHazardLevel column) |
Screenings (1) ──→ (N) Pfm01 [Future FK, not yet enforced]
Pfm01 (1) ──→ (0..1) Pfm01_DstSrp [FK: Pfm01Id, UNIQUE, CASCADE DELETE]
Pfm01 (1) ──→ (0..1) Pfm01_UserSrp [FK: Pfm01Id, UNIQUE, CASCADE DELETE]
Pfm01 (1) ──→ (0..1) Pfm01_ActiveSrp [FK: Pfm01Id, UNIQUE, CASCADE DELETE]
Key architectural pattern: The three SRP child tables are always 1:1 with their parent Pfm entity. Data is never deleted when switching SRP modes — only the SrpMode column on the parent and the ActiveSrp record change. This preserves both DST-calculated and user-defined work.