mirror of
https://github.com/nowcommunity/nowdatabase.git
synced 2026-10-12 19:13:52 +03:00
Restructure documentation and add index
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
# Documentation Index
|
||||
|
||||
This folder is the canonical home for repository documentation.
|
||||
|
||||
## Quick Links
|
||||
|
||||
- Getting started: `README.md` (repo root)
|
||||
- Contributing: `documentation/contributing.md`
|
||||
- DevOps / running locally: `documentation/devops/setup.md`, `documentation/devops/commands.md`
|
||||
- Testing overview: `documentation/testing.md`
|
||||
- Frontend overview: `documentation/frontend/frontend.md`
|
||||
- Backend notes: `documentation/backend/`
|
||||
- Entity docs: `documentation/entities/`
|
||||
|
||||
## Reviewer Notes
|
||||
|
||||
- `documentation/reviewer-notes.md`
|
||||
|
||||
## FAQ / Troubleshooting
|
||||
|
||||
- `documentation/computer_specific_troubleshooting.md`
|
||||
- `documentation/lint-tsc-failure-analysis.md`
|
||||
- `documentation/faq/technical-questions.md`
|
||||
|
||||
## Full Document List (no orphans)
|
||||
|
||||
### Root-level docs
|
||||
|
||||
- `README.md`
|
||||
- `OCCURRENCE_UI_RENAME_PLAN.md`
|
||||
|
||||
### documentation/
|
||||
|
||||
- `documentation/README.md`
|
||||
- `documentation/CHANGELOG.md`
|
||||
- `documentation/api/time-units.md`
|
||||
- `documentation/architecture.md`
|
||||
- `documentation/backend/cross_search_export.md`
|
||||
- `documentation/backend/rate_limiting_for_emails.md`
|
||||
- `documentation/backend/server_side_pagination.md`
|
||||
- `documentation/backend/species_synonym_creation_flow.md`
|
||||
- `documentation/class_diagram.md`
|
||||
- `documentation/codex_planning_prompt.md`
|
||||
- `documentation/coding_prompt_template_fullstack.md`
|
||||
- `documentation/computer_specific_troubleshooting.md`
|
||||
- `documentation/contributing.md`
|
||||
- `documentation/cypress-performance-optimization.md`
|
||||
- `documentation/definition_of_done.md`
|
||||
- `documentation/devops/commands.md`
|
||||
- `documentation/devops/database.md`
|
||||
- `documentation/devops/docker.md`
|
||||
- `documentation/devops/github_actions.md`
|
||||
- `documentation/devops/openshift.md`
|
||||
- `documentation/devops/setup.md`
|
||||
- `documentation/devops/write-guard.md`
|
||||
- `documentation/entities/locality.md`
|
||||
- `documentation/entities/occurrence.md`
|
||||
- `documentation/entities/project.md`
|
||||
- `documentation/entities/reference.md`
|
||||
- `documentation/entities/reference_types.md`
|
||||
- `documentation/entities/region.md`
|
||||
- `documentation/entities/species.md`
|
||||
- `documentation/entities/time_bound.md`
|
||||
- `documentation/entities/time_unit.md`
|
||||
- `documentation/faq/technical-questions.md`
|
||||
- `documentation/frontend/components/EditableTable.md`
|
||||
- `documentation/frontend/components/SimpleTable.md`
|
||||
- `documentation/frontend/components/TableView.md`
|
||||
- `documentation/frontend/components/editingComponents/BasisForAgeSelection.md`
|
||||
- `documentation/frontend/components/editingComponents/DropdownSelector.md`
|
||||
- `documentation/frontend/components/editingComponents/DropdownSelectorWithSearch.md`
|
||||
- `documentation/frontend/components/editingComponents/EditableTextField.md`
|
||||
- `documentation/frontend/components/editingComponents/FieldWithTableSelection.md`
|
||||
- `documentation/frontend/components/editingComponents/FormTextField.md`
|
||||
- `documentation/frontend/components/editingComponents/RadioSelector.md`
|
||||
- `documentation/frontend/components/editingComponents/TimeBoundSelection.md`
|
||||
- `documentation/frontend/components/maps/CoordinateSelectionMap.md`
|
||||
- `documentation/frontend/components/maps/LocalitiesMap.md`
|
||||
- `documentation/frontend/components/maps/README.md`
|
||||
- `documentation/frontend/components/maps/SingleLocalityMap.md`
|
||||
- `documentation/frontend/frontend.md`
|
||||
- `documentation/frontend/routing.md`
|
||||
- `documentation/frontend/tables.md`
|
||||
- `documentation/functionality/cascading_effects.md`
|
||||
- `documentation/functionality/changelog.md`
|
||||
- `documentation/functionality/put_requests_for_db_entries.md`
|
||||
- `documentation/functionality/species_merge.md`
|
||||
- `documentation/functionality/species_taxonomy_checks.md`
|
||||
- `documentation/functionality/table_column_visibility.md`
|
||||
- `documentation/lint-tsc-failure-analysis.md`
|
||||
- `documentation/reference-validation-messages.md`
|
||||
- `documentation/reviewer-notes.md`
|
||||
- `documentation/testing.md`
|
||||
- `documentation/update-log-navigation-flow.md`
|
||||
- `documentation/user_rights.md`
|
||||
@@ -0,0 +1,46 @@
|
||||
# Technical questions
|
||||
|
||||
This note answers `nowcommunity/nowdatabase#101`.
|
||||
|
||||
## 1) Calculated / derived fields
|
||||
|
||||
In this codebase, there are a few different “calculated field” patterns:
|
||||
|
||||
- **Pure display formatting (client-side):** Some table columns are derived from multiple persisted fields at render time.
|
||||
- Example: developmental/functional crown type strings are formatted client-side via
|
||||
`frontend/src/shared/types/util.ts` (`formatDevelopmentalCrownType`, `formatFunctionalCrownType`) and used as
|
||||
`accessorFn` in table definitions (e.g. `frontend/src/components/Species/SpeciesTable.tsx`).
|
||||
- **UI-only edit metadata (client-side):** Edit payload types wrap nested arrays as `Editable<T>` and add `rowState`
|
||||
(e.g. `"new"`, `"removed"`) to drive add/remove UI behavior. This field is not a DB column.
|
||||
- Type-level definition: `frontend/src/shared/types/util.ts` (`EditDataType`, `Editable`, `RowState`).
|
||||
- Backend write-layer interprets `rowState` for join tables via `applyListChanges`:
|
||||
`backend/src/services/write/writeOperations/writeHandler.ts`.
|
||||
- **Client-side derived state / pagination metadata:** Some UI metadata is computed from server responses and cached in
|
||||
Redux/context; it is not persisted to the DB.
|
||||
- Example: `frontend/src/hooks/usePaginatedQuery.ts` computes table pagination metadata.
|
||||
|
||||
### Should derived values be written to the DB?
|
||||
|
||||
General guidance for this repository:
|
||||
|
||||
- Prefer **not** storing derived display-only values in the DB (avoid duplication and drift).
|
||||
- Store (or materialize) a derived value only if you need it for **server-side filtering/sorting** or it is
|
||||
**expensive** to compute and frequently used.
|
||||
- In those cases, prefer computing it in SQL/query code first; only persist if you have a clear contract and
|
||||
a single source of truth.
|
||||
|
||||
## 2) How does deletion of an entire entity show in tables?
|
||||
|
||||
For “entity delete” routes, the backend uses **hard deletes** (SQL `DELETE`) rather than a soft-delete flag:
|
||||
|
||||
- Low-level delete: `backend/src/services/write/writeOperations/databaseHandler.ts` (`DELETE FROM ...`).
|
||||
- Write handler delete: `backend/src/services/write/writeOperations/writeHandler.ts` (`deleteObject`) also logs the
|
||||
removed values before deleting (so update/audit views can still show a deletion event).
|
||||
|
||||
Practical UI behavior:
|
||||
|
||||
- In list tables (e.g. `/locality/all`), a deleted entity typically **disappears from results** after the next fetch.
|
||||
- In edit-mode tables that represent **join rows** (e.g. child lists inside a DetailView), clicking remove generally
|
||||
marks the row with a UI-only `rowState` (often rendered with a red background) until the user saves.
|
||||
- Example: `frontend/src/components/DetailView/common/EditableTable.tsx`.
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# Routing & Unsaved Changes
|
||||
|
||||
This frontend uses React Router v6 in **data router mode** (`createBrowserRouter` + `RouterProvider`). This enables `useBlocker` and related APIs for navigation interception.
|
||||
|
||||
## Data Router Basics
|
||||
|
||||
- Entry point: `frontend/src/router/index.tsx` exports the router created with `createBrowserRouter` and is consumed by `RouterProvider` in `frontend/src/main.tsx`.
|
||||
- Global layout: `App` renders global chrome and an `Outlet` for child routes.
|
||||
- When adding routes, define them in `router/index.tsx` using the data-router configuration (array or `createRoutesFromElements`).
|
||||
|
||||
## Enabling Unsaved-Changes Prompts
|
||||
|
||||
Unsaved-change blocking is provided by `UnsavedChangesProvider` and the `useUnsavedChangesPrompt` hook.
|
||||
|
||||
**Provider**
|
||||
|
||||
- Wrap any page containing a form or editable detail view with `UnsavedChangesProvider`.
|
||||
- The provider listens to `useBlocker` and renders the shared confirmation dialog.
|
||||
|
||||
**Hook**
|
||||
|
||||
- In a form component, call `const { setDirty } = useUnsavedChangesPrompt(isDirty)` where `isDirty` comes from your form state (`react-hook-form`’s `formState.isDirty`, etc.).
|
||||
- Set dirty to `false` immediately before/after a successful submit so navigation to the next page isn’t blocked.
|
||||
- If submit fails, set dirty back to `true` so protection remains while the user fixes errors.
|
||||
|
||||
### Minimal Example
|
||||
|
||||
```tsx
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { UnsavedChangesProvider } from '@/components/UnsavedChangesProvider';
|
||||
import { useUnsavedChangesPrompt } from '@/hooks/useUnsavedChangesPrompt';
|
||||
|
||||
export const ExampleFormPage = () => {
|
||||
const { handleSubmit, formState: { isDirty }, reset } = useForm();
|
||||
const { setDirty } = useUnsavedChangesPrompt(isDirty);
|
||||
|
||||
const onSubmit = handleSubmit(async values => {
|
||||
setDirty(false);
|
||||
try {
|
||||
await save(values);
|
||||
reset(values);
|
||||
} catch {
|
||||
setDirty(true);
|
||||
throw new Error('Save failed');
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<UnsavedChangesProvider>
|
||||
<form onSubmit={onSubmit}>
|
||||
{/* form fields */}
|
||||
<button type="submit">Save</button>
|
||||
</form>
|
||||
</UnsavedChangesProvider>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Detail Views
|
||||
|
||||
- For detail pages that use shared context (e.g., `DetailView`), place the provider at the page level and wire `useUnsavedChangesPrompt` to the context’s `isDirty` flag inside a small tracker component.
|
||||
|
||||
## Notes
|
||||
|
||||
- `useBlocker` requires the data router; ensure new routes continue to use `createBrowserRouter`.
|
||||
- The dialog text can be customized via `UnsavedChangesProvider` props or by passing a `message` option to `useUnsavedChangesPrompt`.
|
||||
|
||||
@@ -1,184 +0,0 @@
|
||||
# Feature Plan: Handle invalid Time Bound IDs in Time Unit PUT requests
|
||||
|
||||
## 1) Assumptions & Scope
|
||||
- The issue is in `PUT /time-unit` backend handling where non-existing `up_bnd` or `low_bnd` can still reach persistence and surface as an unhandled internal error.
|
||||
- Existing role restrictions for time-unit writes (`Admin`, `EditUnrestricted`) remain unchanged.
|
||||
- No schema change is required; this is a validation and error-mapping hardening task.
|
||||
- Impacted areas:
|
||||
- Backend request validation and error handling in `backend/src/routes/timeUnit.ts`
|
||||
- Shared validation behavior used by backend (`backend/src/services/timeUnit.ts`, shared validator if needed)
|
||||
- Backend API tests for time-unit update/create invalid bounds flows
|
||||
- Frontend error messaging path for time-unit save failures only if backend response shape changes
|
||||
- Goal behavior:
|
||||
- Invalid/non-existent `up_bnd`/`low_bnd` should return a client error (`400` or existing project-standard `403`) with actionable payload, never `500`.
|
||||
- Existing valid flows and cascade checks must remain intact.
|
||||
|
||||
## 2) High-Level Plan
|
||||
1. Reproduce and characterize failure path
|
||||
- Add/adjust API test case for `PUT /time-unit` with non-existing `up_bnd` and/or `low_bnd` to confirm current `500` behavior.
|
||||
2. Harden backend validation before write
|
||||
- In time-unit route/service layer, explicitly detect when bound IDs are provided but corresponding time bound lookup returns no record.
|
||||
- Return structured validation error response immediately.
|
||||
3. Normalize database/Prisma error mapping
|
||||
- Add defensive handling for Prisma foreign-key violations as a final guardrail, mapping to deterministic client error.
|
||||
4. Align frontend handling (if necessary)
|
||||
- Ensure existing UI save flow shows validation message from backend instead of generic failure when invalid IDs slip through.
|
||||
5. Validate and regress
|
||||
- Extend backend tests for both single-invalid and dual-invalid bound IDs.
|
||||
- Run lint/typecheck/tests in impacted scope.
|
||||
|
||||
## 3) Tasks (JSON)
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "T1",
|
||||
"title": "Add failing regression coverage for invalid Time Bound references",
|
||||
"summary": "Create backend API tests for PUT /time-unit where up_bnd and/or low_bnd reference non-existent IDs and assert non-500 behavior with structured error payload.",
|
||||
"app": "backend",
|
||||
"files_touched": [
|
||||
"backend/src/api-tests/timeUnit/update.test.ts",
|
||||
"backend/src/api-tests/timeUnit/create.test.ts"
|
||||
],
|
||||
"migrations": false,
|
||||
"permissions": [
|
||||
"Requires authenticated users with Admin or EditUnrestricted role"
|
||||
],
|
||||
"acceptance_criteria": [
|
||||
"Test demonstrates invalid bound IDs are rejected with client error status",
|
||||
"Response includes consumable validation message and does not return 500"
|
||||
],
|
||||
"test_plan": [
|
||||
"Run targeted timeUnit API tests",
|
||||
"Verify existing valid update/create tests still pass"
|
||||
],
|
||||
"estimate_hours": 1.5,
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "T2",
|
||||
"title": "Enforce explicit bound existence validation in PUT /time-unit",
|
||||
"summary": "Update backend route/service logic to treat provided up_bnd/low_bnd IDs as invalid when lookup misses, and short-circuit with structured validation response before writeTimeUnit.",
|
||||
"app": "backend",
|
||||
"files_touched": [
|
||||
"backend/src/routes/timeUnit.ts",
|
||||
"backend/src/services/timeUnit.ts",
|
||||
"frontend/src/shared/validators/timeUnit.ts"
|
||||
],
|
||||
"migrations": false,
|
||||
"permissions": [
|
||||
"Preserve existing role enforcement via requireOneOf([Role.Admin, Role.EditUnrestricted])"
|
||||
],
|
||||
"acceptance_criteria": [
|
||||
"PUT with non-existing up_bnd is rejected consistently",
|
||||
"PUT with non-existing low_bnd is rejected consistently",
|
||||
"PUT with both non-existing IDs is rejected consistently",
|
||||
"No invalid ID request reaches DB write path"
|
||||
],
|
||||
"test_plan": [
|
||||
"Re-run timeUnit API tests",
|
||||
"Add validator unit test(s) if validation contract changes"
|
||||
],
|
||||
"estimate_hours": 2.0,
|
||||
"priority": "high"
|
||||
},
|
||||
{
|
||||
"id": "T3",
|
||||
"title": "Map Prisma FK constraint errors to deterministic client response",
|
||||
"summary": "Add catch-path handling for Prisma known request FK errors as a safety net to return project-standard validation/conflict response instead of 500.",
|
||||
"app": "backend",
|
||||
"files_touched": [
|
||||
"backend/src/routes/timeUnit.ts",
|
||||
"backend/src/services/write/timeUnit.ts"
|
||||
],
|
||||
"migrations": false,
|
||||
"permissions": [
|
||||
"Same write permissions as existing endpoint"
|
||||
],
|
||||
"acceptance_criteria": [
|
||||
"Unexpected FK violations in time-unit write return client error payload",
|
||||
"Unhandled 500 for invalid bound references is eliminated"
|
||||
],
|
||||
"test_plan": [
|
||||
"Add integration assertion for mapped FK error path (if reachable)",
|
||||
"Smoke-test PUT /time-unit valid update path"
|
||||
],
|
||||
"estimate_hours": 1.0,
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "T4",
|
||||
"title": "Verify frontend error surfacing for time-unit save failures",
|
||||
"summary": "Confirm frontend Time Unit edit/create views display backend validation errors for invalid bounds and avoid generic crash-like messaging.",
|
||||
"app": "frontend",
|
||||
"files_touched": [
|
||||
"frontend/src/components/TimeUnit/TimeUnitDetails.tsx",
|
||||
"frontend/src/components/DetailView/common/UpdateTab.tsx",
|
||||
"frontend/src/redux/timeUnitReducer.ts"
|
||||
],
|
||||
"migrations": false,
|
||||
"permissions": [
|
||||
"UI remains editable only for authorized roles"
|
||||
],
|
||||
"acceptance_criteria": [
|
||||
"User sees clear validation message when save includes invalid bound IDs",
|
||||
"No UI regression for normal time-unit save"
|
||||
],
|
||||
"test_plan": [
|
||||
"Add/adjust component or reducer tests for error state display",
|
||||
"Manual smoke check in Time Unit edit form"
|
||||
],
|
||||
"estimate_hours": 1.5,
|
||||
"priority": "medium"
|
||||
},
|
||||
{
|
||||
"id": "T5",
|
||||
"title": "Document API error contract for invalid bound references",
|
||||
"summary": "Update API and entity docs to specify expected status code and payload when Time Unit references missing Time Bound IDs.",
|
||||
"app": "docs",
|
||||
"files_touched": [
|
||||
"documentation/api/time-units.md",
|
||||
"documentation/entities/time_unit.md",
|
||||
"documentation/CHANGELOG.md"
|
||||
],
|
||||
"migrations": false,
|
||||
"permissions": [
|
||||
"N/A"
|
||||
],
|
||||
"acceptance_criteria": [
|
||||
"Docs reflect non-500 behavior and example error payload",
|
||||
"Changelog records bugfix behavior change"
|
||||
],
|
||||
"test_plan": [
|
||||
"Documentation review for accuracy against implemented response"
|
||||
],
|
||||
"estimate_hours": 0.5,
|
||||
"priority": "low"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## 4) Risks & Mitigations
|
||||
- Auth failures: endpoint hardening could accidentally bypass/alter auth flow.
|
||||
- Mitigation: keep `requireOneOf` unchanged and include unauthorized test coverage.
|
||||
- Behavior drift in status codes (`403` vs `400`).
|
||||
- Mitigation: align with existing NOW validation conventions and update docs/tests to one canonical outcome.
|
||||
- Hidden FK paths still causing 500.
|
||||
- Mitigation: explicit pre-write validation + Prisma error mapping guardrail.
|
||||
- Frontend mismatch with new error payload shape.
|
||||
- Mitigation: preserve existing validation error format where possible; add adapter logic only if needed.
|
||||
- Regression in create/update cascade checks.
|
||||
- Mitigation: run full time-unit create/update test suite and targeted locality-cascade tests.
|
||||
|
||||
## 5) Out of Scope
|
||||
- Legacy data cleanup/migration for already persisted inconsistent records.
|
||||
- UI redesign or theming changes beyond existing MUI patterns.
|
||||
- Broad refactor of shared validation framework unrelated to Time Unit bounds.
|
||||
- General performance tuning not specific to this bug.
|
||||
|
||||
## 6) Definition of Done
|
||||
- [ ] Invalid `up_bnd`/`low_bnd` PUT payloads return structured non-500 client errors.
|
||||
- [ ] Authorized/unauthorized behavior remains correct.
|
||||
- [ ] Backend tests added/updated and passing for regression scenarios.
|
||||
- [ ] Linting and type checks pass in touched scopes.
|
||||
- [ ] API/entity docs and changelog updated with new behavior.
|
||||
- [ ] CI passes for backend/frontend checks relevant to touched files.
|
||||
@@ -0,0 +1,24 @@
|
||||
# Reviewer Notes
|
||||
|
||||
This file collects high-signal review notes that previously lived in the root `README.md`.
|
||||
|
||||
## Locality climate pollen validation
|
||||
|
||||
- Locality pollen fields (`pers_pollen_ap`, `pers_pollen_nap`, `pers_pollen_other`) now require integer values in the range `0...100`.
|
||||
- Shared locality validation enforces combined pollen constraint `AP + NAP + OP <= 100` and surfaces the same message in form validation feedback.
|
||||
- Backend locality write validation now evaluates pollen totals with full update context so partial updates cannot bypass the combined-value rule.
|
||||
- API tests include invalid create/update pollen payload coverage for non-integer, out-of-range, and total-above-100 cases.
|
||||
|
||||
## Locality age method persistence
|
||||
|
||||
- In Locality edit mode, the Age tab now preserves method-specific values when switching Dating method between `Time unit`, `Absolute`, and `Composite`.
|
||||
- Switching methods restores previously entered age values (e.g., `min_age`, `max_age`, basis/fraction selections) instead of clearing them.
|
||||
- Locality age validation now evaluates required basis fields against the currently active dating method, while allowing preserved values from other methods to remain in draft state.
|
||||
- See `documentation/CHANGELOG.md` for release-level tracking.
|
||||
|
||||
## Occurrence terminology update
|
||||
|
||||
- User-facing UI captions now use **Occurrence/Occurrences** in navigation, cross-search, and Species detail surfaces.
|
||||
- This is a presentation-only rename: backend/database identifiers are unchanged, including the `now_ls` table and related API payload fields.
|
||||
- See `frontend/src/constants/occurrenceLabels.ts` for centralized frontend label constants and `frontend/tests/locality-species-ui-label-inventory.md` for inventory coverage.
|
||||
|
||||
Reference in New Issue
Block a user