Three bugs found deploying a fresh clone to seedproject.com, each fatal to the documented "spin up a new site" flow: - .gitignore: the unanchored `public/` pattern (meant for the root build output) also ignored api/public/ (the framework's front controller, controllers, models, views) and app/public/ (theme static assets: fonts, avatar placeholder). Neither was ever committed, so every fresh clone 500'd on all /api routes and 404'd on theme assets. Anchor the pattern to /public/ and commit both directories. - api/install/dump.sql: stray `CREATE DATABASE ochenta80_db123` (SQLyog export artifact) aborted `php console app:install` for any non-privileged DB user. The schema must import into whatever database the installer connects to. - PluginManager::boot() queries sp_plugins on every request, but no shipped schema creates it — even a successful install 500'd on every endpoint. Add migration 002_create_sp_plugins.sql matching the columns PluginManager reads/writes. Also empty api/system/errors.json, which shipped with stale error logs from an unrelated project. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C3JqxTe7TKaR7xufcMr7Ds
177 lines
8.4 KiB
JavaScript
177 lines
8.4 KiB
JavaScript
|
|
const SIGNAL_DATA = [
|
|
{ type: 'BUY', price: '2034.50', tp: '2042.00', sl: '2029.00', confidence: 89, time: '2m ago' },
|
|
{ type: 'SELL', price: '2045.10', tp: '2038.50', sl: '2048.00', confidence: 92, time: '15m ago' },
|
|
{ type: 'BUY', price: '2022.80', tp: '2030.00', sl: '2018.50', confidence: 76, time: '42m ago' },
|
|
{ type: 'SELL', price: '2051.00', tp: '2044.20', sl: '2055.00', confidence: 84, time: '1h ago' },
|
|
{ type: 'BUY', price: '2036.20', tp: '2041.50', sl: '2033.00', confidence: 65, time: '1h ago' },
|
|
{ type: 'SELL', price: '2048.90', tp: '2040.00', sl: '2052.00', confidence: 95, time: '2h ago' },
|
|
];
|
|
|
|
const HISTORY_DATA = [
|
|
{ asset: 'XAUUSD', type: 'BUY', entry: '2,024.50', exit: '2,031.20', yield: '+670 pts', color: 'text-emerald-400', time: '2h ago' },
|
|
{ asset: 'XAUUSD', type: 'SELL', entry: '2,038.10', exit: '2,034.40', yield: '+370 pts', color: 'text-emerald-400', time: '4h ago' },
|
|
{ asset: 'XAUUSD', type: 'BUY', entry: '2,018.00', exit: '2,015.50', yield: '-250 pts', color: 'text-rose-400', time: '6h ago' },
|
|
{ asset: 'XAUUSD', type: 'BUY', entry: '2,012.20', exit: '2,020.80', yield: '+860 pts', color: 'text-emerald-400', time: '10h ago' },
|
|
];
|
|
|
|
class Particle {
|
|
constructor(sphereRadius) {
|
|
const theta = Math.random() * 2 * Math.PI;
|
|
const phi = Math.acos(2 * Math.random() - 1);
|
|
this.sphereRadius = sphereRadius;
|
|
this.x = sphereRadius * Math.sin(phi) * Math.cos(theta);
|
|
this.y = sphereRadius * Math.sin(phi) * Math.sin(theta);
|
|
this.z = sphereRadius * Math.cos(phi);
|
|
this.size = Math.random() * 1.8 + 0.6;
|
|
this.baseOpacity = Math.random() * 0.6 + 0.3;
|
|
}
|
|
|
|
rotate(angleX, angleY) {
|
|
let cosY = Math.cos(angleY), sinY = Math.sin(angleY);
|
|
let x = this.x * cosY - this.z * sinY;
|
|
let z = this.z * cosY + this.x * sinY;
|
|
this.x = x; this.z = z;
|
|
|
|
let cosX = Math.cos(angleX), sinX = Math.sin(angleX);
|
|
let y = this.y * cosX - this.z * sinX;
|
|
z = this.z * cosX + this.y * sinX;
|
|
this.y = y; this.z = z;
|
|
}
|
|
|
|
draw(ctx, centerX, centerY) {
|
|
const scale = 300 / (300 + this.z);
|
|
const x2d = this.x * scale + centerX;
|
|
const y2d = this.y * scale + centerY;
|
|
const opacity = Math.max(0, this.baseOpacity + (this.z / this.sphereRadius) * 0.4);
|
|
|
|
ctx.beginPath();
|
|
ctx.arc(x2d, y2d, this.size * scale, 0, Math.PI * 2);
|
|
if (this.x > 40) ctx.fillStyle = `rgba(34, 211, 238, ${opacity})`;
|
|
else if (this.x < -40) ctx.fillStyle = `rgba(167, 139, 250, ${opacity})`;
|
|
else ctx.fillStyle = `rgba(248, 250, 252, ${opacity})`;
|
|
ctx.fill();
|
|
}
|
|
}
|
|
|
|
function initParticleSphere() {
|
|
const canvas = document.getElementById('particle-canvas');
|
|
const ctx = canvas.getContext('2d', { alpha: true });
|
|
const sphereRadius = 105;
|
|
const particleCount = 400;
|
|
const rotationSpeed = 0.003;
|
|
let particles = [];
|
|
let width, height;
|
|
|
|
function resize() {
|
|
const dpr = window.devicePixelRatio || 1;
|
|
const rect = canvas.parentElement.getBoundingClientRect();
|
|
canvas.width = rect.width * dpr;
|
|
canvas.height = rect.height * dpr;
|
|
canvas.style.width = `${rect.width}px`;
|
|
canvas.style.height = `${rect.height}px`;
|
|
ctx.scale(dpr, dpr);
|
|
width = rect.width;
|
|
height = rect.height;
|
|
}
|
|
|
|
function animate() {
|
|
ctx.clearRect(0, 0, width, height);
|
|
const centerX = width / 2;
|
|
const centerY = height / 2;
|
|
particles.sort((a, b) => b.z - a.z);
|
|
particles.forEach(p => {
|
|
p.rotate(rotationSpeed, rotationSpeed * 0.6);
|
|
p.draw(ctx, centerX, centerY);
|
|
});
|
|
requestAnimationFrame(animate);
|
|
}
|
|
|
|
for (let i = 0; i < particleCount; i++) particles.push(new Particle(sphereRadius));
|
|
window.addEventListener('resize', resize);
|
|
resize();
|
|
animate();
|
|
}
|
|
|
|
function renderSignals() {
|
|
const grid = document.getElementById('signals-grid');
|
|
grid.innerHTML = SIGNAL_DATA.map(s => `
|
|
<div class="glass-panel rounded-2xl p-6 hover:border-white/20 transition-all duration-500 group cursor-default border border-white/5 flex flex-col">
|
|
<div class="flex justify-between items-start mb-6">
|
|
<div class="flex items-center gap-4">
|
|
<div class="w-12 h-12 rounded-xl ${s.type === 'BUY' ? 'bg-emerald-400/10 border-emerald-400/20 text-emerald-400' : 'bg-rose-400/10 border-rose-400/20 text-rose-400'} flex items-center justify-center border">
|
|
${s.type === 'BUY' ? '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 7 13.5 15.5 8.5 10.5 2 17"/><polyline points="16 7 22 7 22 13"/></svg>' : '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 17 13.5 8.5 8.5 13.5 2 7"/><polyline points="16 17 22 17 22 11"/></svg>'}
|
|
</div>
|
|
<div>
|
|
<div class="text-[10px] text-slate-500 uppercase tracking-widest mb-0.5">Vector Type</div>
|
|
<div class="text-lg font-bold tracking-tight ${s.type === 'BUY' ? 'text-emerald-400' : 'text-rose-400'}">${s.type} ORDER</div>
|
|
</div>
|
|
</div>
|
|
<div class="text-[10px] text-slate-500 font-mono bg-white/5 border border-white/10 rounded px-2 py-1">${s.time}</div>
|
|
</div>
|
|
<div class="space-y-4 mb-8">
|
|
<div class="flex justify-between items-center bg-white/[0.02] p-2 rounded-lg">
|
|
<span class="text-xs text-slate-500 font-light">Trigger Entry</span>
|
|
<span class="text-slate-200 font-mono font-medium">${s.price}</span>
|
|
</div>
|
|
<div class="flex justify-between items-center p-2">
|
|
<span class="text-xs text-slate-500 font-light">Target TP</span>
|
|
<span class="text-emerald-400 font-mono font-medium">${s.tp}</span>
|
|
</div>
|
|
<div class="flex justify-between items-center p-2">
|
|
<span class="text-xs text-slate-500 font-light">Risk SL</span>
|
|
<span class="text-rose-400 font-mono font-medium">${s.sl}</span>
|
|
</div>
|
|
</div>
|
|
<div class="mt-auto pt-6 border-t border-white/5 flex items-center justify-between">
|
|
<div class="flex flex-col">
|
|
<span class="text-[10px] text-slate-600 uppercase tracking-widest">Confidence</span>
|
|
<span class="text-cyan-400 font-bold text-sm">${s.confidence}%</span>
|
|
</div>
|
|
<div class="h-1.5 w-24 bg-slate-800 rounded-full overflow-hidden">
|
|
<div class="h-full bg-gradient-to-r from-blue-600 to-cyan-400" style="width: ${s.confidence}%"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`).join('');
|
|
}
|
|
|
|
function renderHistory() {
|
|
const tbody = document.getElementById('history-body');
|
|
tbody.innerHTML = HISTORY_DATA.map(t => `
|
|
<tr class="group hover:bg-white/5 transition-colors cursor-default">
|
|
<td class="py-4 px-6">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-6 h-6 rounded-full bg-yellow-500/10 text-yellow-500 flex items-center justify-center text-[10px] font-bold border border-yellow-500/20">G</div>
|
|
<span class="text-slate-200 font-semibold">${t.asset}</span>
|
|
</div>
|
|
</td>
|
|
<td class="py-4 px-6">
|
|
<span class="inline-flex items-center px-2 py-0.5 rounded text-[10px] font-bold ${t.type === 'BUY' ? 'bg-emerald-400/10 text-emerald-400 border-emerald-400/20' : 'bg-rose-400/10 text-rose-400 border-rose-400/20'} border">
|
|
${t.type}
|
|
</span>
|
|
</td>
|
|
<td class="py-4 px-6 font-mono text-slate-400">${t.entry}</td>
|
|
<td class="py-4 px-6 font-mono text-slate-400">${t.exit}</td>
|
|
<td class="py-4 px-6 text-right font-mono font-medium ${t.color}">${t.yield}</td>
|
|
<td class="py-4 px-6 text-right text-slate-500 text-xs font-light">${t.time}</td>
|
|
</tr>
|
|
`).join('');
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
initParticleSphere();
|
|
renderSignals();
|
|
renderHistory();
|
|
|
|
const initBtn = document.getElementById('init-btn');
|
|
const signalsSection = document.getElementById('signals-section');
|
|
|
|
initBtn.addEventListener('click', () => {
|
|
signalsSection.classList.remove('hidden-section');
|
|
signalsSection.classList.add('visible-section');
|
|
setTimeout(() => {
|
|
signalsSection.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
}, 100);
|
|
});
|
|
});
|