# Eye Specialists Page — Drupal 10 Mapping Sheet

This mapping shows how each component maps to a Drupal 10 implementation target (Block, Paragraph, View, Menu, Template). It follows the pattern the Drupal team established for the homepage, cataract page, and centre-cluster page (`docs/drupal-mapping.md`, `docs/cataract-drupal-mapping.md`, `docs/centre-cluster-drupal-mapping.md`).

## Suggested content architecture

**Content type:** `node--landing_page` with a Paragraph field `field_sections` (bundle-restricted entity reference). The eye-specialists page is one node of this type.

**Taxonomy vocabularies:**
- `centre_location` — term fields: `name`, `slug` (auto/pathauto), `display_order` (optional). Populates the Location dropdown in the search filter.
- `speciality` — term fields: `name`, `slug`, `description`. Populates the Speciality dropdown and the per-card speciality tag pills.

**Content type: `node--doctor`** — see full field spec below. Doctors are rendered on this page via a Views display.

---

## Doctor content type — field spec

This is the most important mapping for this page. Each doctor node feeds one `04-doctor-card.html` row.

| Field name | Type | Required | Notes |
|-----------|------|----------|-------|
| `field_photo` | Image (Media entity) | Yes | Rendered at 246px h (desktop). `alt` = node title if empty. |
| `field_qualifications` | Text (long, sanitized) | No | Displayed above the doctor name, e.g. "MD (AIIMS) \| MBBS". May contain pipe-separated credentials. |
| `field_location` | Entity reference (term, vocabulary: `centre_location`) | Yes | Rendered as the yellow location chip overlaid on the photo. |
| `field_name` | Text (plain) | Yes | Maps to `<h3>` inside the card. Same as `node--doctor` title in most setups; use title field or a separate display title. |
| `field_specialities` | Entity reference list (terms[], vocabulary: `speciality`) | No | Rendered as repeating yellow tag pills inside `<div class="flex flex-wrap gap-1">`. One `<span data-speciality-chip>` per term. |
| `field_years_of_experience` | Integer | No | Displayed in the dark blue footer band as "{N}+ Years Of Experience". |
| `field_booking_url` | Link (external URL allowed) | No | `href` on the "Book An Appointment" anchor at bottom of card body. |

**View mode:** `card` on `node--doctor` → `node--doctor--card.html.twig`. This single Twig file renders every doctor card consistently.

---

## Search filter as Views Exposed Form

The `03-search-filter.html` maps directly to a Views Exposed Form on the "doctors" View.

| Exposed filter | Views filter type | Vocabulary / field | Widget | Static HTML hook |
|---------------|------------------|--------------------|--------|-----------------|
| Location | Taxonomy term (dropdown) | `field_location` → `centre_location` vocab | Select list | `<select data-search-location name="location">` |
| Speciality | Taxonomy term (dropdown) | `field_specialities` → `speciality` vocab | Select list | `<select data-search-speciality name="speciality">` |
| Name | Title, contains | Node `title` | Text input | `<input data-search-name name="name">` |
| Submit | Form actions | — | Button, label "Explore" | `<button type="submit">Explore</button>` |

Override `views-exposed-form.html.twig` to emit the exact HTML structure from `03-search-filter.html`, preserving all `id`, `name`, and `data-*` attributes.

---

## Section-by-section mapping

