Files
crm-system/app/webui/components/account-list.js
T

150 lines
4.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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;