Skip to content

Extended Project Pages

Daniel Truong edited this page Oct 2, 2026 · 1 revision

Extended Project Pages

An extended project page is a project page whose masthead, summary panel, tab strip and tab content come from a content file in eagle-public instead of only from the project record. It applies to the react line of eagle-public. Pacific Link is the one project that has one. Every other project keeps the standard page.

When a project gets one

A project gets an extended page when the runtime config key EXTENDED_PROJECT_PAGES maps its id to a content key. The key is empty by default, so no project has one. For the key's shape and limits, and for the steps that switch a page off, see Configuration Management.

A content key that has no entry in EXTENDED_CONTENT leaves the project on the standard page.

Where content lives

Content is a TypeScript file in eagle-public, edited by a developer: src/app/pages/project/extended/content/<key>.ts. The file exports one ExtendedPage object (types.ts). The object is plain data: no functions, dates or class instances. Each file is registered under its content key in EXTENDED_CONTENT in extended-page.ts.

The project record still supplies the id, the centroid, the documents and a fallback description. The Act a project falls under comes from the record, see Project Data Models.

Page-level parts

Every part is optional. A part that is absent is not drawn, and the standard piece stays.

Part What it does Falls back to
version Must be 1. Required
displayName Replaces the project name in the masthead, breadcrumb and tab strip Record name
masthead.badge Gold pill above the title No pill
masthead.subLine Lines joined with " · " under the title Proponent and location from the record
masthead.actions External links shown after Subscribe, in order The Short link action
panel.timeline Draws the page's timeline in the progress slot of the summary panel. Needs timeline Assessment rail
panel.facts Label and value rows in the summary panel. statusDot puts a dot before a value Status, EA decision, Type, Location and Proponent from the record
panel.map Thumbnail of map, with an accessible name and a caption. Needs map Location map
timeline Title, note, steps and currentStep. Read by panel.timeline and by the timeline block None
map GeoJSON URL, labelled lines and places. Read by panel.map and by the routeMap block None
updates Updates from other governments, linked to their source. Read by the updates block and by a tab's count: 'updates' The updates tab reads the record's updates
tabs The tab strip. See Tabs The standard tabs
documents.empty Notice shown in place of an empty EPIC documents table The standard empty table
documents.external Documents other governments publish, grouped by publisher and linked where they are hosted. Shown on the Documents tab. An empty groups leaves the section out None
autoLinks Terms linked on their first mention in each piece of running copy. cited links the term elsewhere when the copy also mentions a given string Copy is not linked

currentStep is an index into timeline.steps. A value equal to the number of steps marks every step complete.

A project under an Act whose stages is none has no assessment rail. See Project Data Models.

Tabs

tabs, when present, is the whole tab strip, in the order written. Rules:

  • A standard entry is { segment: 'overview' } and similar. The segments are overview, updates, engagement, documents, decisions and compliance. The tab is drawn as it is.
  • A standard tab that tabs does not list is not in the strip. Its URL redirects to Overview.
  • tabs must have an overview entry.
  • An entry with main blocks on overview and without replace adds its blocks to the standard Overview. banner blocks go full width between the strip and the tab. main blocks follow the standard main column. aside blocks follow the standard side column.
  • An entry with replace: true on any standard segment draws the content tab instead of the standard one. Only overview may append; every other standard segment needs replace to carry blocks.
  • A custom segment is a tab that exists only on this page, such as /p/<id>/act. It needs main blocks.
  • label sets the strip label. Without it the label is title, then the standard tab's label.
  • title and intro head the tab. layout: 'wide' widens the main column.
  • count: 'updates' shows the number of the page's updates in the strip and hides the tab when there are none.

An unknown path under a project goes to that project's Overview. This holds for every project, extended or not, and for any path below a custom segment. The query string and hash are kept.

Block types

Every block has type, an id unique within its tab, and optional heading and framed (draw as a card). RichText is a string or an array of strings and { text, href } links.

