Browse Source

Adopt OTB portal shell styling for OTB Cloud

master
Don Kingdon 3 weeks ago
parent
commit
89469d6808
  1. 54
      app/static/brand.js
  2. 576
      app/static/css/brand.css
  3. 1276
      app/static/css/style.css
  4. BIN
      app/static/favicon.png
  5. 27
      app/templates/auth/handoff_error.html
  6. 27
      app/templates/auth/login_required.html
  7. 77
      app/templates/cloud/dashboard.html
  8. 2
      app/templates/includes/otb_footer.html
  9. 14
      app/templates/includes/otb_statusbar.html
  10. 42
      app/templates/includes/site_nav.html
  11. 119
      app/templates/portal_base.html

54
app/static/brand.js

@ -0,0 +1,54 @@
(function () {
const root = document.documentElement;
const KEY = "otb-theme";
function applyTheme(theme) {
const resolved = theme === "light" ? "light" : "dark";
root.setAttribute("data-theme", resolved);
document.body.classList.toggle("otb-dark", resolved === "dark");
document.body.classList.toggle("otb-light", resolved === "light");
const toggle = document.getElementById("otbThemeToggle") || document.getElementById("themeToggle");
if (toggle) toggle.checked = resolved === "light";
}
function savedTheme() {
try {
return localStorage.getItem(KEY) || "dark";
} catch (e) {
return "dark";
}
}
function saveTheme(theme) {
try {
localStorage.setItem(KEY, theme);
} catch (e) {}
}
function setupMenu() {
const btn = document.getElementById("otbMenuToggle");
const nav = document.getElementById("otbNavWrap");
if (!btn || !nav) return;
btn.addEventListener("click", function () {
const open = nav.classList.toggle("otb-nav-open");
btn.setAttribute("aria-expanded", open ? "true" : "false");
});
}
document.addEventListener("DOMContentLoaded", function () {
applyTheme(savedTheme());
const toggle = document.getElementById("otbThemeToggle") || document.getElementById("themeToggle");
if (toggle) {
toggle.addEventListener("change", function () {
const next = toggle.checked ? "light" : "dark";
saveTheme(next);
applyTheme(next);
});
}
setupMenu();
});
})();

576
app/static/css/brand.css

