feat: add frontend UI for SimpleNote Web
- Vanilla JS frontend with dark theme - Dashboard with sidebar (libraries tree, tags), document grid, search - Document viewer with markdown rendering and metadata panel - Document editor with split write/preview and formatting toolbar - Login screen with token authentication - All styled according to UI/UX specs (dark theme, accent #00d4aa) - API client for all endpoints - Responsive design
This commit is contained in:
120
public/js/api.js
Normal file
120
public/js/api.js
Normal file
@@ -0,0 +1,120 @@
|
||||
// API Client for SimpleNote Web
|
||||
|
||||
const API_BASE = '/api/v1';
|
||||
|
||||
class ApiClient {
|
||||
constructor() {
|
||||
this.token = localStorage.getItem('sn_token');
|
||||
}
|
||||
|
||||
setToken(token) {
|
||||
this.token = token;
|
||||
if (token) {
|
||||
localStorage.setItem('sn_token', token);
|
||||
} else {
|
||||
localStorage.removeItem('sn_token');
|
||||
}
|
||||
}
|
||||
|
||||
getHeaders() {
|
||||
const headers = {
|
||||
'Content-Type': 'application/json'
|
||||
};
|
||||
if (this.token) {
|
||||
headers['Authorization'] = `Bearer ${this.token}`;
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
async request(method, path, body = null) {
|
||||
const options = {
|
||||
method,
|
||||
headers: this.getHeaders()
|
||||
};
|
||||
if (body) {
|
||||
options.body = JSON.stringify(body);
|
||||
}
|
||||
|
||||
const response = await fetch(`${API_BASE}${path}`, options);
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json().catch(() => ({ message: 'Request failed' }));
|
||||
throw new Error(error.message || `HTTP ${response.status}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
get(path) { return this.request('GET', path); }
|
||||
post(path, body) { return this.request('POST', path, body); }
|
||||
put(path, body) { return this.request('PUT', path, body); }
|
||||
delete(path) { return this.request('DELETE', path); }
|
||||
|
||||
// Auth
|
||||
async login(token) {
|
||||
try {
|
||||
const data = await this.get('/auth/verify');
|
||||
this.setToken(token);
|
||||
return data;
|
||||
} catch (e) {
|
||||
this.setToken(null);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// Documents
|
||||
getDocuments(params = {}) {
|
||||
const query = new URLSearchParams(params).toString();
|
||||
return this.get(`/documents${query ? '?' + query : ''}`);
|
||||
}
|
||||
|
||||
getDocument(id) {
|
||||
return this.get(`/documents/${id}`);
|
||||
}
|
||||
|
||||
createDocument(data) {
|
||||
return this.post('/documents', data);
|
||||
}
|
||||
|
||||
updateDocument(id, data) {
|
||||
return this.put(`/documents/${id}`, data);
|
||||
}
|
||||
|
||||
deleteDocument(id) {
|
||||
return this.delete(`/documents/${id}`);
|
||||
}
|
||||
|
||||
exportDocument(id) {
|
||||
return fetch(`${API_BASE}/documents/${id}/export`, {
|
||||
headers: this.getHeaders()
|
||||
}).then(r => r.text());
|
||||
}
|
||||
|
||||
// Libraries
|
||||
getLibraries() {
|
||||
return this.get('/libraries');
|
||||
}
|
||||
|
||||
getLibrary(id) {
|
||||
return this.get(`/libraries/${id}`);
|
||||
}
|
||||
|
||||
createLibrary(data) {
|
||||
return this.post('/libraries', data);
|
||||
}
|
||||
|
||||
updateLibrary(id, data) {
|
||||
return this.put(`/libraries/${id}`, data);
|
||||
}
|
||||
|
||||
deleteLibrary(id) {
|
||||
return this.delete(`/libraries/${id}`);
|
||||
}
|
||||
|
||||
// Tags
|
||||
getTags() {
|
||||
return this.get('/tags');
|
||||
}
|
||||
}
|
||||
|
||||
export const api = new ApiClient();
|
||||
Reference in New Issue
Block a user