type Shows Main fields
summary Description, headline figures and a numbered list description, stats[] (label, value), itemsHeading, items[]
routeMap The page's map, a tile per line, and facts. Needs map optionsHeading, facts[]
table Named rows with a value on the right intro, rows[] (name, note, value), footnote
statCards Large figures in tiles, each with a source cards[] (value, text, source), note
updates The page's updates: a side card of the newest, or the full list shown, tab, intro
definitions Terms and their definitions items[] (term, detail)
projects Other EPIC projects, linked by record id items[] (id, name, note)
links Links that leave EPIC style (cards, list, compact), items[] (label, href, detail)
contacts Labelled contact links items[] (label, link)
list Numbered or bulleted list style (numbered, bulleted), intro, items[]
steps Numbered steps joined by a line noteLabel, steps[] (name, detail, note)
columns Bullet lists side by side columns[] (heading, items[])
prose Running copy text
timeline The page's timeline, one stop per step. Needs timeline None
band Full-width intro with step tiles. Goes in a tab's banner eyebrow, paragraphs[], steps[] (name, short), primary (label, tab), secondary

updates with shown draws the side card, and tab names the tab that "See all" opens. updates without shown draws the full list. band.primary.tab opens another tab of the page.

Validation

validateExtendedPage in validate-extended-page.ts returns one line per problem. The test validate-extended-page.spec.ts runs it on every file in EXTENDED_CONTENT. A page fails when:

  • The page holds anything a JSON round trip would change: undefined, a function, a date, a class instance or a number that is not finite.
  • version is not 1.
  • panel.timeline is set and there is no timeline, or panel.map is set and there is no map.
  • A routeMap block is used and the page has no map.
  • map.geojsonUrl is not a path on this site.
  • A map line id is repeated, or the map has more than two lines.
  • timeline.currentStep is not an integer from 0 to the number of steps.
  • tabs has no overview entry, or lists a segment twice.
  • A custom segment does not match ^[a-z][a-z0-9-]{0,30}$, or is already a project route: application, certificates, amendments, project-details, commenting or cp.
  • A custom tab has no main blocks.
  • A standard tab other than overview carries blocks without replace.
  • count: 'updates' is set and the page has no updates.
  • A block id is repeated within a tab.
  • A band or updates block names a tab that is not in the strip.
  • A panel fact label, an update (link plus date) or a contact label within a tab is repeated.
  • An href anywhere breaks the link rule.

Link rule

Every href in content is one of:

Form Opens
https: URL A new tab
mailto: or tel: The same tab
A path that starts with / and stays on this site The same tab

http:, other schemes, protocol-relative URLs and values with spaces or control characters fail validation.

Give a project an extended page

  1. Create src/app/pages/project/extended/content/<key>.ts. Export an ExtendedPage with version: 1. The key uses lower-case letters, digits and -.
  2. Import it in extended-page.ts and add '<key>': <export> to EXTENDED_CONTENT.
  3. Run the tests. The validation spec covers every registered file.
  4. Add the project id to EXTENDED_PROJECT_PAGES for the environment, as described in Configuration Management.

Example

This page leaves the masthead, panel and timeline standard. It adds one block to the standard Overview, adds one tab, and leaves Decisions and Compliance out of the strip. It follows fixtures/ordinary-page.ts.

export const larchCreek: ExtendedPage = {
  version: 1,
  tabs: [
    {
      segment: 'overview',
      main: [
        {
          type: 'prose',
          id: 'water',
          heading: 'Water monitoring',
          text: 'Three stations sample Larch Creek each month.',
        },
      ],
    },
    { segment: 'updates' },
    {
      segment: 'community',
      label: 'Community fund',
      title: 'How the community fund works',
      main: [
        {
          type: 'list',
          id: 'rules',
          heading: 'Fund rules',
          style: 'numbered',
          items: ['One grant per group each year.'],
        },
      ],
    },
    { segment: 'engagement' },
    { segment: 'documents' },
  ],
};

Known limits

  • Content ships in the public site build. A copy change needs a deploy.
  • The route map supports two line colours, line-1 and line-2.
  • Ordinary project pages load the extended page code with the main bundle. The map library loads only when a route map renders.

Clone this wiki locally