major restructure
This commit is contained in:
286
soleprint/station/tools/sbwrapper/sidebar.js
Executable file
286
soleprint/station/tools/sbwrapper/sidebar.js
Executable file
@@ -0,0 +1,286 @@
|
||||
// Pawprint Wrapper - Sidebar Logic
|
||||
|
||||
class PawprintSidebar {
|
||||
constructor() {
|
||||
this.config = null;
|
||||
this.currentUser = null;
|
||||
this.sidebar = null;
|
||||
this.toggleBtn = null;
|
||||
}
|
||||
|
||||
async init() {
|
||||
// Load configuration
|
||||
await this.loadConfig();
|
||||
|
||||
// Create sidebar elements
|
||||
this.createSidebar();
|
||||
this.createToggleButton();
|
||||
|
||||
// Setup event listeners
|
||||
this.setupEventListeners();
|
||||
|
||||
// Check if user is already logged in
|
||||
this.checkCurrentUser();
|
||||
|
||||
// Load saved sidebar state
|
||||
this.loadSidebarState();
|
||||
}
|
||||
|
||||
async loadConfig() {
|
||||
try {
|
||||
const response = await fetch('/wrapper/config.json');
|
||||
this.config = await response.json();
|
||||
console.log('[Pawprint] Config loaded:', this.config.nest_name);
|
||||
} catch (error) {
|
||||
console.error('[Pawprint] Failed to load config:', error);
|
||||
// Use default config
|
||||
this.config = {
|
||||
nest_name: 'default',
|
||||
wrapper: {
|
||||
environment: {
|
||||
backend_url: 'http://localhost:8000',
|
||||
frontend_url: 'http://localhost:3000'
|
||||
},
|
||||
users: []
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
createSidebar() {
|
||||
const sidebar = document.createElement('div');
|
||||
sidebar.id = 'pawprint-sidebar';
|
||||
sidebar.innerHTML = this.getSidebarHTML();
|
||||
document.body.appendChild(sidebar);
|
||||
this.sidebar = sidebar;
|
||||
}
|
||||
|
||||
createToggleButton() {
|
||||
const button = document.createElement('button');
|
||||
button.id = 'sidebar-toggle';
|
||||
button.innerHTML = '<span class="icon">◀</span>';
|
||||
button.title = 'Toggle Pawprint Sidebar (Ctrl+Shift+P)';
|
||||
document.body.appendChild(button);
|
||||
this.toggleBtn = button;
|
||||
}
|
||||
|
||||
getSidebarHTML() {
|
||||
const users = this.config.wrapper.users || [];
|
||||
|
||||
return `
|
||||
<div class="sidebar-header">
|
||||
<h2>🐾 Pawprint</h2>
|
||||
<div class="nest-name">${this.config.nest_name}</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-content">
|
||||
<div id="status-container"></div>
|
||||
|
||||
<!-- Quick Login Panel -->
|
||||
<div class="panel">
|
||||
<h3>👤 Quick Login</h3>
|
||||
|
||||
<div id="current-user-display" style="display: none;">
|
||||
<div class="current-user">
|
||||
Logged in as: <strong id="current-username"></strong>
|
||||
<button class="logout-btn" onclick="pawprintSidebar.logout()">
|
||||
Logout
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="user-cards">
|
||||
${users.map(user => `
|
||||
<div class="user-card" data-user-id="${user.id}" onclick="pawprintSidebar.loginAs('${user.id}')">
|
||||
<div class="icon">${user.icon}</div>
|
||||
<div class="info">
|
||||
<span class="label">${user.label}</span>
|
||||
<span class="role">${user.role}</span>
|
||||
</div>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Environment Info Panel -->
|
||||
<div class="panel">
|
||||
<h3>🌍 Environment</h3>
|
||||
<div style="font-size: 12px; opacity: 0.8;">
|
||||
<div style="margin-bottom: 8px;">
|
||||
<strong>Backend:</strong><br>
|
||||
<code style="font-size: 11px;">${this.config.wrapper.environment.backend_url}</code>
|
||||
</div>
|
||||
<div>
|
||||
<strong>Frontend:</strong><br>
|
||||
<code style="font-size: 11px;">${this.config.wrapper.environment.frontend_url}</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-footer">
|
||||
Pawprint Dev Tools
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
// Toggle button
|
||||
this.toggleBtn.addEventListener('click', () => this.toggle());
|
||||
|
||||
// Keyboard shortcut: Ctrl+Shift+P
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.ctrlKey && e.shiftKey && e.key === 'P') {
|
||||
e.preventDefault();
|
||||
this.toggle();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.sidebar.classList.toggle('expanded');
|
||||
this.saveSidebarState();
|
||||
}
|
||||
|
||||
saveSidebarState() {
|
||||
const isExpanded = this.sidebar.classList.contains('expanded');
|
||||
localStorage.setItem('pawprint_sidebar_expanded', isExpanded);
|
||||
}
|
||||
|
||||
loadSidebarState() {
|
||||
const isExpanded = localStorage.getItem('pawprint_sidebar_expanded') === 'true';
|
||||
if (isExpanded) {
|
||||
this.sidebar.classList.add('expanded');
|
||||
}
|
||||
}
|
||||
|
||||
showStatus(message, type = 'info') {
|
||||
const container = document.getElementById('status-container');
|
||||
const statusDiv = document.createElement('div');
|
||||
statusDiv.className = `status-message ${type}`;
|
||||
statusDiv.textContent = message;
|
||||
container.innerHTML = '';
|
||||
container.appendChild(statusDiv);
|
||||
|
||||
// Auto-remove after 5 seconds
|
||||
setTimeout(() => {
|
||||
statusDiv.remove();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
async loginAs(userId) {
|
||||
const user = this.config.wrapper.users.find(u => u.id === userId);
|
||||
if (!user) return;
|
||||
|
||||
this.showStatus(`Logging in as ${user.label}... ⏳`, 'info');
|
||||
|
||||
try {
|
||||
const backendUrl = this.config.wrapper.environment.backend_url;
|
||||
const response = await fetch(`${backendUrl}/api/token/`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username: user.username,
|
||||
password: user.password
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Login failed: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Store tokens
|
||||
localStorage.setItem('access_token', data.access);
|
||||
localStorage.setItem('refresh_token', data.refresh);
|
||||
|
||||
// Store user info
|
||||
localStorage.setItem('user_info', JSON.stringify({
|
||||
username: user.username,
|
||||
label: user.label,
|
||||
role: data.details?.role || user.role
|
||||
}));
|
||||
|
||||
this.showStatus(`✓ Logged in as ${user.label}`, 'success');
|
||||
this.currentUser = user;
|
||||
this.updateCurrentUserDisplay();
|
||||
|
||||
// Reload page after short delay
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
|
||||
} catch (error) {
|
||||
console.error('[Pawprint] Login error:', error);
|
||||
this.showStatus(`✗ Login failed: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
logout() {
|
||||
localStorage.removeItem('access_token');
|
||||
localStorage.removeItem('refresh_token');
|
||||
localStorage.removeItem('user_info');
|
||||
|
||||
this.showStatus('✓ Logged out', 'success');
|
||||
this.currentUser = null;
|
||||
this.updateCurrentUserDisplay();
|
||||
|
||||
// Reload page after short delay
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
checkCurrentUser() {
|
||||
const userInfo = localStorage.getItem('user_info');
|
||||
if (userInfo) {
|
||||
try {
|
||||
this.currentUser = JSON.parse(userInfo);
|
||||
this.updateCurrentUserDisplay();
|
||||
} catch (error) {
|
||||
console.error('[Pawprint] Failed to parse user info:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateCurrentUserDisplay() {
|
||||
const display = document.getElementById('current-user-display');
|
||||
const username = document.getElementById('current-username');
|
||||
|
||||
if (this.currentUser) {
|
||||
display.style.display = 'block';
|
||||
username.textContent = this.currentUser.username;
|
||||
|
||||
// Highlight active user card
|
||||
document.querySelectorAll('.user-card').forEach(card => {
|
||||
card.classList.remove('active');
|
||||
});
|
||||
|
||||
const activeCard = document.querySelector(`.user-card[data-user-id="${this.getUserIdByUsername(this.currentUser.username)}"]`);
|
||||
if (activeCard) {
|
||||
activeCard.classList.add('active');
|
||||
}
|
||||
} else {
|
||||
display.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
getUserIdByUsername(username) {
|
||||
const user = this.config.wrapper.users.find(u => u.username === username);
|
||||
return user ? user.id : null;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize sidebar when DOM is ready
|
||||
const pawprintSidebar = new PawprintSidebar();
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => pawprintSidebar.init());
|
||||
} else {
|
||||
pawprintSidebar.init();
|
||||
}
|
||||
|
||||
console.log('[Pawprint] Sidebar script loaded');
|
||||
Reference in New Issue
Block a user