New barber Amarildo Champimpi is introduced on the barber page in all three
languages, and workers can now be restricted to a subset of the services in
their category.
Amarildo cannot perform beard colouring, any waxing, or the all-in package, and
he is a barber only - but category 1 "Kozmetika" holds 19 barber AND 27 beauty
services, so the category-derived vertical wrongly made him beauty-capable.
Because the worker picker is only ever fetched AFTER services are ticked, one
per-service capability filter solves both problems: excluding him from every
beauty service removes him from that vertical entirely.
worker_services(worker_id, service_id) is an allow-list where an EMPTY set means
UNRESTRICTED. That default is deliberate: a missing migration degrades to the
previous behaviour instead of hiding every worker from the booking flow, and
existing workers keep working untouched. Ticking every box in the admin grid
stores nothing at all, so an unrestricted worker also picks up services added
later; unticking even one makes the worker restricted, and new services must
then be granted explicitly.
Enforcement is in three places. The picker offers only workers who can perform
EVERY selected service, and both booking paths re-check server-side, since the
picker is only a UI affordance - a crafted POST now gets service_not_offered/403
rather than a booking the worker cannot honour.
Fixed alongside, all found while building the above:
- getWorkersByCategorySlug() never filtered is_active, so marking a worker
inactive had NO effect on the public booking flow. Both of its callers are
guest-facing. The sibling fallback getActiveWorkers() had always filtered it.
- Worker profile picture uploads failed SILENTLY above PHP's upload_max_filesize.
Both upload blocks gated on tmp_name alone, which cannot distinguish a rejected
upload from "no file chosen" - PHP empties tmp_name in both cases - so the
worker was saved with an empty worker_profile_img and no error shown. The
upload error code is now read and reported, and a separate guard catches
post_max_size overflow, where $_POST and $_FILES both arrive empty and the form
silently did nothing at all.
- createWorker() omitted is_active from its INSERT, so the column default (1)
always won and a worker created as inactive silently came back active.
Migration - the table MUST be created before this code is deployed, because the
picker query subselects it whenever service ids are passed:
CREATE TABLE worker_services (
worker_id INT NOT NULL,
service_id INT NOT NULL,
PRIMARY KEY (worker_id, service_id),
KEY idx_worker_services_worker (worker_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Already applied on test, dev and prod. Server-side, upload_max_filesize/
post_max_size were raised to 8M/12M on all three environments (php.ini on test,
.user.ini on the shared-host dev and prod docroots) - not carried by this commit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TYGSbK1erKv7VG1pvdPEjG
308 lines
16 KiB
PHP
Executable File
308 lines
16 KiB
PHP
Executable File
<!-- Banner -->
|
||
|
||
<section id="banner">
|
||
<img src="<?php echo SITEURL;?>assets/img/barber/banner.jpg" />
|
||
<!--div class="inner">
|
||
<h2>Ipsum dolore</h2>
|
||
<p>Amet lorem ipsum consequat feugiat</p>
|
||
</div-->
|
||
</section>
|
||
|
||
<section id="mobil-menu">
|
||
<div class="mobil-menucont">
|
||
<ul>
|
||
<li class="mobil-menu-book"><a href="<?php echo SITEURL;?>en/booking/barber">BOOK TIME</a></li>
|
||
<li class=""><a href="https://www.facebook.com/studiobevebarber" class="icon brands fa-facebook-f"><span class="label">Facebook</span></a></li>
|
||
<li class=""><a href="https://www.instagram.com/studio.beve.barber/" 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 SITEURL;?>assets/img/ollo.png">
|
||
<h2>About Us</h2>
|
||
</header>
|
||
<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. We have two private parking spaces that our
|
||
guests can use free of charge during their treatment, subject to availability.
|
||
</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 SITEURL;?>assets/img/evelin.JPG">
|
||
<h4>Evelin Bence <br> <span>Owner, Studio Beve</span></h4>
|
||
</div>
|
||
</div>
|
||
<div class="dolgozo-nev">
|
||
<hr>
|
||
<img src="<?php echo SITEURL;?>assets/img/barber/evelin/evelin.jpg" class="dolgozo-kep">
|
||
<h4>EVELIN Bence <span>- Barber</span></h4>
|
||
</div>
|
||
<div class="bemutatkozas-dolgozo">
|
||
<div class="bem-dolgozo-bal">
|
||
<p>
|
||
"I got acquainted with the basics of being a barber at a very young age, as my
|
||
mother is also a barber. From her I managed at a very young age to master the
|
||
tricks of this wonderful profession. You could say I was born into the profession,
|
||
and I've been officially a second-generation barber for a few years now.
|
||
I am proud to have learned from the best. My master barber and friend Ruben
|
||
Tenkely, owner of "Peaky Barbers – Tenkely Limited Company", European Cup
|
||
winner and Hungarian champion, has gained a lot of experience internationally.
|
||
Among other things, he worked at one of Norway's most famous traditional
|
||
salons, Brødrene Fevangs Barberstue. This allowed me to learn about trends in
|
||
Norway.
|
||
</p>
|
||
<p>
|
||
I have been living in Norway since April 2023 and am eager to learn
|
||
Norwegian. I feel fortunate to have had the opportunity to work alongside Felipe
|
||
Romero in Trondheim, who is also an incredibly knowledgeable barber. More
|
||
than one of you may have met and gotten to know me at his salon.
|
||
I constantly participate in further trainings because I believe that continuous
|
||
development, quality work and dedication help me to become a better
|
||
professional day by day."
|
||
</p>
|
||
</div>
|
||
<div class="bem-dolgozo-jobb">
|
||
<img src="<?php echo SITEURL;?>assets/img/barber/evelin/1.jpg">
|
||
<img src="<?php echo SITEURL;?>assets/img/barber/evelin/2.jpg">
|
||
<img src="<?php echo SITEURL;?>assets/img/barber/evelin/3.jpg">
|
||
<img src="<?php echo SITEURL;?>assets/img/barber/evelin/4.jpg">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="dolgozo-nev">
|
||
<hr>
|
||
<img src="<?php echo SITEURL;?>assets/img/barber/amarildo/amarildo.jpg" class="dolgozo-kep">
|
||
<h4>AMARILDO Champimpi <span>- Barber</span></h4>
|
||
</div>
|
||
<div class="bemutatkozas-dolgozo no-gallery">
|
||
<div class="bem-dolgozo-bal">
|
||
<p>Amarildo here, originally from Albania. I have more than six years of experience as a barber, and throughout my career I have worked in both Greece and England. I have been living in Norway for the past three years, and I am now happy to join the Studio Beve team.</p>
|
||
<p>I specialise in both modern and classic haircuts, as well as skin fades and taper fades. Precision and attention to detail are very important to me, and I always strive to achieve the best possible result for every client.</p>
|
||
<p>Since I started working as a barber, my profession has become a true passion of mine. I continuously develop my skills, learn new techniques, and keep myself up to date with the latest trends and styles.</p>
|
||
<p>For me, barbering is not simply about giving someone a haircut. It is important to create a style that truly suits each individual and to make sure every client leaves my chair feeling confident and satisfied.</p>
|
||
<p>I am excited to bring the experience I have gained over the years, my dedication to the profession, and my creativity to Studio Beve, and I look forward to welcoming you to my chair soon.</p>
|
||
</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 SITEURL;?>assets/img/ollo.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">Beard Trimming</div>
|
||
<div class="sub-title-serv">with scissors and machine and knife shave with hot towel</div>
|
||
</div>
|
||
</div>
|
||
<div class="service-text service-text2">
|
||
<div class="service-icon icon2"></div>
|
||
<div class="service-content">
|
||
<div class="main-title">Child Haircut</div>
|
||
<div class="sub-title-serv">0-12 years old</div>
|
||
</div>
|
||
</div>
|
||
<div class="service-text service-text3">
|
||
<div class="service-icon icon3"></div>
|
||
<div class="service-content">
|
||
<div class="main-title">Beard dying</div>
|
||
<div class="sub-title-serv">beard dying</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="one-third service-part2">
|
||
<div class="service-inner">
|
||
<div class="service service2">
|
||
<a href="#" class="banner-anchor"><img src="<?php echo SITEURL;?>assets/img/service-banner.png" alt="service-banner1" 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">Traditional shave</div>
|
||
<div class="sub-title-serv">traditional knife shave<br> with hot towel</div>
|
||
</div>
|
||
</div>
|
||
<div class="service-text service-text2">
|
||
<div class="service-icon icon2"></div>
|
||
<div class="service-content">
|
||
<div class="main-title">Men's haircut or Skinfade men's haircut</div>
|
||
<div class="sub-title-serv">fade from skin, that is taper, low, mid or high</div>
|
||
</div>
|
||
</div>
|
||
<div class="service-text service-text3">
|
||
<div class="service-icon icon3"></div>
|
||
<div class="service-content">
|
||
<div class="main-title">Wax</div>
|
||
<div class="sub-title-serv">nose, ear and<br> eyebrow waxing</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
|
||
<!-- One -->
|
||
|
||
<section id="one" class="wrapper style1">
|
||
<div class="container">
|
||
<div class="inner">
|
||
<header class="major">
|
||
<img src="<?php echo SITEURL;?>/assets/img/barber/ollo.png">
|
||
<h2>Prices</h2>
|
||
<h4>The price of all hair treatments include hairwash and styling. <br>10% student reduction upon showing ID, available from Monday to Thursday.</h4>
|
||
</header>
|
||
<?php
|
||
$prevousCategory = '';
|
||
|
||
if(is_array($services)){
|
||
foreach($services as $serviceItem){
|
||
if($serviceItem->service_category != $prevousCategory){
|
||
?>
|
||
<div class="prices-category" style="width: 100%;">
|
||
<h4 style="color: #9e8462;"><?php echo $serviceItem->service_category;?></h4>
|
||
</div>
|
||
<?php
|
||
}
|
||
?>
|
||
<div class="one-half">
|
||
<div class="category-content">
|
||
<div class="category-text-left">
|
||
<div class="main-title"><a><?php echo $serviceItem->service_name;?></a></div>
|
||
<div class="sub-title"><?php echo $serviceItem->service_description;?></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;
|
||
}
|
||
}
|
||
?>
|
||
|
||
<?php
|
||
/*
|
||
if(is_array($services)){
|
||
foreach($services as $serviceItem){
|
||
?>
|
||
<div class="one-half">
|
||
<div class="category-content">
|
||
<div class="category-text-left">
|
||
<div class="main-title"><a><?php echo $serviceItem->service_name;?></a></div>
|
||
<div class="sub-title"><?php echo $serviceItem->service_description;?></div>
|
||
</div>
|
||
<div class="category-text-right">
|
||
<div class="price">kr <?php echo $serviceItem->service_price;?></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<?php
|
||
}
|
||
}
|
||
*/
|
||
?>
|
||
|
||
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
|
||
|
||
<!-- Two -->
|
||
<section id="two" class="wrapper style2">
|
||
<div class="container">
|
||
<div class="inner">
|
||
<header class="major">
|
||
<img src="<?php echo SITEURL;?>assets/img/barber/ollo.png">
|
||
<h2 class="nyitva">Working Hours</h2>
|
||
<h4>2 free customer parking spaces – subject to availability</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>
|