Files
studiobeve.no/application/views/pages/includes/massage-form-en.php
T
UbuntuandClaude Opus 5 5c30467fbd Add massage as a third vertical, driven by a config registry
Introduces /massage alongside barber and beauty: landing page, booking
flow, admin support, home tile and SEO entries, in all three languages.

Architecture
- application/config/verticals.php + vertical_helper.php: one registry
  entry per vertical (branding, assets, views, behaviour flags). A fourth
  vertical is a config entry plus content files.
- Strangler: barber and beauty keep pointing at their existing view files,
  so their rendered HTML is unchanged. Only massage uses the new generic
  pages/vertical-*.php and includes/vertical-*.php views, which collapse
  the four duplicated per-language nav/footer branches into one.
- Pages::vertical() + one route; booking(), booking_finished(),
  _booking_error() and manage_booking() are now registry-driven.

Worker/vertical coupling
- getActiveWorkers() derives the vertical from services.service_category_id
  instead of the workers.is_barber / is_beauty flags, which were a
  hand-maintained cache of exactly that fact. Verified against production
  data: the derived set reproduced the stored flags for every worker, in
  both verticals. No schema change was needed for massage.
- The legacy flags are now written through from the category so a rollback
  cannot strand a new worker, and the admin worker UI shows the derived
  verticals read-only instead of two dropdowns that controlled nothing.

Bug fixes found along the way (all pre-existing)
- booking_process() had no server-side category guard; cross-vertical
  mixing was prevented only by client-side JS.
- add-service-form / add-worker-form emitted `selected` on every category
  option, so the newest category silently became the default.
- update-service-form offered only barber/beauty, so editing a service of
  any other type silently rewrote it.
- getWorkerScheduleByDay ignored schedule overrides while getAvailableTimes
  honoured them, so slots could be shown and then rejected. Added an
  override-aware getWorkerScheduleForDate() and used it in both guards.
- Booking lists dereferenced a null service if one had been hard-deleted.
- main.css: .tiles was tuned for exactly two tiles, including an
  absolutely-positioned .style1 at the 1280px breakpoint.

Massage-specific behaviour, opt-in per vertical
- strip_category_prefix: grouped service lists show "50 min" under the
  treatment heading rather than repeating the full name. The full name is
  carried in data-service-name so the totals panel stays unambiguous, and
  services.service_name is untouched for emails and admin.
- single_service_booking: one treatment per booking, enforced in the UI and
  in booking_process(). Re-clicking the selection releases it.
- Displayed treatment time (50/80/110 min) is in the service name; the
  booked slot (60/90/120 min) is service_time and covers changing and
  payment. service_time is never shown to the guest.

DB migrations for dev/prod are in documents/ - additive only, no ALTER.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 16:34:47 +00:00

294 lines
16 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/* -----------------------------------------------------------------------
* Massage vertical landing content - English.
*
* Bio, treatment names and descriptions supplied by the salon.
* Generated from one template so massage-form-no / -en / -hu stay
* structurally identical.
*
* DURATIONS: the price list shows the treatment time from the service NAME
* (50 / 80 / 110 min). services.service_time holds the longer BOOKED slot
* (60 / 90 / 120 min) which covers changing and payment, and is never
* rendered to the guest. See prices_note below.
*
* Still a placeholder: assets/img/massage/masseur.png (therapist portrait),
* assets/img/massage/banner.jpg and map.jpg.
* -------------------------------------------------------------------- */
?>
<!-- Banner -->
<section id="banner">
<img src="<?php echo base_url();?>assets/img/massage/banner.jpg" style="width: 100%;" />
</section>
<section id="mobil-menu">
<div class="mobil-menucont">
<ul>
<li class="mobil-menu-book"><a href="<?php echo SITEURL;?>en/booking/massage">BOOK TIME</a></li>
<li class=""><a href="<?php echo $vertical['facebook'];?>" class="icon brands fa-facebook-f"><span class="label">Facebook</span></a></li>
<li class=""><a href="<?php echo $vertical['instagram'];?>" class="icon brands fa-instagram"><span class="label">Instagram</span></a></li>
</ul>
</div>
</section>
<!-- About Us -->
<section id="aboutus" class="wrapper style1">
<div class="container">
<div class="inner">
<header class="major">
<img src="<?php echo base_url();?>assets/img/massage/ollo_rose.png">
<h2>About us</h2>
</header>
<?php /* Owner block, lifted verbatim from beauty-form-<lang>.php at
generation time so all three verticals introduce Evelin
identically. Do not edit here - edit the beauty file and
regenerate, or the two will drift. */ ?>
<div class="bemutatkozas">
<div class="bem-bal">
<p>
My name is Evelin Bence, leader and creator of Studio Beve. I consider it
important that our guests during their stay – resting a little from the noise of the
world – recharge their batteries and return to everyday life beautified inside as
well as outside.
</p>
<p>
Quality time, maximum attention, the most professional tools, together with
professionalism and expertise, stand for the basis of our philosophy and
services.
</p>
<p>
I believe that beauty, fashion, and trend are not gender-specific, so we have the
opportunity for everyone to become beautiful in one place, regardless of gender.
And among our Barber, Beauty and Massage departments, you can find the right services
for you.
</p>
<p>
With nearly 10 years of experience in the beauty industry, including many years
of international experience, I thought it was time to open my own studio here in
Trondheim at Innheredsveien 92, where guests can park free of charge during
the treatments.
</p>
<p>
It is an extra pleasure for me that I managed to find a traditional place where
there was already a beauty salon in the 50s. Therefore, I now have the
opportunity to carry on this tradition in a modern environment.
Become beautiful with us. We look forward to seeing you.
</p>
</div>
<div class="bem-jobb">
<img src="<?php echo base_url();?>assets/img/evelin_beauty.JPG">
<h4>Evelin Bence <br> <span>Owner, Studio Beve</span></h4>
</div>
</div>
<?php /* Therapist sub-section, following the per-worker pattern
barber/beauty use: circular portrait + name, then the bio
with a photo strip alongside. */ ?>
<div class="dolgozo-nev">
<hr>
<img src="<?php echo base_url();?>assets/img/massage/vincent/vincent.jpg" class="dolgozo-kep">
<h4>VINCENT Pusch <span>- Massage Therapist</span></h4>
</div>
<div class="bemutatkozas-dolgozo">
<div class="bem-dolgozo-bal">
<p>My interest in massage, manual treatment techniques, and the way the human body functions has been with me since 2011 and has, over the years, become a central part of my professional career.</p>
<p>I completed my physiotherapy education in Germany. I began my professional career at a military hospital, where I worked as a physiotherapist and manual therapist. I later continued my work at a multidisciplinary clinic before establishing and running my own private practice in Germany until I moved to Norway.</p>
<p>Over the years, I have continuously expanded my professional knowledge and experience. In addition to physiotherapy and manual therapy, I have worked with sports physiotherapy and, since 2019, also with craniosacral therapy and osteopathy. Continuous professional development and learning new methods are important to me.</p>
<p>I believe that every person and every body is different, which is why I always base my treatments on each client&rsquo;s individual needs and goals. I select and combine different massage and manual techniques accordingly, whether the goal is relaxation, relieving muscle tension caused by everyday or physical strain, recovery after exercise, or simply giving the body time to recharge.</p>
<p>I have been living in Trondheim since May 2026. The process of obtaining Norwegian recognition of my foreign physiotherapy qualification is currently ongoing. At Studio Beve, I currently work as a massage therapist and offer massage and other manual treatments that do not require Norwegian authorization as healthcare personnel. Once Norwegian authorization has been obtained, my professional activities and the range of treatments I can offer may be expanded further.</p>
<p>I look forward to getting to know Studio Beve&rsquo;s clients and using my professional experience to provide attentive, individually tailored treatments.</p>
</div>
<div class="bem-dolgozo-jobb">
<img src="<?php echo base_url();?>assets/img/massage/vincent/1.jpg">
<img src="<?php echo base_url();?>assets/img/massage/vincent/2.jpg">
<img src="<?php echo base_url();?>assets/img/massage/vincent/3.jpg">
<img src="<?php echo base_url();?>assets/img/massage/vincent/4.jpg">
</div>
</div>
</div>
</div>
</section>
<!-- Treatments -->
<section id="treatments" class="wrapper style2">
<div class="container">
<div class="inner">
<header class="major">
<img src="<?php echo base_url();?>assets/img/massage/ollo_rose.png">
<h2 class="nyitva">Treatments</h2>
</header>
</div>
<div class="service-block">
<div class="service-area">
<div class="one-third service-part1">
<div class="service-inner">
<div class="service service1">
<div class="service-text service-text1">
<div class="service-icon icon1"></div>
<div class="service-content">
<div class="main-title">Classic Swedish Massage</div>
<div class="sub-title-serv">full body, relaxation and well-being</div>
</div>
</div>
<div class="service-text service-text2">
<div class="service-icon icon2"></div>
<div class="service-content">
<div class="main-title">Sports Massage</div>
<div class="sub-title-serv">for active individuals and athletes</div>
</div>
</div>
<div class="service-text service-text3">
<div class="service-icon icon3"></div>
<div class="service-content">
<div class="main-title">Deep Tissue Massage</div>
<div class="sub-title-serv">deeper muscles and connective tissue</div>
</div>
</div>
</div>
</div>
</div>
<div class="one-third service-part2">
<div class="service-inner">
<div class="service service2">
<a href="<?php echo SITEURL;?>en/booking/massage" class="banner-anchor"><img src="<?php echo base_url();?>assets/img/service-banner-massage.png" alt="massage" class="banner-image1"></a>
</div>
</div>
</div>
<div class="one-third service-part3">
<div class="service-inner">
<div class="service service3">
<div class="service-text service-text1">
<div class="service-icon icon1"></div>
<div class="service-content">
<div class="main-title">Work Recovery Massage</div>
<div class="sub-title-serv">office and physical work</div>
</div>
</div>
<div class="service-text service-text2">
<div class="service-icon icon2"></div>
<div class="service-content">
<div class="main-title">Studio Beve Signature</div>
<div class="sub-title-serv">individually tailored treatment</div>
</div>
</div>
<div class="service-text service-text3">
<div class="service-icon icon3"></div>
<div class="service-content">
<div class="main-title">Craniosacral Treatment</div>
<div class="sub-title-serv">gentle, holistic manual treatment</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Prices -->
<section id="one" class="wrapper style1">
<div class="container">
<div class="inner">
<header class="major">
<img src="<?php echo SITEURL;?>assets/img/massage/ollo_rose.png">
<h2>Prices</h2>
<h4 class="prices-note">The stated treatment time is hands-on treatment time. The appointment reserved for you is slightly longer, allowing time to change and to pay.</h4>
</header>
<?php
/**
* Grouped by treatment type. Two deliberate differences from the
* barber/beauty price lists, both because massage sells the SAME
* treatment at two or three lengths:
*
* 1. The description is printed ONCE per group rather than once
* per duration, so a three-length treatment does not repeat
* its blurb three times.
* 2. The row label is the duration alone. services.service_name
* has to stay self-describing ("Klassisk svensk massasje -
* 50 min") because the booking form and the confirmation
* email render it with no category around it - but here the
* category heading is directly above, so repeating it just
* makes every row wrap.
*
* $services arrives ordered by service_id and the catalogue is
* inserted grouped, so a simple change-detect is enough.
*/
$prevousCategory = '';
if(is_array($services)){
foreach($services as $serviceItem){
if($serviceItem->service_category != $prevousCategory){
if($prevousCategory !== ''){
echo '<div style="clear:both;"></div>';
}
?>
<div class="prices-category" style="width: 100%">
<h4><?php echo $serviceItem->service_category;?></h4>
<p class="category-intro"><?php echo $serviceItem->service_description;?></p>
</div>
<?php
}
// Same rule as the booking form: the category heading is
// directly above, so the row need not repeat it.
$rowLabel = vertical_service_label($vertical, $serviceItem);
?>
<div class="one-half">
<div class="category-content">
<div class="category-text-left">
<div class="main-title"><a><?php echo $rowLabel;?></a></div>
</div>
<div class="category-text-right">
<div class="price">kr <?php echo $serviceItem->service_price;?></div>
</div>
</div>
</div>
<?php
$prevousCategory = $serviceItem->service_category;
}
echo '<div style="clear:both;"></div>';
}
?>
</div>
</div>
</section>
<!-- Opening hours -->
<section id="two" class="wrapper style2">
<div class="container">
<div class="inner">
<header class="major">
<img src="<?php echo SITEURL;?>assets/img/massage/ollo_rose.png">
<h2 class="nyitva">Opening Hours</h2>
<h4>Free customer parking during your treatment</h4>
</header>
</div>
<div class="nyitvatartas">
<div class="about-content">
<div class="about-text-left">
<div class="title">Weekdays</div>
</div>
<div class="about-text-right">
<div class="time">10:00 - 18:00</div>
</div>
</div>
<div class="about-content">
<div class="about-text-left">
<div class="title">Saturday</div>
</div>
<div class="about-text-right">
<div class="time">10:00 - 16:00</div>
</div>
</div>
<div class="about-content">
<div class="about-text-left">
<div class="title">Sunday</div>
</div>
<div class="about-text-right">
<div class="closed">Closed</div>
</div>
</div>
</div>
</div>
</section>