Files
MySMSAPio/app/views/layouts/public.html.erb
Min Zeya Phyo a5ade4006d
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
welcome page and documentatino
2026-08-01 00:27:37 +08:00

274 lines
11 KiB
Plaintext

<!DOCTYPE html>
<html lang="en" class="h-full">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title><%= content_for(:title) || "MySMSAPio — SMS Gateway API" %></title>
<meta name="description" content="A Rails 8 SMS Gateway Backend API for managing SMS messaging through Android gateway devices. Send SMS, manage OTPs, and communicate with gateway devices in real-time.">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %>
<link rel="icon" href="/icon.png" type="image/png">
<link rel="icon" href="/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/icon.png">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<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=Syne:wght@600;700;800&family=Outfit:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
:root {
--accent: #38bdf8;
--accent-deep: #0ea5e9;
--accent-glow: rgba(56, 189, 248, 0.45);
}
.font-display { font-family: 'Syne', sans-serif; }
.font-body { font-family: 'Outfit', sans-serif; }
.font-mono-tech { font-family: 'JetBrains Mono', ui-monospace, monospace; }
body { font-family: 'Outfit', sans-serif; }
/* Immersive dark stage */
.stage {
position: relative;
background: radial-gradient(ellipse 90% 70% at 50% -10%, #0b1220 0%, #050912 55%, #000 100%);
overflow: hidden;
}
.stage::before {
content: "";
position: absolute; inset: 0;
background-image:
linear-gradient(rgba(56, 189, 248, 0.06) 1px, transparent 1px),
linear-gradient(90deg, rgba(56, 189, 248, 0.06) 1px, transparent 1px);
background-size: 48px 48px;
-webkit-mask-image: radial-gradient(ellipse at top, black 10%, transparent 70%);
mask-image: radial-gradient(ellipse at top, black 10%, transparent 70%);
pointer-events: none;
}
.stage::after {
content: "";
position: absolute; inset: 0;
background:
radial-gradient(circle at 12% 14%, rgba(56, 189, 248, 0.13), transparent 42%),
radial-gradient(circle at 88% 8%, rgba(14, 165, 233, 0.10), transparent 45%),
radial-gradient(circle at 70% 90%, rgba(56, 189, 248, 0.07), transparent 40%);
pointer-events: none;
}
.glass {
background: rgba(255, 255, 255, 0.035);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.08);
}
.glass-strong {
background: rgba(15, 23, 42, 0.55);
backdrop-filter: blur(24px);
-webkit-backdrop-filter: blur(24px);
border: 1px solid rgba(148, 163, 184, 0.14);
}
.beacon-ring { animation: beacon-pulse 2.6s ease-out infinite; }
.beacon-ring.delay-1 { animation-delay: 0.9s; }
.beacon-ring.delay-2 { animation-delay: 1.8s; }
@keyframes beacon-pulse {
0% { transform: scale(0.6); opacity: 0.55; }
80% { opacity: 0; }
100% { transform: scale(1.9); opacity: 0; }
}
.btn-accent {
background: linear-gradient(135deg, #38bdf8 0%, #0ea5e9 100%);
box-shadow: 0 10px 30px -10px var(--accent-glow), inset 0 1px 0 rgba(255,255,255,0.25);
transition: transform .15s ease, box-shadow .2s ease, filter .2s ease;
}
.btn-accent:hover { transform: translateY(-1px); filter: brightness(1.08); box-shadow: 0 16px 40px -10px var(--accent-glow), inset 0 1px 0 rgba(255,255,255,0.3); }
.btn-accent:active { transform: translateY(0); }
.btn-ghost {
background: rgba(255,255,255,0.04);
border: 1px solid rgba(148, 163, 184, 0.22);
transition: border-color .18s ease, background .18s ease, transform .15s ease;
}
.btn-ghost:hover { border-color: var(--accent); background: rgba(56, 189, 248, 0.10); transform: translateY(-1px); }
.nav-link {
position: relative;
transition: color .18s ease;
}
.nav-link::after {
content: "";
position: absolute; left: 0; bottom: -4px;
height: 2px; width: 0;
background: var(--accent);
transition: width .22s ease;
}
.nav-link:hover::after, .nav-link.is-active::after { width: 100%; }
.nav-link.is-active { color: #fff; }
@keyframes fade-up {
from { opacity: 0; transform: translateY(14px); }
to { opacity: 1; transform: translateY(0); }
}
.fade-up { animation: fade-up .6s cubic-bezier(0.16, 1, 0.3, 1) both; }
.fade-up.d-1 { animation-delay: .08s; }
.fade-up.d-2 { animation-delay: .16s; }
.fade-up.d-3 { animation-delay: .24s; }
.fade-up.d-4 { animation-delay: .32s; }
.fade-up.d-5 { animation-delay: .40s; }
/* Code blocks */
.code-block {
background: rgba(2, 6, 23, 0.7);
border: 1px solid rgba(148, 163, 184, 0.16);
font-family: 'JetBrains Mono', ui-monospace, monospace;
}
.code-block .tok-key { color: #7dd3fc; }
.code-block .tok-str { color: #86efac; }
.code-block .tok-num { color: #fcd34d; }
.code-block .tok-com { color: #64748b; font-style: italic; }
.code-block .tok-punc { color: #94a3b8; }
.code-block .tok-bool { color: #f0abfc; }
.copy-btn {
transition: color .15s ease, background .15s ease;
}
.copy-btn.copied { color: #4ade80; }
/* Docs sidebar */
.docs-sidebar a {
transition: color .15s ease, background .15s ease;
}
.docs-sidebar a:hover { color: #e2e8f0; }
.docs-sidebar a.is-active {
color: var(--accent);
background: rgba(56, 189, 248, 0.08);
border-left-color: var(--accent);
}
/* Method badges */
.badge-get { background: rgba(34, 197, 94, 0.14); color: #4ade80; border-color: rgba(74, 222, 128, 0.3); }
.badge-post { background: rgba(56, 189, 248, 0.14); color: #38bdf8; border-color: rgba(56, 189, 248, 0.3); }
.badge-delete { background: rgba(244, 63, 94, 0.14); color: #fb7185; border-color: rgba(251, 113, 133, 0.3); }
.badge-ws { background: rgba(168, 85, 247, 0.14); color: #c084fc; border-color: rgba(192, 132, 252, 0.3); }
.gradient-text {
background: linear-gradient(120deg, #e2e8f0 0%, #38bdf8 60%, #0ea5e9 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
.scroll-fade {
mask-image: linear-gradient(to bottom, black 80%, transparent 100%);
}
html { scroll-behavior: smooth; scroll-padding-top: 90px; }
@media (prefers-reduced-motion: reduce) {
.beacon-ring, .fade-up { animation: none; }
html { scroll-behavior: auto; }
}
</style>
</head>
<body class="stage min-h-full text-slate-200">
<!-- Top navigation -->
<header class="sticky top-0 z-40 glass-strong border-b border-white/5">
<nav class="mx-auto flex max-w-7xl items-center justify-between px-5 py-4 sm:px-8">
<%= link_to root_path, class: "flex items-center gap-3" do %>
<div class="flex h-10 w-10 items-center justify-center rounded-xl bg-gradient-to-br from-sky-400 to-sky-600 shadow-lg shadow-sky-500/30">
<i class="fas fa-sms text-lg text-slate-950"></i>
</div>
<div class="leading-none">
<span class="font-display text-lg font-extrabold tracking-tight text-white">MySMSAPio</span>
<span class="font-mono-tech ml-2 text-[10px] uppercase tracking-[0.3em] text-sky-400/80">v1</span>
</div>
<% end %>
<div class="hidden items-center gap-8 sm:flex">
<%= link_to "Home", root_path, class: "nav-link text-sm font-medium text-slate-300 #{current_page?(root_path) ? 'is-active' : ''}" %>
<%= link_to "API Docs", docs_path, class: "nav-link text-sm font-medium text-slate-300 #{current_page?(docs_path) ? 'is-active' : ''}" %>
<%= link_to "Admin Console", admin_login_path, class: "nav-link text-sm font-medium text-slate-300" %>
</div>
<div class="flex items-center gap-3">
<%= link_to docs_path, class: "btn-accent hidden items-center gap-2 rounded-lg px-4 py-2 text-sm font-semibold text-slate-950 sm:inline-flex" do %>
<i class="fas fa-book"></i> Read the Docs
<% end %>
<a href="https://github.com" target="_blank" rel="noopener" class="btn-ghost inline-flex h-9 w-9 items-center justify-center rounded-lg text-slate-300" aria-label="GitHub">
<i class="fab fa-github"></i>
</a>
</div>
</nav>
</header>
<main class="relative">
<%= yield %>
</main>
<!-- Footer -->
<footer class="relative border-t border-white/5 bg-black/30">
<div class="mx-auto max-w-7xl px-5 py-12 sm:px-8">
<div class="flex flex-col items-center justify-between gap-6 sm:flex-row">
<div class="flex items-center gap-3">
<div class="flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-sky-400 to-sky-600">
<i class="fas fa-sms text-sm text-slate-950"></i>
</div>
<div>
<p class="font-display text-sm font-bold text-white">MySMSAPio</p>
<p class="font-mono-tech text-[10px] uppercase tracking-[0.2em] text-slate-500">SMS Gateway API</p>
</div>
</div>
<div class="flex flex-wrap items-center justify-center gap-x-6 gap-y-2 text-sm text-slate-400">
<%= link_to "Home", root_path, class: "hover:text-sky-400 transition-colors" %>
<%= link_to "API Docs", docs_path, class: "hover:text-sky-400 transition-colors" %>
<%= link_to "Admin", admin_login_path, class: "hover:text-sky-400 transition-colors" %>
<span class="text-slate-600">·</span>
<span class="font-mono-tech text-xs">Rails 8 · Ruby 3.4</span>
</div>
</div>
<p class="mt-8 text-center text-xs text-slate-600">© <%= Time.current.year %> MySMSAPio. Built with Rails 8, Sidekiq, Action Cable & Redis.</p>
</div>
</footer>
<script>
// Copy-to-clipboard for any element with [data-copy]
document.addEventListener("click", (event) => {
const btn = event.target.closest("[data-copy]");
if (!btn) return;
const text = btn.getAttribute("data-copy");
navigator.clipboard.writeText(text).then(() => {
const original = btn.innerHTML;
btn.classList.add("copied");
btn.innerHTML = '<i class="fas fa-check"></i>';
setTimeout(() => { btn.classList.remove("copied"); btn.innerHTML = original; }, 1400);
});
});
// Docs sidebar active link on scroll
(function () {
const links = document.querySelectorAll(".docs-sidebar a[data-target]");
if (!links.length) return;
const sections = Array.from(links).map(l => document.getElementById(l.dataset.target)).filter(Boolean);
function setActive() {
const offset = 120;
let current = sections[0]?.id;
for (const s of sections) {
if (s.getBoundingClientRect().top - offset <= 0) current = s.id;
}
links.forEach(l => l.classList.toggle("is-active", l.dataset.target === current));
}
window.addEventListener("scroll", setActive, { passive: true });
setActive();
})();
</script>
</body>
</html>