# Frontend Probe Mapping

This file captures the backend paths and payload shapes the current frontend
actually uses. It is the source-of-truth companion for
`frontend_case_probe.py` and `frontend_case_specs.json`.

## Why this exists

The existing `tests/e2e-api` suite is backend-oriented: many cases hit
hand-written endpoints and payloads that do not match the current UI.

The frontend probe layer is different:

- it mirrors real frontend request paths
- it uses the same payload format the UI sends today
- it is intended to become a CI smoke gate for user-visible regressions
- it can separate read-only probes from mutation probes for safer local use

## Key mappings

### 1. Create Quote from Opportunity

Frontend source:

- `frontend/src/registries/ActionRegistry.ts`
- `frontend/src/infrastructure/api/data/actions.ts`
- `backend/internal/controlplane/packs/standard_actions.json`

Frontend behavior:

- user clicks the `Create Quote` action on Opportunity detail
- frontend calls `dataAPI.executeStandardCreateRecord(action.id, record.Id)`

Backend call:

```http
POST /services/data/v63.0/actions/standard/createRecord
Content-Type: application/json

{
  "inputs": [
    {
      "actionId": "act_opportunity_createquote",
      "recordId": "<OpportunityId>"
    }
  ]
}
```

Important difference from the current backend-oriented cases:

- this is not a direct `POST /services/data/v63.0/sobjects/Quote`
- the workflow, field copy, default values, and child copy are owned by the backend action config

### 2. Opportunity Products related list

Frontend source:

- `frontend/src/features/record/components/RelatedList.tsx`
- `frontend/src/core/hooks/useRecordList.ts`

Frontend behavior:

- related lists are loaded with `useRecordList`
- `useRecordList` always builds SOQL and calls `/services/data/v63.0/query?q=...`

Backend call shape:

```http
GET /services/data/v63.0/query?q=SELECT Id,OpportunityId,PricebookEntryId,Product2Id,ListPrice,Discount,Unit_Net_Price__c,Quantity,Net_Fees__c,Service_Start_Date__c,Service_End_Date__c FROM OpportunityLineItem WHERE OpportunityId = '<OpportunityId>' ORDER BY CreatedDate DESC LIMIT 50
```

Important difference:

- frontend does not use `/services/data/v63.0/sobjects/OpportunityLineItem?fields=...&q=...`

### 3. Quote Products related list

Frontend source:

- `frontend/src/features/record/components/RelatedList.tsx`
- `backend/internal/features/metadata/virtual_relationships.go`

Frontend behavior:

- the UI label stays `Quote Line Items`
- runtime query resolution is redirected to `quote_product__c`
- lookup field is `Quote__c`, not `QuoteId`

Backend call shape:

```http
GET /services/data/v63.0/query?q=SELECT Id,Quote__c,Product__c,List_Price__c,Discount__c,Unit_Net_Price__c,Quantity__c,NetFees__c,Service_Start_Date__c,Service_End_Date__c FROM quote_product__c WHERE Quote__c = '<QuoteId>' ORDER BY CreatedDate DESC LIMIT 50
```

Important difference:

- frontend does not query `QuoteLineItem` directly for this page
- current `ULI-*` and `R-564/R-572` style cases need to account for this virtual mapping

### 4. Submit for Approval

Frontend source:

- `frontend/src/features/approvals/components/SubmitApprovalModal.tsx`
- `frontend/src/infrastructure/api/approvals.ts`

Backend call:

```http
POST /services/data/v63.0/process/approvals
Content-Type: application/json

{
  "ObjectApiName": "Quote",
  "RecordId": "<QuoteId>",
  "Comments": "optional string",
  "nextApproverIds": ["005..."]
}
```

Important difference:

- frontend uses `ObjectApiName` and `RecordId`
- this does not match older payloads that used `objectId` or mixed naming

### 5. Product lookup search

Frontend source:

- `frontend/src/components/form/SearchableLookup.tsx`
- `frontend/src/infrastructure/api/data/search.ts`

Backend call:

```http
GET /services/data/v63.0/search?q=FIND {TiDB} IN ALL FIELDS RETURNING Product2(Id, Name)
```

Important difference:

- frontend lookup search is SOSL-backed
- it is not a `GET /query` or `GET /sobjects/Product2?...` path

### 6. Create Order from Quote page

Frontend source:

- `frontend/src/pages/ObjectView.tsx`
- `frontend/src/features/record/components/RelatedList.tsx`
- `frontend/src/features/record/components/MetadataRecordForm.tsx`

Frontend behavior:

- route is `/object/order/new?QuoteId=<id>&AccountId=<id>&Product_Family__c=<value>`
- before rendering the form, frontend reads:
  - `GET /services/data/v63.0/sobjects/Quote/<QuoteId>`
  - optionally `GET /services/data/v63.0/sobjects/Contract/<ContractId>`
- form submit uses generic create-record behavior:

```http
POST /services/data/v63.0/sobjects/Order
Content-Type: application/json

{
  "QuoteId": "<QuoteId>",
  "AccountId": "<AccountId>",
  "Product_Family__c": "<Product Family>",
  "EffectiveDate": "<YYYY-MM-DD>",
  "Status": "Draft",
  "...": "other editable form fields"
}
```

Important difference:

- frontend uses `QuoteId`, not `QuoteId__c`
- frontend performs Quote/Contract pre-validation before the final POST
- exact submitted fields are layout-driven because `MetadataRecordForm` only sends editable fields that are visible on the form

## CI guidance

This probe layer is suitable for CI only when the environment is deterministic:

- backend startup is isolated for the test run
- DB state is seeded with stable fixture records
- required IDs are injected via env vars or prior probe extraction

It is not suitable as a hard PR gate if it points at a shared mutable business DB.

Recommended operating mode:

- local default: run read-only probes only
- CI on isolated DB: enable mutation probes as well
