> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/bloom-housing/bloom/llms.txt
> Use this file to discover all available pages before exploring further.

# Partners Portal

> The administrative Next.js dashboard for housing developers, property managers, and city/county staff to manage listings, applications, lotteries, and users.

The Partners Portal is the reference implementation of the Bloom Housing administrative interface. It runs as a Next.js application on port 3001 and provides housing partners — including housing developers, property managers, cities, and counties — with tools to manage listings, review and export applications, run lotteries, and administer users.

## Features

<CardGroup cols={2}>
  <Card title="Listing management" icon="building">
    Create, edit, publish, close, and reopen affordable housing listings. Supports draft and published states.
  </Card>

  <Card title="Application management" icon="folder-open">
    View, edit, add, and export applications per listing. Separate views for pending and resolved applications.
  </Card>

  <Card title="Lottery management" icon="shuffle">
    Run and publish lotteries for closed listings. Lottery data expires after a configurable number of days.
  </Card>

  <Card title="User management" icon="users">
    Invite, confirm, and manage partner users and advocates. Restricted to admin roles.
  </Card>

  <Card title="Admin functions" icon="shield-halved">
    System-level administrative operations accessible only to admin users.
  </Card>

  <Card title="Settings" icon="gear">
    Manage jurisdiction preferences, multiselect questions, and property records. Shown when the feature flag is enabled.
  </Card>
</CardGroup>

## User roles

Access to features within the Partners Portal is controlled by role. The following roles are supported:

| Role                  | Description                                                               |
| --------------------- | ------------------------------------------------------------------------- |
| `admin`               | Full access to all features including user management and admin functions |
| `jurisdictionalAdmin` | Administrative access scoped to a specific jurisdiction                   |
| `partner`             | Access to listings and applications for their associated properties       |
| `leasingAgent`        | Access to view and manage applications for assigned listings              |
| `supportAdmin`        | Support staff with elevated read access for troubleshooting               |

<Warning>
  User management pages (`/users`) and admin functions (`/admin`) are restricted to users with the `admin` or `jurisdictionalAdmin` role. Accessing these pages without the appropriate role will redirect to `/unauthorized`.
</Warning>

## Page structure

All pages live under `sites/partners/src/pages/`.

<AccordionGroup>
  <Accordion title="Authentication pages">
    | File                  | Route              | Description                                   |
    | --------------------- | ------------------ | --------------------------------------------- |
    | `index.tsx`           | `/`                | Root redirect to listings dashboard           |
    | `sign-in.tsx`         | `/sign-in`         | Partner sign-in page                          |
    | `forgot-password.tsx` | `/forgot-password` | Password reset request                        |
    | `reset-password.tsx`  | `/reset-password`  | Password reset confirmation                   |
    | `unauthorized.tsx`    | `/unauthorized`    | Shown when a user lacks permission for a page |
  </Accordion>

  <Accordion title="Listing pages (/listings)">
    | File                        | Route                    | Description                      |
    | --------------------------- | ------------------------ | -------------------------------- |
    | `listings/add.tsx`          | `/listings/add`          | Create a new listing             |
    | `listings/[id]/index.tsx`   | `/listings/[id]`         | Listing detail and edit overview |
    | `listings/[id]/edit.tsx`    | `/listings/[id]/edit`    | Full listing edit form           |
    | `listings/[id]/lottery.tsx` | `/listings/[id]/lottery` | Lottery management for a listing |
  </Accordion>

  <Accordion title="Application pages (/listings/[id]/applications and /application)">
    | File                                   | Route                                  | Description                            |
    | -------------------------------------- | -------------------------------------- | -------------------------------------- |
    | `listings/[id]/applications/index.tsx` | `/listings/[id]/applications`          | All applications for a listing         |
    | `listings/[id]/applications/add.tsx`   | `/listings/[id]/applications/add`      | Add a paper/manual application         |
    | `listings/[id]/applications/pending/`  | `/listings/[id]/applications/pending`  | Applications pending review            |
    | `listings/[id]/applications/resolved/` | `/listings/[id]/applications/resolved` | Resolved/processed applications        |
    | `application/[id]/`                    | `/application/[id]`                    | View or edit an individual application |
  </Accordion>

  <Accordion title="User management pages (/users)">
    | File                  | Route              | Description                       |
    | --------------------- | ------------------ | --------------------------------- |
    | `users/index.tsx`     | `/users`           | List and search all partner users |
    | `users/advocates.tsx` | `/users/advocates` | Manage advocate accounts          |
    | `users/confirm.tsx`   | `/users/confirm`   | Confirm a newly invited user      |
    | `users/terms.tsx`     | `/users/terms`     | Terms of service acceptance       |
  </Accordion>

  <Accordion title="Admin pages (/admin)">
    | File              | Route    | Description                     |
    | ----------------- | -------- | ------------------------------- |
    | `admin/index.tsx` | `/admin` | System administration dashboard |
  </Accordion>

  <Accordion title="Settings pages (/settings)">
    Settings pages are only visible when the settings feature flag is enabled.

    | File                                            | Route                                        | Description                      |
    | ----------------------------------------------- | -------------------------------------------- | -------------------------------- |
    | `settings/index.tsx`                            | `/settings`                                  | Jurisdiction settings overview   |
    | `settings/preferences.tsx`                      | `/settings/preferences`                      | Manage listing preferences       |
    | `settings/properties.tsx`                       | `/settings/properties`                       | Manage property records          |
    | `settings/multiselectquestions/preferences.tsx` | `/settings/multiselectquestions/preferences` | Manage multiselect question sets |
  </Accordion>
