Files
NextChair/legacy-nextjs/templates/22-NextChair - Owner - Loyalty Co.html
T
Stefan Lazic 2ef3654841
CI / ci (push) Canceled after 0s
PHP Rebuild
Initial PHP rebuild of the app
2026-09-05 08:21:33 +01:00

418 lines
21 KiB
HTML
Executable File
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.
<html lang="en">
<head>
<script>
window.FontAwesomeConfig = {
autoReplaceSvg: 'nest'
};
</script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/js/all.min.js"
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<meta charset="utf-8">
<meta name="viewport"
content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>Loyalty Configuration</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&amp;display=swap"
rel="stylesheet">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script>
tailwind.config = {
theme:
{
extend:
{
colors:
{
ink: '#111b15',
stone: '#f4f6f4',
surface: '#ffffff',
muted: '#6c7870',
line: '#e1e7e2',
accent: '#10b981',
},
fontFamily:
{
sans: ['Inter', 'sans-serif']
},
borderRadius:
{
jumbo: '1.5rem',
tile: '1rem'
}
}
}
}
</script>
<style>
::-webkit-scrollbar
{
display: none;
}
</style>
</head>
<body class="bg-stone text-ink font-sans pb-40">
<!-- Header -->
<header id="header"
class="bg-white border-b border-line px-6 pt-12 pb-5 sticky top-0 z-50 flex items-center justify-between">
<div class="flex items-center gap-4"><button onclick="history.back()"
class="w-10 h-10 rounded-full border border-line flex items-center justify-center"><i
class="fa-solid fa-arrow-left text-sm"></i></button>
<h1 class="text-xl font-bold tracking-tight">Loyalty Setup</h1>
</div><button
class="bg-accent text-white text-xs font-bold uppercase tracking-widest px-5 py-2.5 rounded-full">Save</button>
</header>
<main id="main" class="mt-6 px-5 space-y-8">
<!-- 1. Card Appearance -->
<section id="card-appearance" class="space-y-4">
<h3
class="text-[10px] font-bold uppercase tracking-widest text-muted ml-1">
Card Appearance</h3>
<div id="i3isp"
class="bg-white p-6 rounded-jumbo border border-line shadow-sm space-y-8 h-[620px]">
<!-- Back of Card -->
<div id="back-image-section"></div><!-- Divider -->
<!-- Front Background — Toggle -->
<div id="front-background-section"
class="relative left-0 top-[-37px]">
<div class="flex items-center justify-between mb-5"><label
class="block text-xs font-bold">Background</label>
<!-- Toggle Switch -->
<div class="flex items-center gap-2 bg-stone rounded-full p-1">
<button id="btn-color" onclick="setFrontMode('color')"
class="px-3 py-1.5 rounded-full text-[10px] font-bold uppercase tracking-widest transition-all bg-ink text-white shadow">Color</button><button
id="btn-image" onclick="setFrontMode('image')"
class="px-3 py-1.5 rounded-full text-[10px] font-bold uppercase tracking-widest transition-all text-muted">Image</button>
</div>
</div><!-- Color Mode -->
<div id="front-color-mode">
<div class="flex items-center gap-3 flex-wrap"><button
onclick="selectColor(this, '#111b15')"
class="w-11 h-11 rounded-full bg-[#111b15] border-[3px] border-accent shadow-md ring-2 ring-accent/30"></button><button
onclick="selectColor(this, '#f4f6f4')"
class="w-11 h-11 rounded-full bg-[#f4f6f4] border-2 border-line"></button><button
onclick="selectColor(this, '#1e293b')"
class="w-11 h-11 rounded-full bg-[#1e293b] border-2 border-line"></button><button
onclick="selectColor(this, '#451a03')"
class="w-11 h-11 rounded-full bg-[#451a03] border-2 border-line"></button><button
onclick="selectColor(this, '#0f172a')"
class="w-11 h-11 rounded-full bg-[#0f172a] border-2 border-line"></button><button
onclick="selectColor(this, '#064e3b')"
class="w-11 h-11 rounded-full bg-[#064e3b] border-2 border-line"></button>
<!-- Custom --><label
class="w-11 h-11 rounded-full border-2 border-dashed border-line flex items-center justify-center text-muted cursor-pointer relative overflow-hidden"><i
class="fa-solid fa-plus text-xs"></i><input type="color"
onchange="handleCustomColor(this)"
class="absolute inset-0 opacity-0 cursor-pointer w-full h-full"></label>
</div>
<div id="selected-color-preview"
class="mt-4 flex items-center gap-3">
<div
class="w-6 h-6 rounded-full bg-[#111b15] border border-line">
</div><span class="text-xs text-muted font-medium">#111b15 —
Midnight Ink</span>
</div>
</div><!-- Image Mode (hidden by default) -->
<div id="front-image-mode" class="hidden">
<div
class="relative w-full aspect-video rounded-xl overflow-hidden border-2 border-dashed border-line group cursor-pointer">
<div
class="absolute inset-0 flex flex-col items-center justify-center text-muted gap-3">
<div
class="w-14 h-14 rounded-full bg-stone flex items-center justify-center">
<i class="fa-solid fa-image text-2xl"></i></div>
<p class="text-xs font-bold uppercase tracking-widest">Tap to
upload</p>
<p class="text-[10px]">JPG, PNG or WebP — max 5MB</p>
</div>
</div>
<p class="text-[10px] text-muted mt-2 uppercase tracking-widest">
Image will be used as the front card background</p>
</div>
</div><!-- Divider -->
<div id="idxtw" class="border-t border-line relative top-[-38px]">
</div><!-- Stamp Icon -->
<div id="stamp-section" class="relative top-[-44px]"><label
class="block text-xs font-bold mb-3">Stamp Icon / Image</label>
<div class="grid grid-cols-4 gap-3"><button
onclick="selectStamp(this)"
class="aspect-square bg-stone rounded-xl flex items-center justify-center text-2xl text-ink border-2 border-accent shadow-sm"><i
class="fa-solid fa-mustache"></i></button><button
onclick="selectStamp(this)"
class="aspect-square bg-stone rounded-xl flex items-center justify-center text-2xl text-muted border-2 border-transparent"><i
class="fa-solid fa-scissors"></i></button><button
onclick="selectStamp(this)"
class="aspect-square bg-stone rounded-xl flex items-center justify-center text-2xl text-muted border-2 border-transparent"><i
class="fa-solid fa-crown"></i></button><button
onclick="selectStamp(this)"
class="aspect-square bg-stone rounded-xl flex items-center justify-center text-2xl text-muted border-2 border-transparent"><i
class="fa-solid fa-star"></i></button><button
onclick="selectStamp(this)"
class="aspect-square bg-stone rounded-xl flex items-center justify-center text-2xl text-muted border-2 border-transparent"><i
class="fa-solid fa-fire"></i></button><button
onclick="selectStamp(this)"
class="aspect-square bg-stone rounded-xl flex items-center justify-center text-2xl text-muted border-2 border-transparent"><i
class="fa-solid fa-bolt"></i></button><button
onclick="selectStamp(this)"
class="aspect-square bg-stone rounded-xl flex items-center justify-center text-2xl text-muted border-2 border-transparent"><i
class="fa-solid fa-gem"></i></button><button
class="aspect-square border-2 border-dashed border-line rounded-xl flex flex-col items-center justify-center text-muted gap-1"><i
class="fa-solid fa-plus text-xs"></i><span
class="text-[9px] font-bold uppercase tracking-widest">Upload</span></button>
</div>
</div><!-- Divider -->
<div class="border-t border-line relative top-[-50px]" id="i0k6o">
</div><!-- Terms Line -->
<div id="terms-section" class="relative top-[-57px]"><label
class="block text-xs font-bold mb-1">Terms Line <span
class="text-muted font-normal">(shown below card)</span></label>
<p class="text-[10px] text-muted uppercase tracking-widest mb-3">Max
60 characters</p>
<div class="relative"><input id="terms-input" type="text"
maxlength="60"
value="Valid for 12 months. One reward per visit."
oninput="updateCharCount(this)"
placeholder="e.g. Valid for 12 months. One reward per visit."
class="w-full bg-stone border border-line rounded-xl px-4 py-4 text-sm font-medium outline-none focus:border-accent transition-colors pr-16"><span
id="char-count"
class="absolute right-4 top-1/2 -translate-y-1/2 text-[10px] font-bold text-muted">42/60</span>
</div><!-- Preview -->
</div>
</div>
</section><!-- 2. Reward Rules -->
<section id="reward-rules" class="space-y-4">
<h3
class="text-[10px] font-bold uppercase tracking-widest text-muted ml-1">
Reward Rules</h3>
<div
class="bg-white p-6 rounded-jumbo border border-line shadow-sm space-y-8">
<!-- Stamps Required -->
<div id="stamps-required"><label
class="block text-xs font-bold mb-4">Stamps required for
reward</label>
<div class="flex items-center gap-6"><button
onclick="adjustStamps(-1)"
class="w-12 h-12 rounded-full border-2 border-line flex items-center justify-center text-lg font-bold hover:border-accent hover:text-accent transition-all">−</button>
<div class="text-center"><span id="stamp-count"
class="text-4xl font-black">10</span>
<p
class="text-[10px] text-muted uppercase tracking-widest mt-1">
stamps</p>
</div><button onclick="adjustStamps(1)"
class="w-12 h-12 rounded-full border-2 border-line flex items-center justify-center text-lg font-bold hover:border-accent hover:text-accent transition-all">+</button>
</div><!-- Visual dots preview -->
<div id="stamp-dots" class="flex flex-wrap gap-2 mt-5">
</div>
<p class="text-[10px] text-muted mt-3 uppercase tracking-widest">
Recommended: 8–10 stamps</p>
</div><!-- Divider -->
<div class="border-t border-line"></div><!-- Reward Type -->
<div id="reward-type-section"><label
class="block text-xs font-bold mb-4">Reward Type</label>
<div class="grid grid-cols-3 gap-2 mb-5"><button
onclick="selectReward(this, 'percent')"
class="py-3.5 border-2 border-accent bg-accent/5 rounded-xl text-[10px] font-bold uppercase tracking-widest text-ink"><i
class="fa-solid fa-percent block text-base mb-1"></i>Percentage
</button><button onclick="selectReward(this, 'fixed')"
class="py-3.5 border border-line bg-white rounded-xl text-[10px] font-bold uppercase tracking-widest text-muted"><i
class="fa-solid fa-sterling-sign block text-base mb-1"></i>Fixed
Off
</button><button onclick="selectReward(this, 'free')"
class="py-3.5 border border-line bg-white rounded-xl text-[10px] font-bold uppercase tracking-widest text-muted"><i
class="fa-solid fa-gift block text-base mb-1"></i>Free
</button></div><!-- Value input (hidden for free) -->
<div id="reward-value-wrap">
<div class="relative mb-4"><span id="reward-symbol"
class="absolute left-4 top-1/2 -translate-y-1/2 font-black text-ink">%</span><input
id="reward-value" type="number" value="50"
class="w-full bg-stone border border-line rounded-xl pl-10 pr-4 py-4 font-bold text-lg outline-none focus:border-accent transition-colors">
</div>
</div><!-- Reward label -->
<div><label
class="block text-[10px] font-bold uppercase tracking-widest text-muted mb-2 ml-1">Reward
Display Name</label><input id="reward-name" type="text"
value="50% Off Any Service" placeholder="e.g. Next Haircut Free"
class="w-full bg-stone border border-line rounded-xl px-4 py-4 text-sm font-medium outline-none focus:border-accent transition-colors">
</div>
</div>
</div>
</section><!-- Danger Zone -->
<section id="danger-zone" class="pb-2"><button
class="w-full py-4 border-2 border-dashed border-red-200 rounded-xl text-[10px] font-bold uppercase tracking-widest text-red-400 hover:border-red-500 hover:text-red-500 transition-all flex items-center justify-center gap-2"><i
class="fa-solid fa-ban"></i> Disable Loyalty Program
</button></section>
</main><!-- Bottom Nav -->
<nav id="bottom-nav"
class="fixed bottom-0 left-0 right-0 z-50 bg-[#0c0c0c] text-gray-400 py-4 px-10 border-t border-white/5 flex items-center justify-between">
<a href="#"
class="flex flex-col items-center gap-1 hover:text-white transition-colors"><i
class="fa-solid fa-calendar-days text-xl"></i><span
class="text-[10px] font-medium">Calendar</span></a><a href="#"
class="flex flex-col items-center gap-1 hover:text-white transition-colors"><i
class="fa-solid fa-scissors text-xl"></i><span
class="text-[10px] font-medium">Current</span></a><a href="#"
class="flex flex-col items-center gap-1 hover:text-white transition-colors"><i
class="fa-solid fa-users text-xl"></i><span
class="text-[10px] font-medium">Clients</span></a><a href="#"
class="flex flex-col items-center gap-1 text-white"><i
class="fa-solid fa-gear text-xl"></i><span
class="text-[10px] font-medium">Settings</span></a></nav>
<script>
// ── Front background toggle ──────────────────────────────────────
function setFrontMode(mode)
{
const btnColor = document.getElementById('btn-color');
const btnImage = document.getElementById('btn-image');
const colorMode = document.getElementById('front-color-mode');
const imageMode = document.getElementById('front-image-mode');
if (mode === 'color')
{
btnColor.classList.add('bg-ink', 'text-white', 'shadow');
btnColor.classList.remove('text-muted');
btnImage.classList.remove('bg-ink', 'text-white', 'shadow');
btnImage.classList.add('text-muted');
colorMode.classList.remove('hidden');
imageMode.classList.add('hidden');
}
else
{
btnImage.classList.add('bg-ink', 'text-white', 'shadow');
btnImage.classList.remove('text-muted');
btnColor.classList.remove('bg-ink', 'text-white', 'shadow');
btnColor.classList.add('text-muted');
imageMode.classList.remove('hidden');
colorMode.classList.add('hidden');
}
}
// ── Color selection ──────────────────────────────────────────────
const colorNames = {
'#111b15': 'Midnight Ink',
'#f4f6f4': 'Cloud Stone',
'#1e293b': 'Slate Deep',
'#451a03': 'Mahogany',
'#0f172a': 'Void Blue',
'#064e3b': 'Forest Dark',
};
function selectColor(btn, hex)
{
document.querySelectorAll('#front-color-mode button[onclick]').forEach(
b =>
{
b.classList.remove('border-accent', 'ring-2', 'ring-accent/30',
'shadow-md');
b.classList.add('border-line');
b.style.borderWidth = '2px';
});
btn.classList.add('border-accent', 'ring-2', 'ring-accent/30',
'shadow-md');
btn.classList.remove('border-line');
btn.style.borderWidth = '3px';
const preview = document.getElementById('selected-color-preview');
const name = colorNames[hex] || 'Custom';
preview.querySelector('div').style.backgroundColor = hex;
preview.querySelector('span').textContent = `${hex} — ${name}`;
}
function handleCustomColor(input)
{
colorNames[input.value] = 'Custom';
}
// ── Stamp icon selection ─────────────────────────────────────────
function selectStamp(btn)
{
document.querySelectorAll('#stamp-section button[onclick]').forEach(b =>
{
b.classList.remove('border-accent', 'text-ink');
b.classList.add('border-transparent', 'text-muted');
});
btn.classList.add('border-accent', 'text-ink');
btn.classList.remove('border-transparent', 'text-muted');
}
// ── Stamps counter & dots ────────────────────────────────────────
let stampCount = 10;
function adjustStamps(delta)
{
stampCount = Math.min(20, Math.max(3, stampCount + delta));
document.getElementById('stamp-count').textContent = stampCount;
renderDots();
}
function renderDots()
{
const container = document.getElementById('stamp-dots');
container.innerHTML = '';
for (let i = 0; i < stampCount; i++)
{
const dot = document.createElement('div');
dot.className = 'w-6 h-6 rounded-full border-2 border-line bg-stone';
container.appendChild(dot);
}
}
renderDots();
// ── Reward type ──────────────────────────────────────────────────
function selectReward(btn, type)
{
document.querySelectorAll('#reward-type-section .grid button').forEach(
b =>
{
b.classList.remove('border-accent', 'bg-accent/5', 'text-ink');
b.classList.add('border-line', 'text-muted');
});
btn.classList.add('border-accent', 'bg-accent/5', 'text-ink');
btn.classList.remove('border-line', 'text-muted');
const valueWrap = document.getElementById('reward-value-wrap');
const symbol = document.getElementById('reward-symbol');
const rewardName = document.getElementById('reward-name');
if (type === 'free')
{
valueWrap.classList.add('hidden');
rewardName.value = 'Next Service Free';
}
else if (type === 'percent')
{
valueWrap.classList.remove('hidden');
symbol.textContent = '%';
rewardName.value = '50% Off Any Service';
}
else
{
valueWrap.classList.remove('hidden');
symbol.textContent = '£';
rewardName.value = '£5 Off Next Visit';
}
}
// ── Terms character counter ──────────────────────────────────────
function updateCharCount(input)
{
const len = input.value.length;
const counter = document.getElementById('char-count');
counter.textContent = `${len}/60`;
counter.classList.toggle('text-red-500', len >= 55);
counter.classList.toggle('text-muted', len < 55);
document.getElementById('terms-preview').textContent = input.value ||
'—';
}
// Initialise char count
updateCharCount(document.getElementById('terms-input'));
</script>
</body>
</html>