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>
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
/**
|
||||
* Generic <head> for any vertical.
|
||||
*
|
||||
* Expects $vertical (a row from application/config/verticals.php).
|
||||
* Replaces the per-vertical barber-head.php / beauty-head.php, which differ
|
||||
* only in <title>, og:site_name, og:url, meta description and the stylesheet.
|
||||
*/
|
||||
$this->load->helper('url');
|
||||
?>
|
||||
<head>
|
||||
<!-- Google Tag Manager -->
|
||||
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
||||
})(window,document,'script','dataLayer','GTM-TWRHH7DN');</script>
|
||||
<!-- End Google Tag Manager -->
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
|
||||
<title><?php echo htmlspecialchars($vertical['title'], ENT_QUOTES, 'UTF-8');?></title>
|
||||
<meta property="fb:app_id" content="285150047390571" />
|
||||
<meta property="og:site_name" content="<?php echo htmlspecialchars($vertical['og_site_name'], ENT_QUOTES, 'UTF-8');?>">
|
||||
<meta property="og:image" content="<?php echo SITEURL;?>assets/img/studiobeve_facebook.jpg"/>
|
||||
<meta property="og:title" content="Studio Beve" />
|
||||
<meta property="og:description" content="<?php echo htmlspecialchars($vertical['og_description'], ENT_QUOTES, 'UTF-8');?>" />
|
||||
<meta property="og:url" content="<?php echo SITEURL.$vertical['slug'];?>" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta name="description" content="<?php echo htmlspecialchars($vertical['meta_description'], ENT_QUOTES, 'UTF-8');?>">
|
||||
<link rel="icon" type="image/x-icon" href="<?php echo base_url();?>assets/img/favicon/favicon.ico" />
|
||||
<link rel="icon" type="image/png" href="<?php echo base_url();?>assets/img/favicon/favicon.png" />
|
||||
<link rel="icon" type="image/gif" href="<?php echo base_url();?>assets/img/favicon/favicon.gif" />
|
||||
<!-- Opera Speed Dial Favicon -->
|
||||
<link rel="icon" type="image/png" href="<?php echo base_url();?>assets/img/favicon/speeddial-160px.png" />
|
||||
<!-- For iPhone 4 Retina display: -->
|
||||
<script src="<?php echo base_url();?>assets/js/jquery/jquery.js"></script>
|
||||
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="<?php echo base_url();?>assets/img/favicon/apple-touch-icon-114x114-precomposed.png">
|
||||
<link rel="stylesheet" href="<?php echo base_url();?>assets/css/<?php echo $vertical['css'];?>" />
|
||||
<noscript><link rel="stylesheet" href="<?php echo base_url();?>assets/css/noscript.css" /></noscript>
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Abril+Fatface&family=Open+Sans:ital,wght@0,700;0,800;1,700;1,800&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="<?php echo base_url();?>assets/js/jquery/jquery-ui.css">
|
||||
<script src="<?php echo base_url();?>assets/js/jquery/jquery-ui.js"></script>
|
||||
<script src="https://momentjs.com/downloads/moment-with-locales.js"></script>
|
||||
<!-- Start cookieyes banner --> <script id="cookieyes" type="text/javascript" src="https://cdn-cookieyes.com/client_data/82ccb0b38ad63612e4c6a3fa/script.js"></script> <!-- End cookieyes banner -->
|
||||
<link rel="stylesheet" type="text/css" href="<?php echo base_url();?>assets/css/style.css">
|
||||
<link rel="stylesheet" type="text/css" href="<?php echo base_url();?>assets/css/responsive.css">
|
||||
<script src="<?php echo base_url();?>assets/js/script.js"></script>
|
||||
</head>
|
||||
Reference in New Issue
Block a user