feat: Add Projects and Folders UI (SimpleNote v2)

- New Projects view (projects.js): Lists all projects with cards
- New ProjectView (projectView.js): Project dashboard with folder tree
- Updated API client: Projects and Folders CRUD methods
- New modals: NewProjectModal, NewFolderModal, MoveToFolderModal
- Edit/Delete project functionality
- Updated navigation: ProjectList -> ProjectView -> FolderView
- Consistent dark theme styling

Changes:
- public/js/views/projects.js (NEW)
- public/js/views/projectView.js (NEW)
- public/js/api.js (added Projects/Folders API methods)
- public/js/app.js (added navigation routes)
- public/js/components/sidebar.js (added Projects link)
- public/css/style.css (added project/folder styles)
This commit is contained in:
Hiro
2026-03-28 13:03:23 +00:00
parent d7bb018c83
commit 9496fc8e36
6 changed files with 995 additions and 6 deletions

140
public/js/views/projects.js Normal file
View File

@@ -0,0 +1,140 @@
// 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');
function render() {
appEl.innerHTML = `
<header class="app-header">
<div class="logo">📝 SimpleNote</div>
<div class="header-actions">
<button class="btn btn-primary" onclick="window.showNewProjectModal()">+ New Project</button>
</div>
</header>
<div class="projects-page">
<div class="projects-header">
<h1>Projects</h1>
<p class="text-muted">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" style="min-width: 450px;">
<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();
};