Skip to content

Next-gen: javaabu/locations v1.0 #12

Open

Description

@n4ff4h

Location data management: countries, states, cities, atolls and islands, with seedable data for every country and every Maldivian atoll and island, plus views and controllers.

Package: javaabu/locations · Target: v1.0 · Wave: 3
Depends on: javaabu/permissions, javaabu/forms, @javaabu/forms, javaabu/manifest
Optional: javaabu/translatable

Built to the next-gen general rules: CI with test and coverage badges, full docs for people and agents, Laravel Boost guideline and skill, Javaabu codestyle, PHP 8.4 / Laravel 12+ (Laravel packages) or TypeScript (JS packages), Dhivehi and RTL, Blade and Inertia React for views, overridable controllers and policies with javaabu/permissions.

Checklist

Developed by an agent orchestrated from the Javaabu next-gen plan. Cross-package requests go in the dependency's repo with the cross-package label.

Activity

  1. added
    next-genNext-gen Javaabu packages
    epicTracks a whole package
    on Nov 4, 2026
  2. n4ff4h commented on Nov 5, 2026

    @n4ff4h
    CollaboratorAuthor

    Implementation plan

    Studied: STANDARDS/PLAN, ../bulk-actions and ../permissions (structure, CI, route macros, ViewRenderer, React pages), ../settings (stencil forms rendered by either stack), stencil (#14, custom fields), forms v2 select2 cascading (child, filter-field, ajax-url) and its React parity (useCascadeOptions), translatable's package-author contract (n4ff4h/translatable#10).

    Data and sources (#3)

    Raw data lives in versioned JSON under database/data/. Build scripts in bin/ regenerate it from the sources, so every change is reviewable:

    Data Source
    Countries: ISO 3166-1 alpha-2, alpha-3, numeric, English short and official names ISO 3166-1 through Debian iso-codes data/iso_3166-1.json (it tracks the ISO OBP)
    Country names in Dhivehi iso-codes' community iso_3166-1/dv.po (208 names). The rest are translated here and flagged for human review
    Calling codes Google libphonenumber metadata
    Atolls: names, official names, letter codes, Thaana abbreviations, ISO 3166-2:MV codes ISO 3166-2:MV, cross-checked with the Decentralization Act's list of administrative divisions
    Islands: official feature codes (LD0002), English and Dhivehi names, category, atoll capital, coordinates, area Maldives Land and Survey Authority (MLSA) OneMap island register, IslandList_20211101.csv (1,560 features)
    Inhabited-island spellings and council names Cross-checked against the Local Government Authority's council list. Differences go in a review list, not silent edits

    Unnamed features in the register are kept in the data file but not seeded by default.

    Models and migrations (#2)

    Country, State, City, Atoll, Island. All are config-swappable, with a table prefix option.

    • Names: name (English) and dhivehi_name, plus localizedName() for the current locale. The column isn't called name_dv because that's translatable's suffix convention and would collide (Optional javaabu/translatable support #10).
    • Islands: IslandCategories enum (residential, resort, industrial, institutional, uninhabited, …) with en/dv labels.
    • Codes: ISO, letter and FCODE codes are unique and indexed.
    • Relationships: country → states → cities; country → atolls → islands. Atolls and islands have no country column, since they're Maldivian.

    Inputs (#4)

    • Lookup endpoint: Route::locationOptions() adds a read-only JSON endpoint per type. It follows the forms select2 contract: filter[search], filter[country], filter[atoll], per_page=-1, fields.
    • Blade: <x-locations::country-select>, atoll-select, island-select, state-select, city-select, and a cascading <x-locations::address-selects>. They wrap <x-forms::select2> with child and filter-field.
    • React: CountrySelect, AtollSelect, IslandSelect (…) on @javaabu/forms Select2, exported as @javaabu/locations through manifest.
    • Stencil: CountrySelect::make() and friends, as custom stencil fields.

    Pages, controllers and permissions (#8, #9)

    • Routes: Route::locations([...]) registers CRUD for countries, atolls, islands, states and cities. It takes guard, prefix, name, middleware, only and controllers options.
    • Controllers: one overridable controller per resource, on a shared base. Controllers are overridable from config.
    • Forms: defined once as stencil form classes, then rendered by Blade (javaabu/forms) or Inertia React (locations::Countries/Index …). The stack is chosen with config('locations.views').
    • Permissions: view_/edit_/delete_{countries,atolls,islands,states,cities}, registered through javaabu/permissions, with ResourcePolicy subclasses overridable from config or Gate::policy().
    • Tests: feature tests with two guards.

    Optional translatable (#10)

    The JSON storage contract applies. Package models ship translatable subclasses, chosen when translatable is enabled. Seeding writes the Dhivehi names into the translations. Tested with and without it, plus a CI leg without it.

    Order of work

    1. Foundations: PHP 8.4 / Laravel 12+, CI matrix, coverage and codestyle #1 Foundations
    2. Models and migrations #2 Models and migrations
    3. Seed data: all countries, all Maldivian atolls and islands #3 Data, build scripts and seeders
    4. Overridable controllers, policies and default permissions (RBAC, multi-auth) #9 Routes, controllers and policies
    5. Blade (javaabu/forms) and Inertia React (@javaabu/forms) views #8 Views (both stacks)
    6. Location inputs #4 Inputs
    7. Optional javaabu/translatable support #10 Translatable
    8. Dhivehi localization and RTL #7 Dhivehi and RTL
    9. Documentation for people and agents #5 and Laravel Boost guideline and skill #6 Docs and Boost

    Open questions (decided, recorded here)

    • States and cities: they get models, pages and inputs. Only ISO 3166-2 top-level subdivisions are seeded as states (optional seeder, not for MV, whose subdivisions are the atolls). Cities are left for apps.
    • iso-codes licensing: its translations are LGPL-2.1+. The derived file keeps attribution, and the docs flag it for the user to confirm.
  3. n4ff4h commented on Nov 5, 2026

    @n4ff4h
    CollaboratorAuthor

    Final summary

    javaabu/locations v1.0 is on main. CI is green on the full matrix:

    • PHP 8.4/8.5 × Laravel 12/13 × lowest/stable
    • Pint
    • a leg without translatable and Inertia
    • a production-dependency check (no javaabu/helpers or javaabu/settings ^1)
    • a seed-data job that rebuilds the data from its sources and validates it
    • the JS job: lint, format, typecheck, Vitest with coverage

    There are 105 PHPUnit tests and 19 Vitest tests. Every next-gen issue is closed except #11 (needs a human).

    What was built

    • Models. Country, State, City, Atoll, Island, configurable through config('locations.models') and config('locations.tables').
      • Names: English name and dhivehi_name, with localizedName(). Islands also have formattedName() (ހއ. ދިއްދޫ).
      • Islands: an IslandCategories enum, plus scopes (ofAtoll('HA'), inhabited(), search(), ordered()), factories, and restricted deletes.
    • Seed data. Plain JSON in database/data, built by bin/build-data.php from pinned source snapshots and checked by bin/validate-data.php. The sources are cited in docs/advanced-usage/data-sources.md.
      • Countries: all 249 from ISO 3166-1 via Debian iso-codes v4.20.1. Dhivehi names come from iso-codes' translations for 191 of them. Calling codes come from libphonenumber v9.0.42.
      • Atolls: 20 administrative atolls and Malé City, with letter codes, Thaana codes and ISO 3166-2:MV codes. Official Dhivehi names come from the LGA's council names.
      • Islands: all 1,560 features of the MLSA/Geomatics OneMap island register (1,131 named), with feature codes, Dhivehi names, categories, coordinates and areas. The register matches the live 2024 layer as of today, and every LGA island council matches a residential island.
      • States: 3,569 top-level ISO 3166-2 subdivisions (optional).
      • Seeding: php artisan locations:seed and LocationsSeeder only add what's missing unless you pass --update.
    • Selects. Country, state, city, atoll and island selects, cascading through javaabu/forms' own select2 cascade from a read-only endpoint (Route::locationOptions()).
      • Blade: <x-locations::island-select parent="atoll_id">
      • React: IslandSelect from @javaabu/locations
      • Stencil: IslandSelect::make()->dependsOn('atoll_id')
    • Admin pages. Route::locations() registers index, show, create, edit and delete for every type, in Blade or Inertia React (locations::Resources/*), and returns JSON on request.
      • Forms are stencil forms.
      • Each type has its own overridable controller.
      • Permissions are view_, edit_ and delete_{countries,states,cities,atolls,islands} through javaabu/permissions, with overridable policies.
      • Pages work with any guard; tests cover two.
    • Optional javaabu/translatable. Translatable model variants are used when it's enabled. Dhivehi names are seeded as translations, admin forms get an input per language, and dhivehi_name is kept in step.
    • Dhivehi and RTL. Strings in en and dv, permission descriptions in lang/dv.json, Thaana inputs, logical CSS only, and Thaana in the tests.
    • Docs. Docusaurus docs, llms.txt, AGENTS.md/CLAUDE.md, and the Laravel Boost guideline and javaabu-locations skill.

    Decisions worth knowing

    • The Dhivehi column is dhivehi_name, not name_dv. name_dv would collide with translatable's {field}_{locale} attributes.
    • Atolls and islands have no country column. Malé City is the atoll MLE. An atoll select that depends on a country lists the atolls only for the Maldives, so forms' fallback can show a city field for foreign addresses.
    • Island names are spelled as the register spells them (Vilin'gili, Kurin'bi). The LGA's spellings are listed for review, not applied.
    • One set of generic admin pages serves all five types, driven by the controllers, instead of a page per type per stack.

    Intentionally out of scope

    • City seed data: there's no authoritative open list. Cities are for apps to add.
    • Dhivehi names for states: none exist in the sources.
    • Map pickers.

    For the user

    • Review the data and Dhivehi:
      • database/data/REVIEW.md: the 53 Dhivehi country names translated here, 5 replaced iso-codes names, atoll Dhivehi names, 7 council spellings that differ from the register, and one register category typo.
      • docs/advanced-usage/dhivehi-and-rtl.md#for-review: the strings.
    • Licensing: iso-codes' data is LGPL-2.1+. It's shipped as data next to the MIT code. Please confirm that's fine before release (docs/advanced-usage/data-sources.md#licences-and-attribution). The island register needs attribution, which is in the README and the docs.
    • Codecov: make sure CODECOV_TOKEN is available to this repo (uploads use fail_ci_if_error: false).
    • Docs registration: Register with Javaabu Docs (docs.javaabu.com) #11.
    • Release: tag and submit to Packagist. Once forms v2, manifest, permissions v3, stencil and translatable v3 are released, drop the VCS repositories. Once @javaabu/forms is on npm, change its npm dependency in LocationsServiceProvider and package.json.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    epicTracks a whole packagenext-genNext-gen Javaabu packages

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions