All checks were successful
Deploy baustelle-pwa / deploy (push) Successful in 1s
Mobile Progressive Web App für Baustellen-Doku, spricht die REST-API des Dolibarr-Bericht-Moduls. MVP-Features: - Vanilla JavaScript, kein Build-Step nötig - Login mit Dolibarr-Credentials → JWT (7 Tage) - Auftragsliste mit Suche und Multi-User-Filter - Auftragsdetail mit Kunde, Adresse, Click-to-Call - Foto-Aufnahme via Kamera oder Galerie (multiple) - Clientseitige Bildverkleinerung (max 2000px, JPEG q=0.85) - Offline-Queue in IndexedDB für Uploads ohne Netz - Auto-Sync bei Online-Event mit Status-Badge - Service Worker für App-Shell-Cache - PWA-installierbar (Manifest, Icons, Theme-Color) Hosting: awl.data-it-solution.de/baustelle/ via Apache-Alias Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
92 lines
2.8 KiB
JavaScript
92 lines
2.8 KiB
JavaScript
/* API-Client für die Bericht-REST-API */
|
|
(function () {
|
|
// API-Base wird aus der aktuellen Origin gebaut.
|
|
// PWA läuft unter /baustelle/, API unter /custom/bericht/api/
|
|
const API_BASE = window.location.origin + '/custom/bericht/api';
|
|
|
|
let cachedToken = null;
|
|
|
|
async function getToken() {
|
|
if (cachedToken) return cachedToken;
|
|
cachedToken = await idb.get('jwt');
|
|
return cachedToken;
|
|
}
|
|
|
|
async function setToken(t) {
|
|
cachedToken = t;
|
|
await idb.set('jwt', t);
|
|
}
|
|
|
|
async function clearToken() {
|
|
cachedToken = null;
|
|
await idb.del('jwt');
|
|
await idb.del('user');
|
|
}
|
|
|
|
async function request(path, opts = {}) {
|
|
const t = await getToken();
|
|
const headers = opts.headers || {};
|
|
if (t && !headers.Authorization) headers.Authorization = 'Bearer ' + t;
|
|
if (!headers['Content-Type'] && !(opts.body instanceof FormData)) {
|
|
headers['Content-Type'] = 'application/json';
|
|
}
|
|
const r = await fetch(API_BASE + path, { ...opts, headers });
|
|
if (r.status === 401) {
|
|
await clearToken();
|
|
window.location.hash = '#/login';
|
|
throw new Error('Nicht authentifiziert');
|
|
}
|
|
const data = await r.json().catch(() => ({}));
|
|
if (!r.ok) throw new Error(data.error || 'API-Fehler');
|
|
return data;
|
|
}
|
|
|
|
async function login(loginName, password) {
|
|
const r = await request('/auth.php', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ login: loginName, password }),
|
|
});
|
|
await setToken(r.token);
|
|
await idb.set('user', r.user);
|
|
return r;
|
|
}
|
|
|
|
async function logout() {
|
|
await clearToken();
|
|
}
|
|
|
|
async function listOrders(opts = {}) {
|
|
const params = new URLSearchParams();
|
|
if (opts.q) params.set('q', opts.q);
|
|
if (opts.open) params.set('open', '1');
|
|
const qs = params.toString();
|
|
return request('/orders.php' + (qs ? '?' + qs : ''));
|
|
}
|
|
|
|
async function getOrder(id) {
|
|
return request('/orders.php?id=' + id);
|
|
}
|
|
|
|
async function listOrderPhotos(id) {
|
|
return request('/orders.php?id=' + id + '&action=photos');
|
|
}
|
|
|
|
async function uploadOrderPhoto(orderId, fileBlob, filename) {
|
|
const fd = new FormData();
|
|
fd.append('file', fileBlob, filename || 'photo.jpg');
|
|
return request('/orders.php?id=' + orderId + '&action=upload_photo', {
|
|
method: 'POST',
|
|
body: fd,
|
|
});
|
|
}
|
|
|
|
async function getReport(id) {
|
|
return request('/reports.php?id=' + id);
|
|
}
|
|
|
|
window.api = {
|
|
getToken, setToken, clearToken,
|
|
login, logout,
|
|
listOrders, getOrder, listOrderPhotos, uploadOrderPhoto, getReport,
|
|
};
|
|
})();
|