@ -0,0 +1,576 @@
/* ===== OTB shared theme system ===== */
:root{
--otb-bg-main:#050b19;
--otb-bg-grad-1:#1f2a57;
--otb-bg-grad-2:#08111f;
--otb-bg-grad-3:#143c2d;
--otb-panel:#0b1330;
--otb-text:#f1f5ff;
--otb-muted:#c5cde4;
--otb-link:#9fd2ff;
--otb-accent:#62f0cf;
--otb-border:rgba(255,255,255,0.08);
--otb-shadow:0 16px 40px rgba(0,0,0,0.28);
}
html[data-theme="light"]{
--otb-bg-main:#eef3fb;
--otb-bg-grad-1:#f7f9fd;
--otb-bg-grad-2:#edf3fb;
--otb-bg-grad-3:#e7f2ec;
--otb-panel:#ffffff;
--otb-text:#172033;
--otb-muted:#55627c;
--otb-link:#0b63ce;
--otb-accent:#0e8f73;
--otb-border:rgba(18,32,60,0.10);
--otb-shadow:0 10px 28px rgba(16,24,40,0.08);
}
html, body{
background:
linear-gradient(90deg, var(--otb-bg-grad-1) 0%, var(--otb-bg-grad-2) 48%, var(--otb-bg-grad-3) 100%),
var(--otb-bg-main) !important;
color:var(--otb-text) !important;
}
a{ color:var(--otb-link); }
.site-header{ background:transparent; }
.site-brand strong,
.site-navlinks > a,
.dropdown-toggle,
.site-title strong,
.site-title span{
color:var(--otb-text) !important;
}
.site-title span,
.small,
.lead,
.sub,
.portal-sub,
.portal-note,
.muted{
color:var(--otb-muted) !important;
}
.card,
.heroCard,
.sideCard,
.feature,
.portal-card,
.detail-card,
.pay-card,
.snapshot-wrap,
.note,
.tableWrap{
background:var(--otb-panel) !important;
border:1px solid var(--otb-border) !important;
color:var(--otb-text) !important;
box-shadow:var(--otb-shadow);
}
.dropdown-menu{
background:var(--otb-panel) !important;
border:1px solid var(--otb-border) !important;
box-shadow:var(--otb-shadow);
}
.dropdown-menu a{
color:var(--otb-text) !important;
}
input,
select,
textarea{
background:rgba(255,255,255,0.06) !important;
color:var(--otb-text) !important;
border:1px solid var(--otb-border) !important;
}
html[data-theme="light"] input,
html[data-theme="light"] select,
html[data-theme="light"] textarea{
background:#f6f8fc !important;
}
::placeholder{
color:var(--otb-muted);
opacity:0.85;
}
.btn,
.portal-btn{
border:1px solid var(--otb-border) !important;
}
.otb-statusbar{
background:#0b1326 !important;
border-top:1px solid rgba(255,255,255,0.06);
}
.otb-statusbar-inner{
max-width:1200px;
margin:0 auto;
padding:10px 18px;
color:#f3f7ff;
display:flex;
justify-content:center;
align-items:center;
gap:10px;
flex-wrap:wrap;
font-size:13px;
line-height:1.4;
}
.otb-statusbar strong{ color:#f8fbff; }
.otb-statusbar a{
color:#62f0cf !important;
text-decoration:none;
}
.otb-dot{
width:4px;
height:4px;
border-radius:50%;
background:rgba(255,255,255,0.25);
display:inline-block;
}
html[data-theme="light"] .otb-statusbar{
background:#edf2fb !important;
border-top:1px solid rgba(18,32,60,0.08);
}
html[data-theme="light"] .otb-statusbar-inner{
color:#23314a;
}
html[data-theme="light"] .otb-statusbar strong{
color:#16233a;
}
html[data-theme="light"] .otb-dot{
background:rgba(23,32,51,0.22);
}
.portal-shell,
.portal-wrap{
max-width:1200px;
margin:0 auto;
padding:32px 20px 72px;
}
.portal-card{
max-width:760px;
margin:0 auto;
}
@media (max-width: 1100px){
.site-container,
.container{
padding-left:16px !important;
padding-right:16px !important;
}
.site-nav{
display:flex;
flex-direction:column;
align-items:flex-start;
gap:14px;
}
.site-nav-right{
width:100%;
display:flex;
flex-direction:column;
align-items:flex-start;
gap:12px;
}
.site-navlinks{
width:100%;
display:flex;
flex-wrap:wrap;
gap:10px 16px;
align-items:center;
}
.dropdown-menu{
min-width:220px;
}
.portal-shell,
.portal-wrap{
padding:24px 16px 64px;
}
}
@media (max-width: 640px){
.site-brand img{
width:48px;
height:48px;
}
.site-title strong{
font-size:20px;
}
.site-title span{
font-size:13px;
}
.site-navlinks{
gap:8px 14px;
}
.otb-statusbar-inner{
font-size:12px;
gap:8px;
padding:10px 12px;
}
.portal-card{
max-width:100%;
}
}
/* ===== OTB hamburger nav ===== */
.otb-menu-toggle{
display:none !important;
margin-left:auto;
width:46px;
height:42px;
border:1px solid var(--otb-border);
background:rgba(255,255,255,0.04);
border-radius:12px;
padding:0;
align-items:center;
justify-content:center;
flex-direction:column;
gap:5px;
cursor:pointer;
}
.otb-menu-toggle span{
display:block;
width:20px;
height:2px;
background:var(--otb-text);
border-radius:2px;
transition:transform 0.2s ease, opacity 0.2s ease;
}
.site-nav-right{
display:flex;
align-items:center;
gap:16px;
}
@media (max-width: 1100px){
.otb-menu-toggle{
display:flex !important;
}
.site-nav{
display:flex;
align-items:center;
justify-content:space-between;
gap:14px;
flex-wrap:wrap;
}
.site-brand{
min-width:0;
flex:1 1 auto;
}
.site-nav-right{
display:none;
width:100%;
flex-direction:column;
align-items:flex-start;
gap:14px;
padding-top:10px;
}
.site-nav-right.otb-nav-open{
display:flex;
}
.site-navlinks{
width:100%;
display:flex;
flex-direction:column;
align-items:flex-start;
gap:10px;
}
.site-navlinks > a,
.dropdown{
width:100%;
}
.dropdown-toggle{
display:inline-flex;
width:100%;
justify-content:flex-start;
}
.dropdown-menu{
position:static !important;
display:none;
min-width:0;
width:100%;
margin-top:8px;
}
.dropdown:hover .dropdown-menu,
.dropdown:focus-within .dropdown-menu{
display:block;
}
}
/* ===== stronger mobile nav override ===== */
@media (max-width: 1100px){
.otb-menu-toggle{
display:flex !important;
}
.site-nav{
display:flex !important;
align-items:center !important;
justify-content:space-between !important;
gap:14px !important;
flex-wrap:wrap !important;
}
.site-nav-right{
display:none !important;
width:100% !important;
flex-direction:column !important;
align-items:flex-start !important;
gap:14px !important;
padding-top:10px !important;
}
.site-nav-right.otb-nav-open{
display:flex !important;
}
.site-navlinks{
width:100% !important;
display:flex !important;
flex-direction:column !important;
align-items:flex-start !important;
gap:10px !important;
}
.site-navlinks > a,
.dropdown{
width:100% !important;
}
.dropdown-toggle{
width:100% !important;
justify-content:flex-start !important;
}
.dropdown-menu{
position:static !important;
min-width:0 !important;
width:100% !important;
margin-top:8px !important;
}
}
/* ===== desktop nav force ===== */
@media (min-width: 1101px){
.otb-menu-toggle{
display:none !important;
}
.site-nav-right{
display:flex !important;
width:auto !important;
flex-direction:row !important;
align-items:center !important;
gap:16px !important;
padding-top:0 !important;
}
.site-navlinks{
display:flex !important;
flex-direction:row !important;
align-items:center !important;
width:auto !important;
gap:16px !important;
}
}
/* ===== OTB hamburger hard fix ===== */
.otb-menu-toggle{
display:none !important;
appearance:none !important;
-webkit-appearance:none !important;
background:transparent !important;
border:0 !important;
box-shadow:none !important;
outline:none !important;
padding:0 !important;
margin:0 0 0 auto !important;
width:46px !important;
height:42px !important;
align-items:center !important;
justify-content:center !important;
flex-direction:column !important;
gap:5px !important;
cursor:pointer !important;
}
.otb-menu-toggle::before,
.otb-menu-toggle::after{
content:none !important;
display:none !important;
}
.otb-menu-toggle span{
display:block !important;
width:20px !important;
height:2px !important;
min-height:2px !important;
max-height:2px !important;
background:var(--otb-text) !important;
border-radius:2px !important;
margin:0 !important;
padding:0 !important;
}
@media (min-width: 1101px){
.otb-menu-toggle{
display:none !important;
visibility:hidden !important;
pointer-events:none !important;
width:0 !important;
height:0 !important;
overflow:hidden !important;
}
.site-nav-right{
display:flex !important;
width:auto !important;
flex-direction:row !important;
align-items:center !important;
gap:16px !important;
padding-top:0 !important;
}
.site-navlinks{
display:flex !important;
flex-direction:row !important;
align-items:center !important;
width:auto !important;
gap:16px !important;
}
}
@media (max-width: 1100px){
.otb-menu-toggle{
display:flex !important;
visibility:visible !important;
pointer-events:auto !important;
width:46px !important;
height:42px !important;
overflow:visible !important;
border:1px solid var(--otb-border) !important;
background:rgba(255,255,255,0.04) !important;
border-radius:12px !important;
}
.site-nav{
display:flex !important;
align-items:center !important;
justify-content:space-between !important;
gap:14px !important;
flex-wrap:wrap !important;
}
.site-brand{
min-width:0 !important;
flex:1 1 auto !important;
}
.site-nav-right{
display:none !important;
width:100% !important;
flex-direction:column !important;
align-items:flex-start !important;
gap:14px !important;
padding-top:10px !important;
}
.site-nav-right.otb-nav-open{
display:flex !important;
}
.site-navlinks{
width:100% !important;
display:flex !important;
flex-direction:column !important;
align-items:flex-start !important;
gap:10px !important;
}
.site-navlinks > a,
.dropdown{
width:100% !important;
}
.dropdown-toggle{
width:100% !important;
justify-content:flex-start !important;
}
.dropdown-menu{
position:static !important;
min-width:0 !important;
width:100% !important;
margin-top:8px !important;
}
}
/* light theme button consistency fix */
body.light-theme .portal-btn,
body.light-theme a.portal-btn,
body.light-theme button.portal-btn {
background: rgba(255,255,255,0.75);
color: var(--text, #10233f);
border: 1px solid rgba(40,70,120,0.14);
opacity: 1;
}
body.light-theme .portal-btn.primary,
body.light-theme a.portal-btn.primary,
body.light-theme button.portal-btn.primary {
color: #081528;
}
body.light-theme .portal-toolbar .portal-btn,
body.light-theme .portal-actions .portal-btn {
opacity: 1;
}
body.light-theme .portal-btn[disabled],
body.light-theme button.portal-btn[disabled],
body.light-theme .portal-btn.disabled {
opacity: 0.55;
}

1276
app/static/css/style.css

File diff suppressed because it is too large Load Diff

BIN
app/static/favicon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

27
app/templates/auth/handoff_error.html

@ -1,13 +1,26 @@
{% extends "portal_base.html" %} {% extends "portal_base.html" %}
{% block title %}Portal Handoff Error{% endblock %} {% block title %}Portal Handoff Error - OTB Cloud{% endblock %}
{% block content %} {% block portal_content %}
<div class="card"> <div class="portal-page-header">
<h1>Portal handoff failed</h1> <div>
<p class="muted">{{ message }}</p> <h1 class="portal-page-title">Portal handoff failed</h1>
<div class="warn" style="margin-top:16px;"> <p class="portal-page-subtitle">
Please return to the OTB Billing portal and try launching OTB Cloud again. OTB Cloud could not validate the portal launch request.
</p>
</div> </div>
</div> </div>
<section class="portal-card-grid">
<article class="portal-card" style="max-width:900px;">
<div class="portal-card-body">
<p style="margin-top:0;">{{ message }}</p>
<div class="portal-inline-actions">
<a class="portal-btn primary" href="https://otb-billing.outsidethebox.top/portal/services">Return to Services</a>
<a class="portal-btn" href="https://otb-billing.outsidethebox.top/portal">Portal Home</a>
</div>
</div>
</article>
</section>
{% endblock %} {% endblock %}

27
app/templates/auth/login_required.html

@ -1,15 +1,28 @@
{% extends "portal_base.html" %} {% extends "portal_base.html" %}
{% block title %}Portal Login Required{% endblock %} {% block title %}Portal Login Required - OTB Cloud{% endblock %}
{% block content %} {% block portal_content %}
<div class="card"> <div class="portal-page-header">
<h1>Portal login required</h1> <div>
<p class="muted"> <h1 class="portal-page-title">Portal login required</h1>
<p class="portal-page-subtitle">
OTB Cloud is available only through a signed handoff from the OTB Billing portal. OTB Cloud is available only through a signed handoff from the OTB Billing portal.
</p> </p>
<div class="warn" style="margin-top:16px;">
Please return to the OTB Billing portal and open OTB Cloud from your Services page.
</div> </div>
</div> </div>
<section class="portal-card-grid">
<article class="portal-card" style="max-width:900px;">
<div class="portal-card-body">
<p style="margin-top:0;">
Please return to the OTB Billing portal and launch OTB Cloud from your Services page.
</p>
<div class="portal-inline-actions">
<a class="portal-btn primary" href="https://otb-billing.outsidethebox.top/portal/services">Go to Services</a>
<a class="portal-btn" href="https://otb-billing.outsidethebox.top/portal">Go to Portal</a>
</div>
</div>
</article>
</section>
{% endblock %} {% endblock %}

77
app/templates/cloud/dashboard.html

@ -2,37 +2,76 @@
{% block title %}OTB Cloud Dashboard{% endblock %} {% block title %}OTB Cloud Dashboard{% endblock %}
{% block content %} {% block portal_content %}
<div class="card" style="margin-bottom:16px;"> <div class="portal-page-header">
<h1 style="margin-top:0;">OTB Cloud Dashboard</h1> <div>
<p class="muted" style="margin-bottom:10px;">Authenticated user: {{ user_email }}</p> <h1 class="portal-page-title">OTB Cloud Dashboard</h1>
<p class="muted" style="margin:0;">Tenant slug: <span class="badge">{{ tenant_slug }}</span></p> <p class="portal-client-name">{{ user_email }}</p>
<p class="portal-page-subtitle">
Secure backup and storage dashboard for your account.
</p>
</div>
<div class="portal-toolbar" style="display:flex;flex-direction:column;align-items:flex-end;gap:10px;">
<div style="display:flex;gap:10px;justify-content:flex-end;flex-wrap:wrap;">
<a class="portal-btn primary" href="https://otb-billing.outsidethebox.top/portal/services">Back to Services</a>
<a class="portal-btn" href="/auth/logout">Logout</a>
</div>
<div style="text-align:right;font-size:14px;opacity:0.95;">
<div>Logged in as:
<strong>{{ user_email }}</strong></div>
<div>Tenant slug:
<strong>{{ tenant_slug }}</strong></div>
</div>
</div>
</div> </div>
<div class="grid"> <section class="services-grid">
<div class="card"> <article class="service-card status-beta">
<h2 style="margin-top:0;">Devices</h2> <div class="service-card-header">
<div>
<h2>Devices</h2>
<p>Registered source locations for uploaded data.</p>
</div>
<div>
<span class="service-badge service-badge-beta">Active</span>
</div>
</div>
<div class="service-card-actions">
{% if devices %} {% if devices %}
<ul style="padding-left:18px; margin-bottom:0;"> <ul style="padding-left:18px; margin:0;">
{% for device in devices %} {% for device in devices %}
<li style="margin-bottom:8px;"> <li style="margin-bottom:10px;">
<strong>{{ device.device_name }}</strong> <strong>{{ device.device_name }}</strong>
<span class="muted">({{ device.device_type }})</span><br> <span style="opacity:0.85;">({{ device.device_type }})</span><br>
<span class="muted">{{ device.relative_path }}</span> <span style="opacity:0.75;">{{ device.relative_path }}</span>
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
{% else %} {% else %}
<p class="muted">No devices have been created yet.</p> <p>No devices have been created yet.</p>
{% endif %} {% endif %}
</div> </div>
</article>
<article class="service-card status-beta">
<div class="service-card-header">
<div>
<h2>Current scope</h2>
<p>OTB Cloud is now operating inside the branded OTB portal shell.</p>
</div>
<div>
<span class="service-badge service-badge-beta">In Progress</span>
</div>
</div>
<div class="card"> <div class="service-card-actions">
<h2 style="margin-top:0;">Current scope</h2> <p style="margin:0;">
<p class="muted"> Next steps are the searchable file library, bulk upload endpoints, zip export, and media processing jobs.
OTB Cloud is now running as a portal-linked secure storage service.
Next steps are real OTB Billing handoff integration, file library pages, and upload endpoints.
</p> </p>
</div> </div>
</div> </article>
</section>
{% endblock %} {% endblock %}

2
app/templates/includes/otb_footer.html

@ -0,0 +1,2 @@
{% include "includes/otb_statusbar.html" %}
<script src="/static/brand.js" defer></script>

14
app/templates/includes/otb_statusbar.html

@ -0,0 +1,14 @@
<div class="otb-statusbar">
<div class="otb-statusbar-inner">
<strong>All billing is calculated in 🇨🇦 CAD</strong>
<span class="otb-dot"></span>
<span>Crypto conversions use the <a href="https://monitor.outsidethebox.top" target="_blank" rel="noopener noreferrer">OTB Oracle</a></span>
<span class="otb-dot"></span>
<span>
Methods:
<strong>Credit Card</strong> <span style="opacity:0.7;">(via Square)</span>,
<strong>e-Transfer</strong>,
and <strong>enabled crypto assets</strong>
</span>
</div>
</div>

42
app/templates/includes/site_nav.html

@ -0,0 +1,42 @@
<header class="site-header">
<div class="site-container">
<div class="site-nav">
<a class="site-brand" href="https://outsidethebox.top">
<img src="https://outsidethebox.top/assets/favicon.png" alt="outsidethebox.top logo" />
<div class="site-title">
<strong>outsidethebox.top</strong>
<span>Managed hosting • no client server logins</span>
</div>
</a>
<button class="otb-menu-toggle" id="otbMenuToggle" aria-label="Toggle menu" aria-expanded="false" aria-controls="otbNavWrap">
<span></span>
<span></span>
<span></span>
</button>
<div class="site-nav-right" id="otbNavWrap">
<nav class="site-navlinks">
<a href="https://outsidethebox.top">Home</a>
<a href="https://outsidethebox.top/pricing.html">Pricing</a>
<a href="https://outsidethebox.top/terms.html">ToS</a>
<a href="https://outsidethebox.top/contact.html">Contact</a>
<div class="dropdown">
<a href="#" class="dropdown-toggle">Services</a>
<div class="dropdown-menu">
<a href="https://follow-me.outsidethebox.top">Follow-me Tracker</a>
<a href="https://monitor.outsidethebox.top">Oracle</a>
<a href="https://video.outsidethebox.top">Video</a>
</div>
</div>
<a href="https://otb-billing.outsidethebox.top/portal">Portal</a>
</nav>
<label class="otb-theme-switch" title="Toggle light / dark mode">
<input type="checkbox" id="otbThemeToggle" aria-label="Toggle theme" />
<span class="otb-theme-slider"></span>
</label>
</div>
</div>
</div>
</header>

119
app/templates/portal_base.html

@ -1,114 +1,25 @@
<!doctype html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}OTB Cloud{% endblock %}</title> <title>{% block title %}Portal - OutsideTheBox{% endblock %}</title>
<style> <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
:root { <link rel="stylesheet" href="{{ url_for('static', filename='css/brand.css') }}">
--bg: #0b1220; <link rel="icon" type="image/png" href="{{ url_for('static', filename='favicon.png') }}">
--panel: #121b2d; {% block head_extra %}{% endblock %}
--panel-2: #16233b;
--text: #e9eef8;
--muted: #9fb0cf;
--line: rgba(255,255,255,0.10);
--accent: #4da3ff;
--danger: #ff6b6b;
--ok: #4fd18b;
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: Arial, sans-serif;
background: linear-gradient(180deg, #08101d 0%, #0b1220 100%);
color: var(--text);
}
a { color: var(--accent); text-decoration: none; }
.wrap { max-width: 1200px; margin: 0 auto; padding: 20px; }
.topbar {
border-bottom: 1px solid var(--line);
background: rgba(255,255,255,0.03);
}
.nav {
display: flex;
gap: 18px;
align-items: center;
justify-content: space-between;
padding: 14px 20px;
max-width: 1200px;
margin: 0 auto;
}
.brand {
font-weight: bold;
font-size: 20px;
letter-spacing: 0.3px;
}
.nav-links {
display: flex;
gap: 14px;
flex-wrap: wrap;
align-items: center;
}
.card {
background: var(--panel);
border: 1px solid var(--line);
border-radius: 18px;
padding: 20px;
box-shadow: 0 10px 24px rgba(0,0,0,0.18);
}
.grid {
display: grid;
gap: 16px;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
.muted { color: var(--muted); }
.badge {
display: inline-block;
padding: 6px 10px;
border-radius: 999px;
background: rgba(77,163,255,0.12);
border: 1px solid rgba(77,163,255,0.22);
color: var(--text);
font-size: 12px;
}
.footer {
border-top: 1px solid var(--line);
margin-top: 28px;
color: var(--muted);
}
.footer .wrap {
padding-top: 16px;
padding-bottom: 24px;
}
.warn {
border-left: 4px solid var(--danger);
background: rgba(255,107,107,0.08);
padding: 14px 16px;
border-radius: 12px;
}
</style>
</head> </head>
<body> <body>
<div class="topbar"> {% include "includes/site_nav.html" %}
<div class="nav">
<div class="brand">OTB Cloud</div> <div class="portal-shell">
<div class="nav-links"> <div class="portal-wrap">
{% if session.get("otb_user_id") %} {% block portal_content %}{% endblock %}
<a href="/dashboard">Dashboard</a>
<a href="/auth/logout">Logout</a>
{% endif %}
</div>
</div> </div>
</div> </div>
<div class="wrap"> {% block scripts %}{% endblock %}
{% block content %}{% endblock %}
</div>
<div class="footer"> {% include "includes/otb_footer.html" %}
<div class="wrap">
OTB Cloud is a portal-linked secure backup and storage service for Outsidethebox.top.
</div>
</div>
</body> </body>
</html> </html>

Loading…
Cancel
Save