262 lines
13 KiB
TypeScript
262 lines
13 KiB
TypeScript
/**
|
|
* rSchedule landing page — persistent job scheduling for rSpace.
|
|
*/
|
|
export function renderLanding(): string {
|
|
return `
|
|
<!-- Hero -->
|
|
<div class="rl-hero">
|
|
<span class="rl-tagline" style="color:#f59e0b;background:rgba(245,158,11,0.1);border-color:rgba(245,158,11,0.2)">
|
|
Persistent Scheduling
|
|
</span>
|
|
<h1 class="rl-heading" style="background:linear-gradient(to right,#f59e0b,#f97316,#ef4444);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text">
|
|
Automate (you)rSpace,<br>on (you)rSchedule.
|
|
</h1>
|
|
<p class="rl-subtitle">
|
|
Cron-powered job scheduling with email, webhooks, calendar events, and backlog briefings — all managed from within rSpace.
|
|
</p>
|
|
<p class="rl-subtext">
|
|
rSchedule replaces system-level crontabs with an <span style="color:#f59e0b;font-weight:600">in-process, persistent scheduler</span>.
|
|
Jobs survive restarts, fire on a 60-second tick loop, and are fully configurable through the UI.
|
|
</p>
|
|
<div class="rl-cta-row">
|
|
<a href="#" class="rl-cta-primary" id="ml-primary"
|
|
style="background:linear-gradient(to right,#f59e0b,#f97316);color:#0b1120"
|
|
onclick="document.querySelector('.rl-hero').closest('[data-space]')?.getAttribute('data-space') ? window.location.href='/' + document.querySelector('.rl-hero').closest('[data-space]').getAttribute('data-space') + '/rschedule' : void 0; return false;">
|
|
Open Scheduler
|
|
</a>
|
|
<a href="#" class="rl-cta-primary" id="ml-automations"
|
|
style="background:linear-gradient(to right,#8b5cf6,#6366f1);color:#fff"
|
|
onclick="document.querySelector('.rl-hero').closest('[data-space]')?.getAttribute('data-space') ? window.location.href='/' + document.querySelector('.rl-hero').closest('[data-space]').getAttribute('data-space') + '/rschedule/reminders' : window.location.href='/demo/rschedule/reminders'; return false;">
|
|
Automation Canvas
|
|
</a>
|
|
<a href="#features" class="rl-cta-secondary">Learn More</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Features (4-card grid) -->
|
|
<section class="rl-section" id="features" style="border-top:none">
|
|
<div class="rl-container">
|
|
<div class="rl-grid-4">
|
|
<div class="rl-card rl-card--center" style="padding:2rem">
|
|
<div class="rl-icon-box" style="background:rgba(245,158,11,0.12);font-size:1.5rem">
|
|
<span style="font-size:1.5rem">⏰</span>
|
|
</div>
|
|
<h3>Cron Expressions</h3>
|
|
<p>Standard cron syntax with timezone support. Schedule anything from every minute to once a year.</p>
|
|
</div>
|
|
<div class="rl-card rl-card--center" style="padding:2rem">
|
|
<div class="rl-icon-box" style="background:rgba(249,115,22,0.12);font-size:1.5rem">
|
|
<span style="font-size:1.5rem">📧</span>
|
|
</div>
|
|
<h3>Email Actions</h3>
|
|
<p>Send scheduled emails via SMTP — morning briefings, weekly digests, monthly audits.</p>
|
|
</div>
|
|
<div class="rl-card rl-card--center" style="padding:2rem">
|
|
<div class="rl-icon-box" style="background:rgba(239,68,68,0.12);font-size:1.5rem">
|
|
<span style="font-size:1.5rem">🔗</span>
|
|
</div>
|
|
<h3>Webhook Actions</h3>
|
|
<p>Fire HTTP requests on schedule — trigger builds, sync data, or ping external services.</p>
|
|
</div>
|
|
<div class="rl-card rl-card--center" style="padding:2rem">
|
|
<div class="rl-icon-box" style="background:rgba(52,211,153,0.12);font-size:1.5rem">
|
|
<span style="font-size:1.5rem">📋</span>
|
|
</div>
|
|
<h3>Backlog Briefings</h3>
|
|
<p>Automated task digests from your Backlog — morning, weekly, and monthly summaries delivered by email.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Your Automations -->
|
|
<section class="rl-section" id="automations">
|
|
<div class="rl-container">
|
|
<h2 style="text-align:center;font-size:1.5rem;margin-bottom:0.5rem;color:#e2e8f0">Your Automations</h2>
|
|
<p style="text-align:center;color:rgba(148,163,184,0.7);margin-bottom:2rem;font-size:0.9rem">
|
|
Visual workflows built on the automation canvas
|
|
</p>
|
|
<div id="rs-automations-list" style="min-height:120px">
|
|
<p style="text-align:center;color:rgba(148,163,184,0.5);padding:2rem 0">Loading automations…</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<script>
|
|
(function() {
|
|
var space = 'demo';
|
|
var el = document.querySelector('.rl-hero');
|
|
if (el) {
|
|
var ds = el.closest('[data-space]');
|
|
if (ds) space = ds.getAttribute('data-space') || 'demo';
|
|
}
|
|
var basePath = '/' + space + '/rschedule/';
|
|
var container = document.getElementById('rs-automations-list');
|
|
|
|
fetch(basePath + 'api/workflows')
|
|
.then(function(r) { return r.ok ? r.json() : { results: [] }; })
|
|
.then(function(data) {
|
|
var wfs = data.results || [];
|
|
if (wfs.length === 0) {
|
|
container.innerHTML =
|
|
'<div style="text-align:center;padding:2.5rem 1rem">' +
|
|
'<p style="color:rgba(148,163,184,0.6);margin-bottom:1.5rem">No automations yet.</p>' +
|
|
'<a href="' + basePath + 'reminders" ' +
|
|
'style="display:inline-block;padding:0.6rem 1.5rem;border-radius:8px;' +
|
|
'background:linear-gradient(to right,#8b5cf6,#6366f1);color:#fff;text-decoration:none;font-weight:600;font-size:0.9rem">' +
|
|
'+ Create your first automation</a>' +
|
|
'</div>';
|
|
return;
|
|
}
|
|
|
|
var viewMode = wfs.length > 6 ? 'list' : 'grid';
|
|
var html = '';
|
|
|
|
if (viewMode === 'grid') {
|
|
html += '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:1rem">';
|
|
for (var i = 0; i < wfs.length; i++) {
|
|
var w = wfs[i];
|
|
var nodeCount = (w.nodes || []).length;
|
|
var edgeCount = (w.edges || []).length;
|
|
var statusColor = w.enabled ? '#34d399' : '#64748b';
|
|
var statusLabel = w.enabled ? 'Active' : 'Disabled';
|
|
var lastRun = w.lastRunAt ? new Date(w.lastRunAt).toLocaleDateString() : 'Never';
|
|
var runStatusIcon = w.lastRunStatus === 'success' ? '✓' : w.lastRunStatus === 'error' ? '✗' : '—';
|
|
var runStatusColor = w.lastRunStatus === 'success' ? '#34d399' : w.lastRunStatus === 'error' ? '#ef4444' : '#64748b';
|
|
|
|
// Build a mini node-count summary
|
|
var triggers = 0, conditions = 0, actions = 0;
|
|
for (var n = 0; n < (w.nodes || []).length; n++) {
|
|
var t = (w.nodes[n].type || '');
|
|
if (t.indexOf('trigger') === 0) triggers++;
|
|
else if (t.indexOf('condition') === 0) conditions++;
|
|
else if (t.indexOf('action') === 0) actions++;
|
|
}
|
|
|
|
html += '<a href="' + basePath + 'reminders?wf=' + w.id + '" ' +
|
|
'style="display:block;text-decoration:none;border-radius:12px;' +
|
|
'background:rgba(30,41,59,0.6);border:1px solid rgba(148,163,184,0.12);' +
|
|
'padding:1.25rem;transition:border-color 0.2s,transform 0.15s;cursor:pointer" ' +
|
|
'onmouseover="this.style.borderColor=\'rgba(139,92,246,0.4)\';this.style.transform=\'translateY(-2px)\'" ' +
|
|
'onmouseout="this.style.borderColor=\'rgba(148,163,184,0.12)\';this.style.transform=\'none\'">' +
|
|
|
|
// Header row
|
|
'<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:0.75rem">' +
|
|
'<span style="font-weight:600;color:#e2e8f0;font-size:0.95rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + (w.name || 'Untitled') + '</span>' +
|
|
'<span style="font-size:0.7rem;padding:2px 8px;border-radius:9999px;background:' + statusColor + '20;color:' + statusColor + ';font-weight:500;white-space:nowrap">' + statusLabel + '</span>' +
|
|
'</div>' +
|
|
|
|
// Node summary
|
|
'<div style="display:flex;gap:0.75rem;margin-bottom:0.6rem;font-size:0.8rem;color:rgba(148,163,184,0.7)">' +
|
|
(triggers ? '<span style="color:#60a5fa">' + triggers + ' trigger' + (triggers > 1 ? 's' : '') + '</span>' : '') +
|
|
(conditions ? '<span style="color:#fbbf24">' + conditions + ' condition' + (conditions > 1 ? 's' : '') + '</span>' : '') +
|
|
(actions ? '<span style="color:#34d399">' + actions + ' action' + (actions > 1 ? 's' : '') + '</span>' : '') +
|
|
(!nodeCount ? '<span>Empty workflow</span>' : '') +
|
|
'</div>' +
|
|
|
|
// Footer
|
|
'<div style="display:flex;align-items:center;justify-content:space-between;font-size:0.75rem;color:rgba(148,163,184,0.5)">' +
|
|
'<span>Runs: ' + (w.runCount || 0) + '</span>' +
|
|
'<span>Last: <span style="color:' + runStatusColor + '">' + runStatusIcon + '</span> ' + lastRun + '</span>' +
|
|
'</div>' +
|
|
'</a>';
|
|
}
|
|
html += '</div>';
|
|
} else {
|
|
// List view for many workflows
|
|
html += '<div style="display:flex;flex-direction:column;gap:0.5rem">';
|
|
for (var i = 0; i < wfs.length; i++) {
|
|
var w = wfs[i];
|
|
var nodeCount = (w.nodes || []).length;
|
|
var statusColor = w.enabled ? '#34d399' : '#64748b';
|
|
var statusLabel = w.enabled ? 'Active' : 'Disabled';
|
|
var lastRun = w.lastRunAt ? new Date(w.lastRunAt).toLocaleDateString() : 'Never';
|
|
var runStatusIcon = w.lastRunStatus === 'success' ? '✓' : w.lastRunStatus === 'error' ? '✗' : '—';
|
|
var runStatusColor = w.lastRunStatus === 'success' ? '#34d399' : w.lastRunStatus === 'error' ? '#ef4444' : '#64748b';
|
|
|
|
html += '<a href="' + basePath + 'reminders?wf=' + w.id + '" ' +
|
|
'style="display:flex;align-items:center;gap:1rem;text-decoration:none;border-radius:8px;' +
|
|
'background:rgba(30,41,59,0.4);border:1px solid rgba(148,163,184,0.08);' +
|
|
'padding:0.75rem 1rem;transition:border-color 0.2s" ' +
|
|
'onmouseover="this.style.borderColor=\'rgba(139,92,246,0.4)\'" ' +
|
|
'onmouseout="this.style.borderColor=\'rgba(148,163,184,0.08)\'">' +
|
|
'<span style="width:8px;height:8px;border-radius:50%;background:' + statusColor + ';flex-shrink:0"></span>' +
|
|
'<span style="flex:1;font-weight:500;color:#e2e8f0;font-size:0.9rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + (w.name || 'Untitled') + '</span>' +
|
|
'<span style="font-size:0.75rem;color:rgba(148,163,184,0.5)">' + nodeCount + ' nodes</span>' +
|
|
'<span style="font-size:0.75rem;color:rgba(148,163,184,0.5)">' + (w.runCount || 0) + ' runs</span>' +
|
|
'<span style="font-size:0.75rem;color:' + runStatusColor + '">' + runStatusIcon + ' ' + lastRun + '</span>' +
|
|
'</a>';
|
|
}
|
|
html += '</div>';
|
|
}
|
|
|
|
// Add "Open Canvas" link at the bottom
|
|
html += '<div style="text-align:center;margin-top:1.5rem">' +
|
|
'<a href="' + basePath + 'reminders" ' +
|
|
'style="color:#8b5cf6;text-decoration:none;font-size:0.9rem;font-weight:500">' +
|
|
'Open Automation Canvas →</a>' +
|
|
'</div>';
|
|
|
|
container.innerHTML = html;
|
|
})
|
|
.catch(function() {
|
|
container.innerHTML =
|
|
'<div style="text-align:center;padding:2rem">' +
|
|
'<p style="color:rgba(148,163,184,0.5)">Could not load automations.</p>' +
|
|
'<a href="' + basePath + 'reminders" ' +
|
|
'style="color:#8b5cf6;text-decoration:none;font-size:0.9rem">Open Automation Canvas →</a>' +
|
|
'</div>';
|
|
});
|
|
})();
|
|
</script>
|
|
|
|
<!-- How it works -->
|
|
<section class="rl-section">
|
|
<div class="rl-container">
|
|
<h2 style="text-align:center;font-size:1.5rem;margin-bottom:2rem;color:#e2e8f0">How it works</h2>
|
|
<div class="rl-grid-2">
|
|
<div class="rl-card" style="padding:2rem">
|
|
<h3 style="color:#f59e0b">Persistent Jobs</h3>
|
|
<p>Jobs are stored in Automerge documents — they survive container restarts and server reboots. No more lost crontabs.</p>
|
|
</div>
|
|
<div class="rl-card" style="padding:2rem">
|
|
<h3 style="color:#f97316">60-Second Tick Loop</h3>
|
|
<p>A lightweight in-process loop checks every 60 seconds for due jobs. No external scheduler process needed.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Ecosystem integration -->
|
|
<section class="rl-section">
|
|
<div class="rl-container">
|
|
<h2 style="text-align:center;font-size:1.5rem;margin-bottom:2rem;color:#e2e8f0">Ecosystem Integration</h2>
|
|
<div class="rl-grid-3">
|
|
<div class="rl-card rl-card--center" style="padding:1.5rem">
|
|
<h3>rCal</h3>
|
|
<p>Create recurring calendar events automatically via the calendar-event action type.</p>
|
|
</div>
|
|
<div class="rl-card rl-card--center" style="padding:1.5rem">
|
|
<h3>rInbox</h3>
|
|
<p>Schedule email delivery through shared SMTP infrastructure.</p>
|
|
</div>
|
|
<div class="rl-card rl-card--center" style="padding:1.5rem">
|
|
<h3>Backlog</h3>
|
|
<p>Scan backlog tasks and generate automated priority briefings on any cadence.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- CTA -->
|
|
<section class="rl-section" style="text-align:center;padding:4rem 0">
|
|
<h2 class="rl-heading" style="font-size:1.75rem;background:linear-gradient(to right,#f59e0b,#f97316);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text">
|
|
Stop managing crontabs. Start scheduling from rSpace.
|
|
</h2>
|
|
<p style="color:rgba(148,163,184,0.8);margin-top:1rem">
|
|
<a href="/" style="color:#f59e0b;text-decoration:none">← Back to rSpace</a>
|
|
</p>
|
|
</section>
|
|
`;
|
|
}
|