closed sections by default, yyyy-mm-dd hh:mm timestamp, infos section with stored answers, needs updated with coupon/homepage done

This commit is contained in:
Oliver
2026-06-09 19:41:24 -03:00
parent f2a96d8cff
commit 09c0605bf6
+101 -46
View File
@@ -99,15 +99,18 @@
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
margin-bottom: 12px; margin-bottom: 0;
cursor: pointer; cursor: pointer;
user-select: none; user-select: none;
padding: 6px 0;
} }
.section-header:hover { color: var(--accent); }
.section-header h2 { .section-header h2 {
font-size: 15px; font-size: 15px;
font-weight: 600; font-weight: 600;
color: var(--text); color: var(--text);
} }
.section-header:hover h2 { color: var(--accent); }
.section-header .count { .section-header .count {
font-size: 12px; font-size: 12px;
color: var(--text-muted); color: var(--text-muted);
@@ -121,12 +124,17 @@
} }
.section-toggle.collapsed { transform: rotate(-90deg); } .section-toggle.collapsed { transform: rotate(-90deg); }
.section-body { .section-body {
transition: max-height .25s ease, opacity .15s ease; transition: max-height .25s ease, opacity .15s ease, margin .15s ease;
overflow: hidden; overflow: hidden;
max-height: 0;
opacity: 0;
margin: 0;
}
.section-body.open {
max-height: 20000px; max-height: 20000px;
opacity: 1; opacity: 1;
margin-top: 12px;
} }
.section-body.hidden { max-height: 0; opacity: 0; padding: 0; }
/* ── Goals Grid ──────────────────────── */ /* ── Goals Grid ──────────────────────── */
.goals-grid { .goals-grid {
@@ -460,6 +468,39 @@
} }
.highlight { color: var(--accent); } .highlight { color: var(--accent); }
/* ── Infos ───────────────────────────── */
.infos-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-card);
padding: 20px 24px;
}
.infos-list { list-style: none; }
.infos-list li {
display: flex;
align-items: flex-start;
gap: 10px;
padding: 7px 0;
border-bottom: 1px solid rgba(30,30,42,.4);
font-size: 13px;
color: var(--text);
line-height: 1.6;
}
.infos-list li:last-child { border-bottom: none; }
.infos-list li .info-label {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: .04em;
color: var(--accent);
white-space: nowrap;
flex-shrink: 0;
min-width: 100px;
}
.infos-list li .info-value {
color: var(--text);
}
@media (max-width: 900px) { @media (max-width: 900px) {
.campaign-grid { grid-template-columns: 1fr; } .campaign-grid { grid-template-columns: 1fr; }
.comms-grid { grid-template-columns: 1fr; } .comms-grid { grid-template-columns: 1fr; }
@@ -491,7 +532,7 @@
<div class="section"> <div class="section">
<div class="section-header" onclick="toggleSection('goals-section', this)"> <div class="section-header" onclick="toggleSection('goals-section', this)">
<h2>🎯 Q2/2026 Goals</h2> <h2>🎯 Q2/2026 Goals</h2>
<span class="section-toggle"></span> <span class="section-toggle collapsed"></span>
</div> </div>
<div class="section-body" id="goals-section"> <div class="section-body" id="goals-section">
<div class="goals-grid" id="goals-grid"></div> <div class="goals-grid" id="goals-grid"></div>
@@ -503,19 +544,19 @@
<div class="section-header" onclick="toggleSection('campaigns-section', this)"> <div class="section-header" onclick="toggleSection('campaigns-section', this)">
<h2>📡 Campaigns</h2> <h2>📡 Campaigns</h2>
<span class="section-count count">overview</span> <span class="section-count count">overview</span>
<span class="section-toggle"></span> <span class="section-toggle collapsed"></span>
</div> </div>
<div class="section-body" id="campaigns-section"> <div class="section-body" id="campaigns-section">
<div class="campaign-grid" id="campaign-grid"></div> <div class="campaign-grid" id="campaign-grid"></div>
</div> </div>
</div> </div>
<!-- ════════ NEEDS (CHECKBOXES) ════════ --> <!-- ════════ NEEDS ════════ -->
<div class="section"> <div class="section">
<div class="section-header" onclick="toggleSection('needs-section', this)"> <div class="section-header" onclick="toggleSection('needs-section', this)">
<h2>📦 Items Needed From You</h2> <h2>📦 Items Needed From You</h2>
<span class="section-count count">click to check off</span> <span class="section-count count">click to check off</span>
<span class="section-toggle"></span> <span class="section-toggle collapsed"></span>
</div> </div>
<div class="section-body" id="needs-section"> <div class="section-body" id="needs-section">
<div class="needs-card"> <div class="needs-card">
@@ -529,18 +570,32 @@
<div class="section-header" onclick="toggleSection('comms-section', this)"> <div class="section-header" onclick="toggleSection('comms-section', this)">
<h2>✉ Communications</h2> <h2>✉ Communications</h2>
<span class="section-count count">important only</span> <span class="section-count count">important only</span>
<span class="section-toggle"></span> <span class="section-toggle collapsed"></span>
</div> </div>
<div class="section-body" id="comms-section"> <div class="section-body" id="comms-section">
<div class="comms-grid" id="comms-grid"></div> <div class="comms-grid" id="comms-grid"></div>
</div> </div>
</div> </div>
<!-- ════════ INFOS ════════ -->
<div class="section">
<div class="section-header" onclick="toggleSection('infos-section', this)">
<h2>📋 Infos</h2>
<span class="section-count count">look here for answers</span>
<span class="section-toggle collapsed"></span>
</div>
<div class="section-body" id="infos-section">
<div class="infos-card">
<ul class="infos-list" id="infos-list"></ul>
</div>
</div>
</div>
<!-- ════════ ROUTINE ════════ --> <!-- ════════ ROUTINE ════════ -->
<div class="section"> <div class="section">
<div class="section-header" onclick="toggleSection('routine-section', this)"> <div class="section-header" onclick="toggleSection('routine-section', this)">
<h2>⏰ Daily Routine</h2> <h2>⏰ Daily Routine</h2>
<span class="section-toggle"></span> <span class="section-toggle collapsed"></span>
</div> </div>
<div class="section-body" id="routine-section"> <div class="section-body" id="routine-section">
<div class="grid-2"> <div class="grid-2">
@@ -641,22 +696,31 @@
} }
]; ];
/* Top 5 most important needs */ /* Top 5 needs — 2 done after Oliver's last update */
const NEEDS = [ const NEEDS = [
{ id: "n1", text: "Business emails for top 20 YouTube creators — blocking cold email outreach", done: false }, { id: "n1", text: "Business emails for top 20 YouTube creators — blocking cold email outreach", done: false },
{ id: "n2", text: "Influencer partnership budget (free plans? cash? affiliate %?)", done: false }, { id: "n2", text: "Coupon codes ready: creator950 ($9.50), friends950 ($9.50) — unlimited, includes OpenRouter credits", done: true },
{ id: "n3", text: "Product demo video (3 min screen recording)", done: false }, { id: "n3", text: "Product demo video (3 min screen recording)", done: false },
{ id: "n4", text: "Social media account access (X/Twitter + LinkedIn posting)", done: false }, { id: "n4", text: "Social media account access (X/Twitter + LinkedIn posting)", done: false },
{ id: "n5", text: "'Why I built Derez.ai' notes for blog post", done: false }, { id: "n5", text: "'Why I built Derez.ai' story — available on homepage", done: true },
]; ];
/* Filtered comms — spam/sales/ads/work-for-you removed */ /* ── Infos (answers Oliver asked for, stored here not chat) ── */
const infos = [
{ label: 'Coupon Codes', value: 'creator950 ($9.50 off), friends950 ($9.50 off) — unlimited use, OpenRouter credits still apply' },
{ label: 'Cold Email', value: 'Signed as Mark — Customer Success Agent. Templates reviewed via Telegram before send.' },
{ label: 'Influencer Offer', value: 'Free 3-month agent + 10% recurring affiliate commission. Pitch: "Deploy AI agents in 5 min — no DevOps."' },
{ label: 'Site Status', value: 'derez.ai live. Prelaunch. 89 CRM contacts (59 YT creators on Hold).' },
{ label: 'Timezone', value: 'America/Asuncion (PYST, UTC-3/UTC-4). Daily brief 09:00.' },
];
/* ── Filtered comms — spam/sales/ads/work-for-you removed ── */
function isSpam(email) { function isSpam(email) {
const lower = (email.subj + ' ' + email.from + ' ' + (email.detail || '')).toLowerCase(); const lower = (email.subj + ' ' + email.from + ' ' + (email.detail || '')).toLowerCase();
const spamPatterns = [ const spamPatterns = [
'spam', 'advertisement', 'advert', 'work for you', 'work from home', 'spam', 'advertisement', 'advert', 'work for you', 'work from home',
'make money', 'earn', 'investment opportunity', 'click here', 'make money', 'earn', 'investment opportunity', 'click here',
'buy now', 'limited offer', 'discount', 'free money', 'congratulations', 'buy now', 'limited offer', 'free money', 'congratulations',
'you\'ve won', 'marketing offer', 'promotion', 'partner offer', 'you\'ve won', 'marketing offer', 'promotion', 'partner offer',
'guest post', 'seo services', 'link building', 'content writing services', 'guest post', 'seo services', 'link building', 'content writing services',
]; ];
@@ -664,10 +728,10 @@
} }
const rawInbox = [ const rawInbox = [
{ id: 'i1', time: 'Jun 09 14:44', from: 'Mail Delivery System', subj: 'Undelivered — the@123.de', detail: '421 Downstream server error — cold email bounced', alert: true, spam: false }, { id: 'i1', time: '14:44', from: 'Mail Delivery System', subj: 'Undelivered — the@123.de', detail: '421 Downstream server error — cold email bounced', alert: true, spam: false },
{ id: 'i2', time: 'Jun 08 21:04', from: 'Mail Delivery System', subj: 'Undelivered — the@123.de', detail: '421 Downstream server error', alert: false, spam: false }, { id: 'i2', time: '21:04', from: 'Mail Delivery System', subj: 'Undelivered — the@123.de', detail: '421 Downstream server error', alert: false, spam: false },
{ id: 'i3', time: 'Jun 10 08:12', from: 'seo@boostmyrank.xyz', subj: 'Guest post opportunity for your site', detail: 'Wants to publish sponsored content', spam: true }, { id: 'i3', time: '08:12', from: 'seo@boostmyrank.xyz', subj: 'Guest post opportunity for your site', detail: 'Wants to publish sponsored content', spam: true },
{ id: 'i4', time: 'Jun 09 19:33', from: 'hannah@freelance.io', subj: 'I can help with your content marketing', detail: 'Offering writing services', spam: true }, { id: 'i4', time: '19:33', from: 'hannah@freelance.io', subj: 'I can help with your content marketing', detail: 'Offering writing services', spam: true },
]; ];
const rawSent = [ const rawSent = [
@@ -675,29 +739,6 @@
{ time: 'Jun 08', to: 'oliver@odoo4projects.com', subj: 'managing your agents without SSH', spam: false }, { time: 'Jun 08', to: 'oliver@odoo4projects.com', subj: 'managing your agents without SSH', spam: false },
]; ];
const strategyCards = [
{
icon: '📢', title: 'Influencer Outreach',
body: 'Target 60 YouTube creators in AI/ML/DevOps. Offer free 3-month agent + 10% recurring affiliate commission. Pitch: "Deploy AI agents in 5 minutes — no DevOps needed."',
goal: 'Goal: 20 onboarded, 3+ social mentions'
},
{
icon: '👨‍👩‍👧‍👦', title: 'Cold Email',
body: 'Templates & coupons ready (friends950, creator950). Signed as Mark. All drafts reviewed via Telegram before sending.',
goal: 'Goal: 100 sent, 10+ replies'
},
{
icon: '🔗', title: 'LinkedIn Presence',
body: 'Zero presence. Need page, connections, and content strategy before outreach.',
goal: 'Goal: Page live, 100 connections'
},
{
icon: '📈', title: 'SEO & Analytics',
body: 'Plausible analytics live with 7 custom events. Weekly cron monitors insights. Need Search Console + directory listings.',
goal: 'Goal: 10K visitors/mo, top 5 for "AI agent hosting"'
}
];
const comms = { const comms = {
inbox: { inbox: {
total: rawInbox.filter(i => !i.spam).length, total: rawInbox.filter(i => !i.spam).length,
@@ -713,16 +754,23 @@
}; };
/* ════════ RENDER ════════ */ /* ════════ RENDER ════════ */
function fmtDate() { function fmtTimestamp(date) {
return new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); const d = date || new Date();
const y = d.getFullYear();
const mo = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
const h = String(d.getHours()).padStart(2, '0');
const mi = String(d.getMinutes()).padStart(2, '0');
return `${y}-${mo}-${day} ${h}:${mi}`;
} }
function pct(cur, target) { return target > 0 ? Math.min(100, Math.round(cur / target * 100)) : 0; } function pct(cur, target) { return target > 0 ? Math.min(100, Math.round(cur / target * 100)) : 0; }
function barClass(p) { return p >= 80 ? 'green' : p >= 40 ? 'blue' : p >= 20 ? 'yellow' : 'red'; } function barClass(p) { return p >= 80 ? 'green' : p >= 40 ? 'blue' : p >= 20 ? 'yellow' : 'red'; }
function toggleSection(id, header) { function toggleSection(id, header) {
const el = document.getElementById(id); const el = document.getElementById(id);
const toggle = header.querySelector('.section-toggle'); const toggle = header.querySelector('.section-toggle');
el.classList.toggle('hidden'); el.classList.toggle('open');
toggle.classList.toggle('collapsed'); toggle.classList.toggle('collapsed');
} }
@@ -752,7 +800,7 @@
}).join(''); }).join('');
} }
/* ── Interactive checkboxes ── */ /* ── Needs (checkboxes) ── */
function getNeeds() { function getNeeds() {
try { try {
const saved = localStorage.getItem("derez-needs"); const saved = localStorage.getItem("derez-needs");
@@ -823,6 +871,12 @@
</div>`; </div>`;
} }
function renderInfos() {
document.getElementById('infos-list').innerHTML = infos.map(i =>
`<li><span class="info-label">${i.label}</span><span class="info-value">${i.value}</span></li>`
).join('');
}
async function fetchLiveData() { async function fetchLiveData() {
try { try {
const res = await fetch('data/operations.json?t=' + Date.now()); const res = await fetch('data/operations.json?t=' + Date.now());
@@ -836,11 +890,12 @@
} }
/* ── Init ── */ /* ── Init ── */
document.getElementById('brief-date').textContent = fmtDate(); document.getElementById('brief-date').textContent = fmtTimestamp();
renderGoals(); renderGoals();
renderCampaigns(); renderCampaigns();
renderNeeds(getNeeds()); renderNeeds(getNeeds());
renderCommunications(); renderCommunications();
renderInfos();
fetchLiveData(); fetchLiveData();
</script> </script>
</body> </body>