| # | Component | Drupal implementation target | Notes |
|---|-----------|-----------------------------|-------|
| 1 | `01-utility-bar.html` | `region--secondary.html.twig` (utility region) + menu block | Same as homepage |
| 2 | `02-main-nav.html` | `region--header.html.twig` + `menu.html.twig` + CTA button block | Same as homepage |
| 3 | `01-breadcrumb.html` | Drupal breadcrumb block (`system_breadcrumb_block`) → `breadcrumb.html.twig` override | Path-aware; standard Drupal. Trail: Home › Eye Specialists Near Me |
| 4 | `02-hero-banner.html` | Paragraph bundle `hero_banner_callback` — fields: `field_heading` (text), `field_subtitle` (text), `field_hero_image` (media), `field_phone_placeholder` (text), `field_cta_label` (text) | Same bundle used by cataract and centre-cluster heroes; parameterise via field values |
| 5 | `03-search-filter.html` | `views-exposed-form.html.twig` override on the "doctors" View | Expose `field_location`, `field_specialities`, `title`; button label "Explore" |
| 6 | `04-doctor-card.html` | `node--doctor--card.html.twig` (view mode `card`) | Views row template; see Doctor content type spec above |
| 7 | `05-load-more.html` | `views_load_more` contrib module → renders below the doctor grid | Wire `data-doctor-load-more` in the pager template override |
| 8 | `06-our-story.html` | Paragraph bundle `text_callout` — fields: `field_heading` (text), `field_body` (long text, formatted) | Same bundle as centre-cluster `08-our-story.html`; fully reusable |
| 9 | `05-specialities.html` (shared) | Paragraph bundle `specialities_accordion` + nested paragraph `speciality_item` | Already mapped in homepage mapping doc; no new mapping needed |
| 10 | `07-why-cfs.html` | Paragraph bundle `feature_card_grid` containing `field_cards` (entity reference → paragraph bundle `feature_card` with `field_heading`) | Variable card count; same structure as centre-cluster `10-why-cfs.html` |
| 11 | `08-faq.html` | Paragraph bundle `faq_section` with nested paragraph bundle `faq_item` (`field_question`, `field_answer`) | Same pattern as `centre-cluster-components/11-faq.html`; content team must replace placeholder questions |
| 12 | `09-callback-form.html` | Paragraph bundle `callback_cta` — fields: `field_eyebrow` (text), `field_heading` (text), `field_background_image` (media). Webform reference `request_a_callback` for form rendering | "Could not find" eyebrow variant; shared Webform; same paragraph bundle as centre-cluster and cataract |
| 13 | `15-footer.html` (shared) | `region--footer.html.twig` split into 3–4 blocks | Same as homepage |
| 14 | `16-sticky-bar.html` (shared) | Custom block inside `region--bottom.html.twig` | Same as homepage |

---

## Breadcrumb spec

Twig trail for this page:
```twig
{# breadcrumb.html.twig #}
<nav class="breadcrumb ..." aria-label="Breadcrumb">
  <ol ...>
    <li><a href="{{ path('<front>') }}">Home</a></li>
    <li aria-hidden="true" ...>›</li>
    <li aria-current="page" ...>Eye Specialists Near Me</li>
  </ol>
</nav>
```

No dynamic ancestors required — this is a flat second-level page.

---

## Hero banner paragraph fields

| HTML element | Paragraph field | Type | Notes |
|-------------|----------------|------|-------|
| `<h1>` text | `field_heading` | Text (plain) | Default: "Eye Specialists Near You" |
| Subtitle `<p>` | `field_subtitle` | Text (plain) | Default: "Discover top-rated eye specialists close to you." |
| `<img>` src | `field_hero_image` | Media (image) | Placeholder: `assets/images/specialists-group.png` |
| Phone placeholder | `field_phone_placeholder` | Text (plain) | Default: "Contact Number" |
| CTA button label | `field_cta_label` | Text (plain) | Default: "Request a Callback" |
| `+91` prefix | — | Decorative HTML | No field needed; always rendered |

Hero form posts to the shared `request_a_callback` Webform (or custom route). `+91` prefix is decorative — Webform handles full phone validation.

---

## Our Story paragraph fields

| HTML element | Paragraph field | Type | Notes |
|-------------|----------------|------|-------|
| `<h2>` text | `field_heading` | Text (plain) | Default: "Our Story and Vision" |
| Body `<p>` | `field_body` | Text (long, formatted) | Default: CFS founding story paragraph |

Paragraph bundle: `text_callout` (shared with centre-cluster).

---

## Why CFS fields

**Paragraph bundle: `feature_card_grid`**

| Field | Type | Notes |
|-------|------|-------|
| `field_cards` | Entity reference (paragraph[], bundle: `feature_card`) | 4 cards for this page |

