( ′∀`)σ≡σ☆))Д′)レ(゚∀゚;)ヘ=З=З=Зε≡(ノ´_ゝ`)ノ
<?php
// Connect registration using BrandCreator's join styling and functionality,
// then auto-sign in across Insights, Sites, and I.
require_once __DIR__ . '/auth.php';
// Read optional redirect destination
$redirect = isset($_GET['redirect']) ? trim($_GET['redirect']) : '/connect/index.php';
?>
<!DOCTYPE html>
<html lang="en" class="light-style layout-wide customizer-hide" dir="ltr" data-theme="theme-default" data-assets-path="/brandcreator/dashboard/assets/" data-template="vertical-menu-template" data-style="light">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum=1.0" />
<title>Join | BrandCreator – Build Your Brand Effortlessly</title>
<meta name="description" content="Join BrandCreator to quickly and effortlessly build a stunning, professional brand that stands out." />
<meta name="keywords" content="BrandCreator registration, sign up, brand building, professional branding">
<link rel="canonical" href="https://thebrand.ai/insights">
<!-- Favicon -->
<link rel="icon" type="image/x-icon" href="https://thebrand.ai/favicon/favicon.ico" />
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com/">
<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Public+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap" rel="stylesheet">
<!-- Icons -->
<link rel="stylesheet" href="/brandcreator/dashboard/assets/vendor/fonts/boxicons.css" />
<!-- Core CSS -->
<link rel="stylesheet" href="/brandcreator/dashboard/assets/vendor/css/rtl/core.css" class="template-customizer-core-css" />
<link rel="stylesheet" href="/brandcreator/dashboard/assets/vendor/css/rtl/theme-default.css" class="template-customizer-theme-css" />
<link rel="stylesheet" href="/brandcreator/dashboard/assets/css/demo.css" />
<!-- Page CSS -->
<link rel="stylesheet" href="/brandcreator/dashboard/assets/vendor/css/pages/page-auth.css">
<!-- SweetAlert / animations (for parity) -->
<link rel="stylesheet" href="/brandcreator/dashboard/assets/vendor/libs/animate-css/animate.css" />
<link rel="stylesheet" href="/brandcreator/dashboard/assets/vendor/libs/sweetaler/sweetalert2.css" />
<!-- Helpers & Config -->
<script src="/brandcreator/dashboard/assets/vendor/js/helpers.js"></script>
<script src="/brandcreator/dashboard/assets/vendor/js/template-customizer.js"></script>
<script src="/brandcreator/dashboard/assets/js/config.js"></script>
<style>
.hero-button { box-shadow:none; color:#fff; letter-spacing:-0.02em; background-color:#eb008b; border:0; padding:10px 40px; font-size:20px; border-radius:15px; cursor:pointer; transition:background-color .3s,color .3s,transform .2s ease-out; }
.status{font-size:13px}.ok{color:#0a7f28}.err{color:#b00020}
.hidden{display:none}
#bgX{background-image:url('https://www.thebrand.ai/brandcreator/dashboard/assets/img/illustrations/business-meeting-over-coffee.png?yy');background-size:cover;background-repeat:no-repeat;background-attachment:fixed;background-color:#9fccde;height:100vh;margin:0;background-position:bottom}
swal2-deny{display:none!important;visibility:hidden!important}.swal2-cancel{display:none!important;visibility:hidden!important}
/* Google button styles (match Connect login) */
.gsi-material-button{background-color:#fff;border:1px solid #747775;border-radius:20px;box-sizing:border-box;color:#1f1f1f;cursor:pointer;font-family:'Roboto',arial,sans-serif;font-size:14px;height:40px;letter-spacing:.25px;outline:none;overflow:hidden;padding:0 12px;text-align:center;transition:background-color .218s,border-color .218s,box-shadow .218s;vertical-align:middle;white-space:nowrap;width:auto;max-width:400px}
.gsi-material-button-content{display:flex;align-items:center;justify-content:flex-start;height:100%}
.gsi-material-button-icon{height:20px;width:20px}
.gsi-material-button-contents{flex-grow:1;text-align:center}
</style>
</head>
<body>
<div class="authentication-wrapper authentication-cover">
<a href="/connect/index.php" class="app-brand auth-cover-brand gap-2">
<span class="app-brand-logo demo"><img src="/brandcreator/files/assets/logo-white3.png" style="max-height:60px"></span>
</a>
<div class="authentication-inner row m-0">
<div class="d-none d-lg-flex col-lg-7 col-xl-8 align-items-center p-5" id="bgX"></div>
<div class="d-flex col-12 col-lg-5 col-xl-4 align-items-center authentication-bg p-sm-12 p-6">
<div class="w-px-400 mx-auto mt-12 pt-5">
<h4 class="mb-1">Join Brand AI 👋</h4>
<p class="mb-6">Create your account to access your brand identity, AI-powered insights, design tools, and more — all in one place.</p>
<form id="formAuthentication" class="mb-6" method="POST">
<div class="mb-6">
<label for="username" class="form-label">Username</label>
<input type="text" class="form-control" id="username" name="username" placeholder="Enter your username" autofocus>
</div>
<div class="mb-6">
<label for="first_name" class="form-label">First name</label>
<input type="text" class="form-control" id="first_name" name="first_name" placeholder="Enter your first name">
</div>
<div class="mb-6">
<label for="last_name" class="form-label">Last name</label>
<input type="text" class="form-control" id="last_name" name="last_name" placeholder="Enter your last name">
</div>
<div class="mb-6">
<label for="email" class="form-label">Email</label>
<input type="text" class="form-control" id="email" name="email" placeholder="Enter your email">
</div>
<div class="mb-6 form-password-toggle">
<label class="form-label" for="password">Password</label>
<div class="input-group input-group-merge">
<input type="password" id="password" class="form-control" name="password" placeholder="············" aria-describedby="password" />
<span class="input-group-text cursor-pointer"><i class="bx bx-hide"></i></span>
</div>
</div>
<div class="mb-6 form-password-toggle">
<label class="form-label" for="confirm_password">Confirm password</label>
<div class="input-group input-group-merge">
<input type="password" id="confirm_password" class="form-control" name="confirm_password" placeholder="············" aria-describedby="confirm_password" />
<span class="input-group-text cursor-pointer"><i class="bx bx-hide"></i></span>
</div>
</div>
<div class="mb-6 mt-8">
<div class="form-check mb-8 ms-2">
<input class="form-check-input" type="checkbox" id="terms-conditions" name="terms">
<label class="form-check-label" for="terms-conditions">
I agree to
<a href="https://www.thebrand.ai/policy/privacy.html">privacy policy & terms</a>
</label>
</div>
</div>
<button class="btn btn-primary d-grid w-100 hero-button" type="submit">
Sign up
</button>
</form>
<p class="text-center">
<span>Already have an account?</span>
<a href="/connect/index.php">
<span>Sign in instead</span>
</a>
</p>
<div class="divider my-6">
<div class="divider-text">or</div>
</div>
<p class="text-center">
<button type="button" class="gsi-material-button w-100" onclick="googleSignIn()">
<div class="gsi-material-button-content">
<img class="gsi-material-button-icon" src="https://www.gstatic.com/images/branding/googleg/1x/googleg_standard_color_128dp.png" alt="Google" />
<span class="gsi-material-button-contents">Continue with Google</span>
</div>
</button>
</p>
<div id="progressPanel" class="hidden">
<h4 class="mb-1">Signing you in across apps…</h4>
<div class="list-group mb-4">
<div class="d-flex justify-content-between py-2"><span class="fw-bold">Insights</span><span id="st-insights" class="status">Initializing…</span></div>
<div class="d-flex justify-content-between py-2"><span class="fw-bold">Sites</span><span id="st-sites" class="status">Initializing…</span></div>
<div class="d-flex justify-content-between py-2"><span class="fw-bold">Design Templates</span><span id="st-i" class="status">Initializing…</span></div>
</div>
<button id="continueBtn" class="btn btn-primary w-100" disabled>Continue</button>
</div>
</div>
</div>
</div>
</div>
<script>
(function(){
const redirect = <?php echo json_encode($redirect); ?>;
const progressPanel = document.getElementById('progressPanel');
const joinForm = document.getElementById('formAuthentication');
const statuses = {
insights: document.getElementById('st-insights'),
sites: document.getElementById('st-sites'),
i: document.getElementById('st-i')
};
const results = { insights: false, sites: false, i: false };
function setStatus(id, text, cls){ statuses[id].textContent = text; statuses[id].className = 'status ' + (cls || ''); }
function mark(id, ok, msg){
results[id] = !!ok;
setStatus(id, ok ? 'Signed in' : ('Failed: ' + (msg || 'Error')), ok ? 'ok' : 'err');
const allDone = Object.values(results).every(Boolean);
document.getElementById('continueBtn').disabled = !allDone;
}
function googleSignIn(){
// Match I’s flow: go directly to I’s Google connect endpoint
window.location.href = '/i/connect-with-google';
}
async function issueCookie(email, name){
// Issue Connect SSO cookie for unified session
const resp = await fetch('/connect/issue_sso.php', {
method: 'POST', credentials: 'include',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ email, name })
});
return resp.ok;
}
async function loginInsights(email, password){
try {
setStatus('insights', 'Signing in…');
const resp = await fetch('/insights/data/files/login.php', {
method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, credentials: 'include',
body: new URLSearchParams({ email, password })
});
const ct = resp.headers.get('content-type') || '';
if (ct.includes('application/json')) {
const data = await resp.json();
if (data && data.success) return mark('insights', true);
const msg = (data && data.message) || '';
if (/verify your email/i.test(msg)) {
return mark('insights', false, 'Requires email verification');
}
return mark('insights', false, msg);
}
return mark('insights', resp.ok);
} catch(e){ return mark('insights', false, e.message); }
}
async function loginSites(email, password){
try {
setStatus('sites', 'Signing in…');
const token = getCookie('csrf_cookie_brandsites') || await ensureSitesCsrf();
const resp = await fetch('/sites/index.php/authenticate/verifyLogin', {
method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'X-Requested-With': 'XMLHttpRequest' }, credentials: 'include',
body: new URLSearchParams({ ppl_email: email, ppl_pass: password, csrf_brandsites: token })
});
const ct = resp.headers.get('content-type') || '';
if (ct.includes('application/json')) {
const data = await resp.json();
if (data.status === 'success') {
try { await fetch('/sites/index.php/accounts/splashPage', { credentials: 'include' }); } catch(e) {}
return mark('sites', true);
}
}
// Provision/update Sites user with the same password, then retry
setStatus('sites', 'Provisioning user…');
const prov = await fetch('/connect/provision_sites.php', {
method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ email: email, password: password })
});
const pjson = await prov.json().catch(() => ({ success:false }));
if (pjson && pjson.success) {
setStatus('sites', 'Retrying sign in…');
const token2 = getCookie('csrf_cookie_brandsites') || await ensureSitesCsrf();
const resp2 = await fetch('/sites/index.php/authenticate/verifyLogin', {
method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'X-Requested-With': 'XMLHttpRequest' }, credentials: 'include',
body: new URLSearchParams({ ppl_email: email, ppl_pass: password, csrf_brandsites: token2 })
});
const ct2 = resp2.headers.get('content-type') || '';
if (ct2.includes('application/json')) {
const data2 = await resp2.json();
const ok = data2.status === 'success';
if (ok) { try { await fetch('/sites/index.php/accounts/splashPage', { credentials: 'include' }); } catch(e) {} }
return mark('sites', ok, data2.message);
}
return mark('sites', resp2.ok);
}
return mark('sites', false, (pjson && pjson.message) || 'Provisioning failed');
} catch(e){ return mark('sites', false, e.message); }
}
async function signUpSites(name, email, password){
try {
setStatus('sites', 'Creating account…');
const token = getCookie('csrf_cookie_brandsites') || await ensureSitesCsrf();
const resp = await fetch('/sites/index.php/authenticate/verifySignUp', {
method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'X-Requested-With': 'XMLHttpRequest' }, credentials: 'include',
body: new URLSearchParams({ pps_name: name || (email.split('@')[0]), pps_email: email, pps_password: password, csrf_brandsites: token })
});
const ct = resp.headers.get('content-type') || '';
if (ct.includes('application/json')) {
const data = await resp.json();
if (data && data.status === 'success') return true;
// If the email already exists, treat as non-fatal and proceed to login
const msg = (data && data.message) || '';
if (/already exists/i.test(msg)) return true;
setStatus('sites', 'Signup failed');
return false;
}
// Non-JSON response: treat based on HTTP ok
return resp.ok;
} catch(e){ setStatus('sites', 'Signup error'); return false; }
}
function getCookie(name){
const v = (`; ${document.cookie}`).split(`; ${name}=`);
if (v.length === 2) return v.pop().split(';').shift();
return '';
}
async function ensureSitesCsrf(){
try {
// Hit a Sites page to ensure CSRF cookie is seeded
await fetch('/sites/index.php/accounts/login', { method: 'GET', credentials: 'include' });
return getCookie('csrf_cookie_brandsites');
} catch(e){ return ''; }
}
async function signUpI(name, email, password, firstName, lastName, confirmPassword){
try {
setStatus('i', 'Creating account…');
const resp = await fetch('/i/auth_controller/register_post', {
method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'X-Requested-With': 'XMLHttpRequest' }, credentials: 'include',
body: new URLSearchParams({
username: name || (email.split('@')[0]),
first_name: firstName || '',
last_name: lastName || '',
email: email,
password: password,
confirm_password: confirmPassword || password,
sys_lang_id: '1'
})
});
const ct = resp.headers.get('content-type') || '';
if (ct.includes('application/json')) {
const data = await resp.json();
if (data && (data.result === 1 || data.success)) return true;
setStatus('i', 'Signup failed');
return false;
}
return resp.ok;
} catch(e){ setStatus('i', 'Signup error'); return false; }
}
async function loginBrandCreator(email, password, username){
try {
const resp = await fetch('/brandcreator/dashboard/data/files/login.php', {
method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, credentials: 'include',
body: new URLSearchParams({ email, password })
});
const ct = resp.headers.get('content-type') || '';
if (ct.includes('application/json')) {
const data = await resp.json();
if (data && data.success) return true;
}
// If login failed, provision account then login
const r = await fetch('/brandcreator/dashboard/data/files/register.php', {
method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ username: (username || (email.split('@')[0])), email, password })
});
const txt = await r.text();
if (txt === '2' || txt === '1' || txt === '0') {
await fetch('/brandcreator/dashboard/data/files/login.php', {
method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, credentials: 'include',
body: new URLSearchParams({ email, password })
});
}
return true;
} catch(e){ return false; }
}
async function loginI(email, password){
try {
setStatus('i', 'Signing in…');
const resp = await fetch('/i/auth_controller/login_post', {
method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'X-Requested-With': 'XMLHttpRequest' }, credentials: 'include',
body: new URLSearchParams({ email: email, password: password, sys_lang_id: '1' })
});
const ct = resp.headers.get('content-type') || '';
if (ct.includes('application/json')) {
const data = await resp.json();
if (data && (data.result === 1 || data.success)) return mark('i', true);
return mark('i', false, (data && (data.error || data.message)));
}
return mark('i', resp.ok);
} catch(e){ return mark('i', false, e.message); }
}
function showProgress(){ joinForm.classList.add('hidden'); progressPanel.classList.remove('hidden'); }
document.getElementById('continueBtn').addEventListener('click', function(){ window.location.href = redirect; });
// Submit handler: align with I/register fields and post to I
joinForm.addEventListener('submit', async function(ev){
ev.preventDefault();
const username = document.getElementById('username').value.trim();
const firstName = document.getElementById('first_name').value.trim();
const lastName = document.getElementById('last_name').value.trim();
const email = document.getElementById('email').value.trim();
const password = document.getElementById('password').value;
const confirmPassword = document.getElementById('confirm_password').value;
const agreed = document.getElementById('terms-conditions').checked;
if (!agreed) { alert('Please agree to privacy policy & terms'); return; }
if (!firstName || !lastName) { alert('Please enter your first and last name'); return; }
if (!password || password.length < 4) { alert('Password must be at least 4 characters'); return; }
if (confirmPassword !== password) { alert('Passwords do not match'); return; }
// Register via I and then cascade sign-ins
try {
const okI = await signUpI(username, email, password, firstName, lastName, confirmPassword);
if (!okI) { alert('Registration failed'); return; }
// Ensure BrandCreator session (attempt login, or provision then login)
await loginBrandCreator(email, password, username);
// Issue Connect SSO cookie
await issueCookie(email, username || (email.split('@')[0]));
// Copy details to Sites table (sign up), then start cross-app sign-in
await signUpSites(username, email, password);
showProgress();
await loginInsights(email, password);
await loginI(email, password);
await loginSites(email, password);
// Auto-redirect only if all app sign-ins succeeded
try {
var allOk = Object.values(results).every(Boolean);
if (allOk) {
setTimeout(function(){ window.location.href = redirect; }, 5000);
}
} catch(e) { /* swallow */ }
} catch(e){ alert('Error: ' + e.message); }
});
})();
</script>
</body>
</html>