</AccordionGroup>

## Running locally

<Steps>
  <Step title="Install dependencies">
    From the monorepo root:

    ```bash theme={null}
    yarn install
    ```
  </Step>

  <Step title="Configure environment variables">
    Copy the template and set values for your local environment:

    ```bash theme={null}
    cp sites/partners/.env.template sites/partners/.env
    ```

    Edit `sites/partners/.env` as needed. See the [environment variables](#environment-variables) section below for details on each variable.
  </Step>

  <Step title="Start the backend and partners app">
    From the monorepo root, this starts the backend on port 3100 and the partners portal on port 3001:

    ```bash theme={null}
    yarn dev:all
    ```

    Alternatively, start only the partners portal from within `sites/partners`:

    ```bash theme={null}
    yarn dev
    ```
  </Step>

  <Step title="Sign in">
    Navigate to [http://localhost:3001](http://localhost:3001) and sign in with the default local development credentials:

    * **Email:** `admin@example.com`
    * **Password:** `abcdef`
  </Step>
</Steps>

<Note>
  The backend must be running on port 3100 before starting the partners portal. Sign-in and all data operations require an active API connection.
</Note>

## Environment variables

Copy `.env.template` to `.env` inside `sites/partners/`. The following variables control the app's behavior:

<AccordionGroup>
  <Accordion title="API and backend">
    | Variable             | Default                 | Description                                               |
    | -------------------- | ----------------------- | --------------------------------------------------------- |
    | `BACKEND_API_BASE`   | `http://localhost:3100` | Base URL for the backend API                              |
    | `BACKEND_PROXY_BASE` | *(empty)*               | If set, routes API calls through this proxy               |
    | `LISTINGS_QUERY`     | `/listings`             | Path appended to backend base to query listings           |
    | `API_PASS_KEY`       | `some-key-here`         | Shared passkey; must match the backend's configured value |
  </Accordion>

  <Accordion title="Application behavior">
    | Variable                   | Default | Description                                                      |
    | -------------------------- | ------- | ---------------------------------------------------------------- |
    | `NEXTJS_PORT`              | `3001`  | Port the Next.js app listens on                                  |
    | `LOTTERY_DAYS_TILL_EXPIRY` | `45`    | Number of days after which lottery data is considered expired    |
    | `ALLOW_SEO_INDEXING`       | `FALSE` | Set to `TRUE` in production only; enables search engine indexing |
  </Accordion>

  <Accordion title="Third-party integrations">
    | Variable                    | Default   | Description                                              |
    | --------------------------- | --------- | -------------------------------------------------------- |
    | `MAPBOX_TOKEN`              | *(empty)* | Mapbox API token for property map views                  |
    | `CLOUDINARY_CLOUD_NAME`     | `exygy`   | Cloudinary cloud name for image uploads                  |
    | `CLOUDINARY_KEY`            | `abcxyz`  | Cloudinary API key for signed uploads                    |
    | `CLOUDINARY_SIGNED_PRESET`  | `test123` | Cloudinary signed upload preset                          |
    | `RECAPTCHA_KEY`             | *(empty)* | Google reCAPTCHA site key                                |
    | `AXE_DEVELOPER_HUB_API_KEY` | *(empty)* | Axe DevHub API key for automated accessibility reporting |
  </Accordion>

  <Accordion title="Feature flags">
    | Variable                            | Default   | Description                                                         |
    | ----------------------------------- | --------- | ------------------------------------------------------------------- |
    | `SHOW_LOTTERY`                      | `FALSE`   | Enables lottery management UI within listing pages                  |
    | `SHOW_SMS_MFA`                      | `TRUE`    | Enables SMS-based multi-factor authentication (on by default)       |
    | `APPLICATION_EXPORT_AS_SPREADSHEET` | `FALSE`   | Exports applications as a spreadsheet (`.xlsx`) instead of CSV      |
    | `USE_SECURE_DOWNLOAD_PATHWAY`       | `TRUE`    | Downloads exported files through a secure S3 private bucket pathway |
    | `LIMIT_CLOSED_LISTING_ACTIONS`      | `FALSE`   | Restricts edit, republish, and reopen actions on closed listings    |
    | `RUN_ACCESSIBILITY_E2E_TESTS`       | *(empty)* | Enables accessibility checks in the Cypress E2E suite               |
  </Accordion>
</AccordionGroup>

## Testing

The partners portal has two test suites.

<Tabs>
  <Tab title="Cypress (E2E)">
    Run the full end-to-end test suite against a running instance of the app:

    ```bash theme={null}
    yarn test
    ```

    Run from within `sites/partners/` with the app already running on port 3001 and the backend running on port 3100.

    <Info>
      Set `RUN_ACCESSIBILITY_E2E_TESTS=TRUE` in `.env` to also run axe-based accessibility checks during the Cypress suite.
    </Info>
  </Tab>

  <Tab title="Jest (unit/integration)">
    Run unit and integration tests:

    ```bash theme={null}
    yarn test:unit
    ```

    Run with coverage reports:

    ```bash theme={null}
    yarn test:unit:coverage
    ```

    Both commands run from within `sites/partners/`.
  </Tab>
</Tabs>
