Some checks failed
Proyectos/simplenote-web/pipeline/head There was a failure building this commit
- Fixed malformed .mobile-nav-btn CSS syntax - Added global overflow-x: hidden to prevent horizontal scroll - Made projects.js use consistent sidebar overlay approach - Removed hardcoded min-width: 450px from all modals - Added safe area insets for notched mobile devices - Hamburger menu now appears on ALL views on mobile - Sidebar slides in as overlay on mobile - All touch targets are at least 44px - Font sizes are at least 16px to prevent iOS zoom - Modals are full-width on mobile
180 lines
6.5 KiB
JavaScript
180 lines
6.5 KiB
JavaScript
// Projects List View - Shows all projects
|
|
|
|
import { api } from '../api.js';
|
|
|
|
export async function renderProjects(app) {
|
|
let projects = [];
|
|
|
|
try {
|
|
const result = await api.getProjects();
|
|
projects = result.projects || [];
|
|
} catch (e) {
|
|
app.showToast('Failed to load projects', 'error');
|
|
}
|
|
|
|
const appEl = document.getElementById('app');
|
|
|
|
// Mobile sidebar functions - consistent with other views
|
|
window.toggleMobileSidebar = function() {
|
|
const sidebar = document.getElementById('sidebar');
|
|
const overlay = document.querySelector('.sidebar-overlay');
|
|
if (sidebar) {
|
|
sidebar.classList.toggle('mobile-open');
|
|
if (overlay) overlay.classList.toggle('active');
|
|
}
|
|
};
|
|
|
|
window.closeMobileSidebar = function() {
|
|
const sidebar = document.getElementById('sidebar');
|
|
const overlay = document.querySelector('.sidebar-overlay');
|
|
if (sidebar) {
|
|
sidebar.classList.remove('mobile-open');
|
|
if (overlay) overlay.classList.remove('active');
|
|
}
|
|
};
|
|
|
|
function render() {
|
|
appEl.innerHTML = `
|
|
<header class="app-header">
|
|
<button type="button" class="mobile-nav-btn" onclick="window.toggleMobileSidebar()" title="Menu">☰</button>
|
|
<div class="logo">📝 SimpleNote</div>
|
|
<div class="header-actions">
|
|
<button class="btn btn-primary" onclick="window.showNewProjectModal()">+ New</button>
|
|
</div>
|
|
</header>
|
|
<div class="sidebar-overlay" onclick="window.closeMobileSidebar()"></div>
|
|
<aside class="sidebar" id="sidebar" style="padding-top: 56px;">
|
|
<button class="sidebar-close-btn" onclick="window.closeMobileSidebar()">✕</button>
|
|
<div class="sidebar-scroll">
|
|
<div class="sidebar-section">
|
|
<h3>📋 Projects</h3>
|
|
<div class="tag-list">
|
|
${projects.length === 0 ? '<p style="color:var(--color-text-muted);font-size:0.875rem;">No projects yet</p>' : projects.map(project => `
|
|
<div class="tag-item" onclick="window.app.navigate('project', {id: '${project.id}'}); window.closeMobileSidebar();">
|
|
<span>📋 ${escapeHtml(project.name)}</span>
|
|
</div>
|
|
`).join('')}
|
|
</div>
|
|
</div>
|
|
<div class="quick-links">
|
|
<a class="quick-link" href="#" onclick="window.showNewProjectModal(); window.closeMobileSidebar(); return false;">+ New Project</a>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
<div class="projects-page">
|
|
<div class="projects-header">
|
|
<h1>Projects</h1>
|
|
<p style="color: var(--color-text-secondary);">Organize your documents into projects and folders</p>
|
|
</div>
|
|
<div class="projects-grid">
|
|
${projects.length === 0 ? `
|
|
<div class="empty-state">
|
|
<div class="icon">📋</div>
|
|
<h3>No projects yet</h3>
|
|
<p>Create your first project to get started</p>
|
|
<button class="btn btn-primary" onclick="window.showNewProjectModal()">+ Create Project</button>
|
|
</div>
|
|
` : projects.map(project => renderProjectCard(project)).join('')}
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
render();
|
|
}
|
|
|
|
function renderProjectCard(project) {
|
|
const createdDate = formatDate(project.createdAt);
|
|
const docCount = project.documentCount || 0;
|
|
const folderCount = project.folderCount || 0;
|
|
|
|
return `
|
|
<div class="project-card" onclick="window.app.navigate('project', {id: '${project.id}'})">
|
|
<div class="project-card-header">
|
|
<div class="project-icon">📋</div>
|
|
<div class="project-info">
|
|
<h3 class="project-name">${escapeHtml(project.name)}</h3>
|
|
${project.description ? `<p class="project-description">${escapeHtml(project.description)}</p>` : ''}
|
|
</div>
|
|
</div>
|
|
<div class="project-card-meta">
|
|
<span class="meta-item">📄 ${docCount} docs</span>
|
|
<span class="meta-item">📁 ${folderCount} folders</span>
|
|
<span class="meta-item">📅 ${createdDate}</span>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
function escapeHtml(str) {
|
|
if (!str) return '';
|
|
const div = document.createElement('div');
|
|
div.textContent = str;
|
|
return div.innerHTML;
|
|
}
|
|
|
|
function formatDate(dateStr) {
|
|
if (!dateStr) return '';
|
|
const date = new Date(dateStr);
|
|
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
|
|
}
|
|
|
|
// Global function: Show modal to create new project
|
|
window.showNewProjectModal = function() {
|
|
const backdrop = document.createElement('div');
|
|
backdrop.className = 'modal-backdrop';
|
|
backdrop.innerHTML = `
|
|
<div class="modal">
|
|
<div class="modal-header">
|
|
<span>📋</span>
|
|
<h3>Create New Project</h3>
|
|
<button class="modal-close" onclick="this.closest('.modal-backdrop').remove()">✕</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="form-group">
|
|
<label for="new-project-name">Project Name</label>
|
|
<input type="text" id="new-project-name" class="form-control" placeholder="e.g., Backend Requirements">
|
|
</div>
|
|
<div class="form-group" style="margin-top: 16px;">
|
|
<label for="new-project-description">Description (optional)</label>
|
|
<textarea id="new-project-description" class="form-control" placeholder="Brief description of the project..." rows="3" style="resize: vertical;"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-ghost" onclick="this.closest('.modal-backdrop').remove()">Cancel</button>
|
|
<button class="btn btn-primary" id="create-project-btn">Create Project</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
document.body.appendChild(backdrop);
|
|
|
|
const nameInput = document.getElementById('new-project-name');
|
|
const descInput = document.getElementById('new-project-description');
|
|
const createBtn = document.getElementById('create-project-btn');
|
|
|
|
createBtn.onclick = async () => {
|
|
const name = nameInput.value.trim();
|
|
if (!name) {
|
|
window.app.showToast('Please enter a project name', 'error');
|
|
return;
|
|
}
|
|
try {
|
|
await api.createProject({
|
|
name,
|
|
description: descInput.value.trim()
|
|
});
|
|
backdrop.remove();
|
|
window.app.showToast('Project created successfully', 'success');
|
|
window.app.navigate('projects');
|
|
} catch (e) {
|
|
window.app.showToast('Failed to create project: ' + e.message, 'error');
|
|
}
|
|
};
|
|
|
|
backdrop.onclick = (e) => {
|
|
if (e.target === backdrop) backdrop.remove();
|
|
};
|
|
|
|
nameInput.focus();
|
|
}
|