diff --git a/docker/php/entrypoint.sh b/docker/php/entrypoint.sh index 548cd77..43a286a 100644 --- a/docker/php/entrypoint.sh +++ b/docker/php/entrypoint.sh @@ -15,7 +15,7 @@ cd /var/www/html DRUSH="vendor/bin/drush --root=/var/www/html/web" HAS_TABLES=$($DRUSH sql:query \ - "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='public' AND table_name='config';" \ + "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='public' AND table_name='users';" \ 2>/dev/null || echo "0") IS_SETUP=$($DRUSH sql:query \ @@ -52,13 +52,13 @@ if [ "$IS_SETUP" != "1" ]; then if ls /var/www/html/config/sync/*.yml >/dev/null 2>&1; then echo "[entrypoint] Importing configuration from sync dir..." - $DRUSH config:import -y || echo "[entrypoint] WARNING: config import failed." + $DRUSH config:import --partial -y || echo "[entrypoint] WARNING: config import failed." fi echo "[entrypoint] Setup complete." else echo "[entrypoint] Setup already complete, importing configuration..." - $DRUSH config:import -y 2>/dev/null && \ + $DRUSH config:import -y >/dev/null 2>&1 && \ echo "[entrypoint] Config imported." || \ echo "[entrypoint] No config to import, continuing." fi diff --git a/web/modules/custom/riverside_pt/css/nav.css b/web/modules/custom/riverside_pt/css/nav.css index 851cfde..affb92c 100644 --- a/web/modules/custom/riverside_pt/css/nav.css +++ b/web/modules/custom/riverside_pt/css/nav.css @@ -1,26 +1,111 @@ +/* ── Hide Olivero's sticky-header toggle ─────────────────── */ +.sticky-header-toggle { + display: none; +} + +/* ── Header: let the nav take all remaining width ────────── */ +.site-header__inner__container { + gap: 2rem; +} + +.header-nav { + flex: 1; + min-width: 0; +} + +/* ── Primary nav: flat horizontal list ──────────────────── */ +.primary-nav__menu--level-1 { + display: flex !important; + align-items: center; + width: 100%; + margin: 0 !important; + padding: 0 !important; + border: none !important; + background: none !important; +} + +.primary-nav__menu-item--level-1 { + border: none !important; + background: none !important; + padding: 0 !important; + margin: 0 !important; +} + +/* Plain text nav links */ +.primary-nav__menu-link--level-1 { + font-size: 0.9375rem !important; + font-weight: 400 !important; + color: #374151 !important; + padding: 0.25rem 0.875rem !important; + border: none !important; + background: none !important; + text-decoration: none !important; + border-radius: 0 !important; +} + +.primary-nav__menu-link--level-1:hover, +.primary-nav__menu-link--level-1:focus { + color: #1e3a8a !important; + background: none !important; + border: none !important; + text-decoration: none !important; +} + +/* Active trail */ +.primary-nav__menu-link--level-1.primary-nav__menu-link--active-trail { + color: #1e3a8a !important; + font-weight: 500 !important; +} + +/* ── Push CTA group to the far right ────────────────────── */ +.primary-nav__menu-item--level-1:has(> .nav-cta) { + margin-left: auto !important; +} + +.primary-nav__menu-item--level-1:has(> .nav-cta) +~ .primary-nav__menu-item--level-1:has(> .nav-cta) { + margin-left: 0.5rem !important; +} + +/* ── CTA buttons — rectangular ──────────────────────────── */ .primary-nav__menu-link.nav-cta { - border: 2px solid #3b82f6; - border-radius: 999px; - color: #3b82f6; - padding: 0.25rem 1rem; - transition: background 0.15s, color 0.15s; + display: inline-block !important; + padding: 0.5rem 1.25rem !important; + border-radius: 4px !important; + font-size: 0.9rem !important; + font-weight: 500 !important; + text-decoration: none !important; + transition: background 0.15s, color 0.15s, border-color 0.15s; } -.primary-nav__menu-link.nav-cta:hover, -.primary-nav__menu-link.nav-cta:focus { - background: #3b82f6; - border-color: #3b82f6; - color: #fff; +/* Contact: outlined */ +.primary-nav__menu-link.nav-cta:not(.nav-cta--primary) { + background: transparent !important; + color: #1e3a5f !important; + border: 1.5px solid #1e3a5f !important; } +.primary-nav__menu-link.nav-cta:not(.nav-cta--primary):hover, +.primary-nav__menu-link.nav-cta:not(.nav-cta--primary):focus { + background: #1e3a5f !important; + color: #fff !important; +} + +/* Book An Appointment: filled */ .primary-nav__menu-link.nav-cta--primary { - background: #3b82f6; - border-color: #3b82f6; - color: #fff; + background: #1e3a5f !important; + color: #fff !important; + border: 1.5px solid #1e3a5f !important; } .primary-nav__menu-link.nav-cta--primary:hover, .primary-nav__menu-link.nav-cta--primary:focus { - background: #1d4ed8; - border-color: #1d4ed8; + background: #152a45 !important; + border-color: #152a45 !important; + color: #fff !important; +} + +/* ── Inner span inside each link — remove Olivero padding ── */ +.primary-nav__menu-link-inner--level-1 { + padding: 0 !important; } diff --git a/web/modules/custom/riverside_pt/riverside_pt.install b/web/modules/custom/riverside_pt/riverside_pt.install index 5cc47c6..26c4914 100644 --- a/web/modules/custom/riverside_pt/riverside_pt.install +++ b/web/modules/custom/riverside_pt/riverside_pt.install @@ -9,165 +9,166 @@ use Drupal\field\Entity\FieldStorageConfig; use Drupal\field\Entity\FieldConfig; function riverside_pt_install() { - array_walk([ - // Appointment - NodeType::create([ - 'type' => 'appointment', - 'name' => 'Appointment', - 'description' => 'A booking between a Patient and a Provider at a particular time.', - 'new_revision' => FALSE, - 'display_submitted' => FALSE, - ]), - FieldStorageConfig::create([ - 'field_name' => 'field_appointment_date', - 'entity_type' => 'node', - 'type' => 'datetime', - 'settings' => ['datetime_type' => 'datetime'], - ]), - FieldStorageConfig::create([ - 'field_name' => 'field_duration_minutes', - 'entity_type' => 'node', - 'type' => 'integer', - ]), - FieldStorageConfig::create([ - 'field_name' => 'field_service_type', - 'entity_type' => 'node', - 'type' => 'list_string', - 'settings' => [ - 'allowed_values' => [ - 'diagnostic' => 'Diagnostic', - 'sports_rehab' => 'Sports Rehab', - 'pre_post_surgical_rehab' => 'Pre/Post-Surgical Rehab', - 'neurological_pt' => 'Neurological PT', - ], - ], - ]), - FieldStorageConfig::create([ - 'field_name' => 'field_provider', - 'entity_type' => 'node', - 'type' => 'entity_reference', - 'settings' => ['target_type' => 'user'], - ]), - FieldConfig::create([ - 'field_name' => 'field_appointment_date', - 'entity_type' => 'node', - 'bundle' => 'appointment', - 'label' => 'Appointment Date', - 'required' => TRUE, - ]), - FieldConfig::create([ - 'field_name' => 'field_duration_minutes', - 'entity_type' => 'node', - 'bundle' => 'appointment', - 'label' => 'Duration (Minutes)', - 'required' => TRUE, - ]), - FieldConfig::create([ - 'field_name' => 'field_service_type', - 'entity_type' => 'node', - 'bundle' => 'appointment', - 'label' => 'Service Type', - 'required' => TRUE, - ]), - FieldConfig::create([ - 'field_name' => 'field_provider', - 'entity_type' => 'node', - 'bundle' => 'appointment', - 'label' => 'Provider', - 'required' => TRUE, - 'settings' => [ - 'handler' => 'default:user', - 'handler_settings' => [ - 'filter' => [ - 'type' => '_role', - 'role' => ['provider' => 'provider'], - ], - ], - ], - ]), + // Pass 1: types, roles, and field storages (no inter-dependencies). + NodeType::create([ + 'type' => 'appointment', + 'name' => 'Appointment', + 'description' => 'A booking between a Patient and a Provider at a particular time.', + 'new_revision' => FALSE, + 'display_submitted' => FALSE, + ])->save(); - // Provider - Role::create([ - 'id' => 'provider', - 'label' => 'Provider', - ]), + NodeType::create([ + 'type' => 'provider_availability', + 'name' => 'Provider Availability', + 'description' => 'A window of time during which a Provider is available for appointments.', + 'new_revision' => FALSE, + 'display_submitted' => FALSE, + ])->save(); - // Provider Availability - NodeType::create([ - 'type' => 'provider_availability', - 'name' => 'Provider Availability', - 'description' => 'A window of time during which a Provider is available for appointments.', - 'new_revision' => FALSE, - 'display_submitted' => FALSE, - ]), - FieldStorageConfig::create([ - 'field_name' => 'field_start_datetime', - 'entity_type' => 'node', - 'type' => 'datetime', - 'settings' => ['datetime_type' => 'datetime'], - ]), - FieldStorageConfig::create([ - 'field_name' => 'field_end_datetime', - 'entity_type' => 'node', - 'type' => 'datetime', - 'settings' => ['datetime_type' => 'datetime'], - ]), - FieldConfig::create([ - 'field_name' => 'field_provider', - 'entity_type' => 'node', - 'bundle' => 'provider_availability', - 'label' => 'Provider', - 'required' => TRUE, - 'settings' => [ - 'handler' => 'default:user', - 'handler_settings' => [ - 'filter' => [ - 'type' => '_role', - 'role' => ['provider' => 'provider'], - ], - ], - ], - ]), - FieldConfig::create([ - 'field_name' => 'field_start_datetime', - 'entity_type' => 'node', - 'bundle' => 'provider_availability', - 'label' => 'Start', - 'required' => TRUE, - ]), - FieldConfig::create([ - 'field_name' => 'field_end_datetime', - 'entity_type' => 'node', - 'bundle' => 'provider_availability', - 'label' => 'End', - 'required' => TRUE, - ]), - ], fn($entity) => $entity->save()); + Role::create(['id' => 'provider', 'label' => 'Provider'])->save(); - try { - _riverside_pt_build_navigation(); - } - catch (\Exception $e) { - \Drupal::logger('riverside_pt')->error('Navigation setup failed: @msg', ['@msg' => $e->getMessage()]); - } + FieldStorageConfig::create([ + 'field_name' => 'field_appointment_date', + 'entity_type' => 'node', + 'type' => 'datetime', + 'settings' => ['datetime_type' => 'datetime'], + ])->save(); - \Drupal::configFactory()->getEditable('system.site') - ->set('page.front', '/home') - ->save(); + FieldStorageConfig::create([ + 'field_name' => 'field_duration_minutes', + 'entity_type' => 'node', + 'type' => 'integer', + ])->save(); + + FieldStorageConfig::create([ + 'field_name' => 'field_service_type', + 'entity_type' => 'node', + 'type' => 'list_string', + 'settings' => [ + 'allowed_values' => [ + 'diagnostic' => 'Diagnostic', + 'sports_rehab' => 'Sports Rehab', + 'pre_post_surgical_rehab' => 'Pre/Post-Surgical Rehab', + 'neurological_pt' => 'Neurological PT', + ], + ], + ])->save(); + + FieldStorageConfig::create([ + 'field_name' => 'field_provider', + 'entity_type' => 'node', + 'type' => 'entity_reference', + 'settings' => ['target_type' => 'user'], + ])->save(); + + FieldStorageConfig::create([ + 'field_name' => 'field_start_datetime', + 'entity_type' => 'node', + 'type' => 'datetime', + 'settings' => ['datetime_type' => 'datetime'], + ])->save(); + + FieldStorageConfig::create([ + 'field_name' => 'field_end_datetime', + 'entity_type' => 'node', + 'type' => 'datetime', + 'settings' => ['datetime_type' => 'datetime'], + ])->save(); + + // Clear field definition cache so FieldConfig::preSave() can find the storages. + \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions(); + + // Pass 2: field configs (depend on storages being in the DB). + FieldConfig::create([ + 'field_name' => 'field_appointment_date', + 'entity_type' => 'node', + 'bundle' => 'appointment', + 'label' => 'Appointment Date', + 'required' => TRUE, + ])->save(); + + FieldConfig::create([ + 'field_name' => 'field_duration_minutes', + 'entity_type' => 'node', + 'bundle' => 'appointment', + 'label' => 'Duration (Minutes)', + 'required' => TRUE, + ])->save(); + + FieldConfig::create([ + 'field_name' => 'field_service_type', + 'entity_type' => 'node', + 'bundle' => 'appointment', + 'label' => 'Service Type', + 'required' => TRUE, + ])->save(); + + FieldConfig::create([ + 'field_name' => 'field_provider', + 'entity_type' => 'node', + 'bundle' => 'appointment', + 'label' => 'Provider', + 'required' => TRUE, + 'settings' => [ + 'handler' => 'default:user', + 'handler_settings' => [ + 'filter' => ['type' => '_role', 'role' => ['provider' => 'provider']], + ], + ], + ])->save(); + + FieldConfig::create([ + 'field_name' => 'field_provider', + 'entity_type' => 'node', + 'bundle' => 'provider_availability', + 'label' => 'Provider', + 'required' => TRUE, + 'settings' => [ + 'handler' => 'default:user', + 'handler_settings' => [ + 'filter' => ['type' => '_role', 'role' => ['provider' => 'provider']], + ], + ], + ])->save(); + + FieldConfig::create([ + 'field_name' => 'field_start_datetime', + 'entity_type' => 'node', + 'bundle' => 'provider_availability', + 'label' => 'Start', + 'required' => TRUE, + ])->save(); + + FieldConfig::create([ + 'field_name' => 'field_end_datetime', + 'entity_type' => 'node', + 'bundle' => 'provider_availability', + 'label' => 'End', + 'required' => TRUE, + ])->save(); + + try { + _riverside_pt_build_navigation(); + } + catch (\Exception $e) { + \Drupal::logger('riverside_pt')->error('Navigation setup failed: @msg', ['@msg' => $e->getMessage()]); + } + + \Drupal::configFactory()->getEditable('system.site') + ->set('page.front', '/home') + ->save(); } function _riverside_pt_build_navigation(): void { $em = \Drupal::entityTypeManager(); - // Remove whatever links Standard profile put in the main menu. foreach ($em->getStorage('menu_link_content')->loadByProperties(['menu_name' => 'main']) as $link) { $link->delete(); } - // Create placeholder basic pages. foreach (['Services', 'About', 'FAQ', 'Contact'] as $title) { - $existing = $em->getStorage('node')->loadByProperties(['title' => $title, 'type' => 'page']); - if ($existing) { + if ($em->getStorage('node')->loadByProperties(['title' => $title, 'type' => 'page'])) { continue; } $node = Node::create(['type' => 'page', 'title' => $title, 'status' => 1]); @@ -179,7 +180,6 @@ function _riverside_pt_build_navigation(): void { ])->save(); } - // Build the primary navigation. $defs = [ ['title' => 'Home', 'uri' => 'route:', 'weight' => 0, 'class' => NULL], ['title' => 'Services', 'uri' => 'internal:/services', 'weight' => 1, 'class' => NULL],