**Nested paragraph bundle: `feature_card`**

| Field | Type | Notes |
|-------|------|-------|
| `field_heading` | Text (plain) | Card title, e.g. "Eye Care Specialists" |

Card decorative line is rendered in the Twig template, not a field.

---

## FAQ fields

**Paragraph bundle: `faq_section`**

| Field | Type | Notes |
|-------|------|-------|
| `field_items` | Entity reference (paragraph[], bundle: `faq_item`) | 6 items for this page |

**Nested paragraph bundle: `faq_item`**

| Field | Type | Notes |
|-------|------|-------|
| `field_question` | Text (plain) | Accordion button text |
| `field_answer` | Text (long, formatted) | Hidden answer panel |

---

## Callback form fields

**Paragraph bundle: `callback_cta`**

| Field | Type | Notes |
|-------|------|-------|
| `field_eyebrow` | Text (plain) | Default: "Could not find what you are looking for?" |
| `field_heading` | Text (plain) | Default: "Request a Callback" |
| `field_background_image` | Media (image) | Default: `assets/images/callback-bg.png` |
| `field_webform` | Webform reference | Shared `request_a_callback` Webform |

Webform fields: `full_name` (text, required) + `phone` (telephone, required, pattern `[0-9]{10}`). Submit handlers: email notification + CRM hook.

---

## Layout risks / notes for Drupal team

1. **Doctor Views grouping is optional.** Unlike the centre-cluster page (grouped by state), the eye-specialists page shows a flat grid of all doctors, filtered via exposed form. No grouping field needed — just a flat Views list with pager.

2. **`node--doctor--card.html.twig` must emit all data-* attributes.** The card template must include `data-doctor-card` on the `<article>`, `data-speciality-chip` on each tag span, and `data-faq-toggle` equivalent attributes if FAQ is ever card-level. Without these hooks, JS interactions break silently.

3. **Speciality tag count is variable.** The card uses `flex flex-wrap` — zero, one, or many speciality terms all render cleanly. Do not cap the tag count in Views/Twig.

4. **Accordion JS is shared.** When the Drupal team ports FAQ to Twig, do not add per-page JS. The existing `data-faq-toggle` handler in `js/main.js` already covers this page — no Drupal-side JS work needed.

5. **Hero `+91` prefix has no `aria-hidden`.** This matches the existing cataract and centre-cluster pattern. Consider a project-wide a11y improvement: add `aria-hidden="true"` to all `+91` prefix divs so screen readers skip the decorative country code.

6. **Search form `id` and `name` attributes must match Views exposed form output.** The `id="search-location"`, `id="search-speciality"`, `id="search-name"` attributes in the static HTML must be preserved in the Twig override for label associations to work correctly.

---

## Required Drupal modules (suggested)

- `paragraphs` (contrib)
- `views_load_more` (contrib)
- `webform` (for callback form)
- `media` + `media_library`
- `pathauto` (for taxonomy term URL aliases)
- `taxonomy` (core — for `centre_location` and `speciality` vocabularies)

## Testing checklist for Drupal theme integration

- [ ] Views "doctors" page display renders doctor cards matching `04-doctor-card.html` layout
- [ ] Exposed form — location dropdown options match `centre_location` taxonomy terms
- [ ] Exposed form — speciality dropdown options match `speciality` taxonomy terms
- [ ] Filtering by location narrows the card grid; empty result shows graceful message
- [ ] Each doctor card's speciality chips reflect the node's `field_specialities` terms
- [ ] "Load More" reveals next batch of doctors (Views `views_load_more` pager)
- [ ] FAQ accordion `data-faq-toggle` click expands/collapses each answer independently
- [ ] Callback form submits correctly and sends email notification
- [ ] Mobile hamburger (`data-menu-toggle`) opens full-screen nav overlay
- [ ] Sticky bottom bar visible on mobile, chat bubble visible on desktop (not both)
- [ ] Breadcrumb shows "Home › Eye Specialists Near Me"
- [ ] Hero form `+91` phone input validates 10-digit numbers only
