uhifadhi / incident-module
Incidents: what happened in an area — poaching, human–wildlife conflict (with fines and compensation), unauthorized construction and roadkill, as one record type served to every department that needs it.
Package info
github.com/uhifadhilabs/incident-module
Type:symfony-bundle
pkg:composer/uhifadhi/incident-module
Requires
- php: >=8.4
- doctrine/dbal: ^4.0
- doctrine/doctrine-bundle: ^3.0
- doctrine/orm: ^3.5
- fundistadi/postgis-bundle: ^0.5
- symfony/asset: ^7.3 || ^8.0
- symfony/asset-mapper: ^7.3 || ^8.0
- symfony/config: ^7.3 || ^8.0
- symfony/console: ^7.3 || ^8.0
- symfony/dependency-injection: ^7.3 || ^8.0
- symfony/framework-bundle: ^7.3 || ^8.0
- symfony/http-foundation: ^7.3 || ^8.0
- symfony/http-kernel: ^7.3 || ^8.0
- symfony/twig-bundle: ^7.3 || ^8.0
- symfony/uid: ^7.3 || ^8.0
- symfony/ux-icons: ^3.4
- uhifadhi/area-module: ^0.3 || ^0.4 || ^0.5 || ^0.6
- uhifadhi/map-module: ^0.1 || ^0.2
- uhifadhi/module-contracts: ^0.3
- uhifadhi/widget-module: ^0.1
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.95
- phpstan/phpstan: ^2.2
- phpunit/phpunit: ^13.2
- symfony/browser-kit: ^7.3 || ^8.0
- symfony/css-selector: ^7.3 || ^8.0
- symfony/security-bundle: ^7.3 || ^8.0
- symfony/security-core: ^7.3 || ^8.0
- symfony/security-csrf: ^7.3 || ^8.0
- symfony/security-http: ^7.3 || ^8.0
- symfony/stimulus-bundle: ^2.20 || ^2.30 || ^2.0
- uhifadhi/seam-module: ^0.1 || ^0.2
- uhifadhi/shell-module: ^0.6
- uhifadhi/storage-module: ^0.2
- uhifadhi/team-module: ^0.4
Suggests
- uhifadhi/seam-module: The per-area module catalogue this module registers itself in, and the grid an area’s Modules tab draws. Without it the incident screens are reachable only by their own routes.
- uhifadhi/shell-module: The page frame every incident screen renders in. Without it the screens have no frame to extend and the module is a register with no way to read it.
- uhifadhi/storage-module: To put an incident’s evidence on the platform’s Files hub (/files). Register the bundle and IncidentFileSource registers itself; without it, incidents simply do not appear there.
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-05 14:00:56 UTC
README
What happened in an area, recorded once: poaching, human–wildlife conflict (with the fines and compensation that follow), compliance and encroachment, and wildlife mortality. A uhifadhi module bundle.
What it is
An incident is one event, in one area, at one place, in one category, at one
point in a five-state workflow — reported → verified → in progress → resolved → closed. One record type serves every reader: Protection and Ecology read
subsets of one taxonomy rather than each keeping their own copy.
The module ships seven incident* tables, the report flow, the case file, a
sixteen-widget dashboard surface composed on uhifadhi/widget-module, and a
seeded, configurable taxonomy of four kinds and sixteen sub-categories.
Installation
composer require uhifadhi/incident-module
The bundle registers via Flex ("type": "symfony-bundle"), which adds
Uhifadhi\Incident\UhifadhiIncidentBundle to config/bundles.php.
Getting started
Then, in the host:
-
Answer the user contract. Five columns name a person — who reported the incident, who it is assigned to, who acted on the event, who linked it to another, and the team member behind a party to it — and none of them names an account class. They are mapped to
Uhifadhi\ModuleContracts\Entity\UserInterface, and the installation resolves that interface to whatever it calls its people. Installuhifadhi/team-moduleand the answer arrives with it (0.3.2 and later states the resolution from its own bundle); otherwise write one line naming your own class, under theorm:key already inconfig/packages/doctrine.yaml:doctrine: orm: resolve_target_entities: Uhifadhi\ModuleContracts\Entity\UserInterface: App\Entity\Person
Until something answers it, the bundle installs and the kernel boots, but anything that walks the metadata — including the
diffbelow — stops on the unresolved interface. Deleting an account later sets those five columns null and leaves the incidents standing, which is why each of those records keeps the person's name beside the relation. -
Migrate. The bundle maps its own entities, so no doctrine mappings block is needed — just
bin/console doctrine:migrations:diffand review. It adds sevenincident*tables and nothing else; it alters no host table. -
Install the taxonomy — the one step that is not automatic, because it is a data decision and a bundle that wrote rows into a host's database on boot would be making it for them:
bin/console incidents:taxonomy:sync
Idempotent and non-destructive. Run it again after any change to
incident.taxonomy; a kind of incident that has left the configuration is left alone, never deleted, because case files are filed against it. The three Stimulus controllers —incident-map,incident-board,incident-report— need no step of their own: Flex synchronisesassets/controllers.jsonfrom this package's ownassets/package.jsonon everycomposer require/update, because the package declares thesymfony-uxkeyword.
Everything this module binds to now arrives as a module of its own, and composer
installs all of them: the area an incident happens in and its zones from
uhifadhi/area-module, the dashboard framework from uhifadhi/widget-module,
Leaflet and the map seam from uhifadhi/map-module, and the two seam contracts
from uhifadhi/module-contracts. Three more are suggestions rather than
requirements: uhifadhi/shell-module is the page frame every screen renders in,
uhifadhi/seam-module is the per-area catalogue this module registers itself in,
and uhifadhi/storage-module puts an incident's evidence on the Files hub.
The one thing an installation still provides is the ACCOUNT CLASS behind the
person contract — see the user contract above. uhifadhi/team-module answers it
from its own bundle; an installation with an account class of its own names it in
one line of resolve_target_entities. symfony/ux-icons with the lucide set
imported is the other standing expectation.
Learn more
- Charter — one record type and many readers, why departments are a lens and never a fence, and why the dashboard rides the host's framework.
- The model — the seven tables, and the three rules about money, filing and provenance that somebody will otherwise re-argue.
- The workflow, and the seam under it — the five places,
their guards, and how
IncidentWorkflowmaps one-to-one onto a Symfonystate_machine. - Screens — the routes, why the five design directions are presets rather than pages, and the query string another module files with.
- Permissions — the two declared permissions and the sentences the host's matrix prints under them.
- Configuration —
config/packages/incident.yaml, the taxonomy tree, and whatleadsdoes and does not decide. - Evidence on the Files hub — the optional
uhifadhi/storage-moduleseam, and what this module honestly knows about a file. - Dev tooling —
incidents:seed:demo, the design's sample month, and why it is registered only whereincident.dev_toolsis on. - Development —
composer check, the tooling levels, and the real-PostGIS test suites.
License
AGPL-3.0-or-later — see LICENSE: the same license as the uhifadhi host this module plugs into. Use, modify and self-host freely; if you offer a modified version to users over a network, they are entitled to the source of what they're running.