From 8b9a77290ee03dd872137e16b9f720043261e8b0 Mon Sep 17 00:00:00 2001 From: Leopoldadmin Date: Thu, 4 Jun 2026 00:06:27 +0000 Subject: [PATCH] Upload app/webui/components/account-list.js --- app/webui/components/account-list.js | 149 +++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 app/webui/components/account-list.js diff --git a/app/webui/components/account-list.js b/app/webui/components/account-list.js new file mode 100644 index 0000000..e9358a3 --- /dev/null +++ b/app/webui/components/account-list.js @@ -0,0 +1,149 @@ +/** + * Account-List Component (Alpine.js) + * + * Used in: accounts.html + * + * Features: + * - Paginated table of accounts + * - Search by name (?q=) + * - Filter by industry (?industry=) and size (?size=) + * - "+ New Account" modal that POSTs to /api/v1/accounts/ + * - Row click → /accounts-detail.html?id=… + * + * Backend: GET /api/v1/accounts/?skip=&limit=&q=&industry=&size=&owner_id= + * POST /api/v1/accounts/ → AccountOut + * + * R-5: x-text only. All user-controlled strings go through x-text or + * textContent (never x-html / innerHTML). + */ + +import { api } from '/js/api.js'; + +export function accountList() { + return { + // State + accounts: [], + total: 0, + skip: 0, + limit: 20, + q: '', + industry: '', + size: '', + ownerId: '', + loading: false, + error: null, + + // Create-modal state + showCreate: false, + createForm: this._initCreateForm(), + createError: '', + creating: false, + + // Lookup values (could be hard-coded; kept here for clarity) + industries: ['technology', 'finance', 'healthcare', 'retail', 'manufacturing', 'other'], + sizes: ['startup', 'smb', 'midmarket', 'enterprise'], + + async init() { + await this.load(); + }, + + async load() { + this.loading = true; + this.error = null; + try { + const params = new URLSearchParams(); + params.set('skip', String(this.skip)); + params.set('limit', String(this.limit)); + if (this.q) params.set('q', this.q); + if (this.industry) params.set('industry', this.industry); + if (this.size) params.set('size', this.size); + if (this.ownerId) params.set('owner_id', this.ownerId); + + const res = await api.get(`/accounts/?${params.toString()}`); + this.accounts = Array.isArray(res) ? res : (res && res.items) || []; + this.total = (res && res.total) || this.accounts.length; + } catch (err) { + this.error = err.message || 'Fehler beim Laden der Accounts.'; + this._notify('error', this.error); + } finally { + this.loading = false; + } + }, + + async search() { + this.skip = 0; + await this.load(); + }, + + async nextPage() { + if (this.skip + this.limit < this.total) { + this.skip += this.limit; + await this.load(); + } + }, + + async prevPage() { + if (this.skip - this.limit >= 0) { + this.skip -= this.limit; + await this.load(); + } + }, + + get pageInfo() { + const from = this.total === 0 ? 0 : this.skip + 1; + const to = Math.min(this.skip + this.limit, this.total); + return `${from}–${to} von ${this.total}`; + }, + + // === Create modal === + openCreate() { + this.createForm = this._initCreateForm(); + this.createError = ''; + this.showCreate = true; + }, + closeCreate() { + this.showCreate = false; + }, + async submitCreate() { + this.creating = true; + this.createError = ''; + try { + const payload = { + name: this.createForm.name, + }; + if (this.createForm.website) payload.website = this.createForm.website; + if (this.createForm.industry) payload.industry = this.createForm.industry; + if (this.createForm.size) payload.size = this.createForm.size; + + await api.post('/accounts/', payload); + this._notify('success', 'Account angelegt.'); + this.showCreate = false; + await this.load(); + } catch (err) { + this.createError = err.message || 'Fehler beim Anlegen.'; + } finally { + this.creating = false; + } + }, + + // === Internals === + _initCreateForm() { + return { name: '', website: '', industry: '', size: '' }; + }, + _notify(type, message) { + if (window.Alpine) { + Alpine.store('notifications').push(message, type); + } + }, + }; +} + +if (typeof window !== 'undefined') { + document.addEventListener('alpine:init', () => { + if (window.Alpine) { + window.Alpine.data('accountList', accountList); + } + }); +} + +export default accountList;