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

207 lines
6.5 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.
/**
* Activity-List Component (Alpine.js)
*
* Used in: activities.html
*
* Features:
* - Paginated table of activities
* - Filter by type, owner, overdue, completed
* - "+ New Activity" modal that POSTs to /api/v1/activities/
* - Row click opens a detail modal with a "Complete" button
*
* Backend: GET /api/v1/activities/?skip=&limit=&type=&owner_id=&overdue=&completed=
* POST /api/v1/activities/
* PATCH /api/v1/activities/{id}/complete
*
* R-5: x-text only. Even activity bodies go through x-text, never x-html.
*/
import { api } from '/js/api.js';
export const ACTIVITY_TYPES = ['call', 'email', 'meeting', 'task', 'note'];
export const TYPE_BADGE = {
call: 'badge-blue',
email: 'badge-yellow',
meeting: 'badge-green',
task: 'badge-gray',
note: 'badge-gray',
};
export function activityList() {
return {
// State
activities: [],
total: 0,
skip: 0,
limit: 20,
type: '',
ownerId: '',
overdue: '', // '', 'true', 'false'
completed: '', // '', 'true', 'false'
loading: false,
error: null,
// Create-modal state
showCreate: false,
createForm: this._initForm(),
createError: '',
creating: false,
// Detail-modal state
showDetail: false,
selected: null,
completing: false,
// Lookups
types: ACTIVITY_TYPES.slice(),
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.type) params.set('type', this.type);
if (this.ownerId) params.set('owner_id', this.ownerId);
if (this.overdue) params.set('overdue', this.overdue);
if (this.completed) params.set('completed', this.completed);
const res = await api.get(`/activities/?${params.toString()}`);
this.activities = Array.isArray(res) ? res : (res && res.items) || [];
this.total = (res && res.total) || this.activities.length;
} catch (err) {
this.error = err.message || 'Fehler beim Laden der Activities.';
this._notify('error', this.error);
} finally {
this.loading = false;
}
},
async applyFilters() {
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}`;
},
// === Row helpers ===
isOverdue(a) {
if (a.completed_at) return false;
if (!a.due_date) return false;
return new Date(a.due_date) < new Date();
},
isCompleted(a) { return !!a.completed_at; },
typeBadge(t) { return TYPE_BADGE[t] || 'badge-gray'; },
formatDateTime(iso) {
if (!iso) return '';
try { return new Date(iso).toLocaleString('de-DE'); }
catch { return iso; }
},
// === Create modal ===
openCreate() {
this.createForm = this._initForm();
this.createError = '';
this.showCreate = true;
},
closeCreate() { this.showCreate = false; },
async submitCreate() {
this.creating = true;
this.createError = '';
try {
const payload = {
type: this.createForm.type,
subject: this.createForm.subject,
};
if (this.createForm.body) payload.body = this.createForm.body;
if (this.createForm.due_date) payload.due_date = this.createForm.due_date;
if (this.createForm.account_id) payload.account_id = Number(this.createForm.account_id);
if (this.createForm.contact_id) payload.contact_id = Number(this.createForm.contact_id);
if (this.createForm.deal_id) payload.deal_id = Number(this.createForm.deal_id);
await api.post('/activities/', payload);
this._notify('success', 'Activity angelegt.');
this.showCreate = false;
await this.load();
} catch (err) {
this.createError = err.message || 'Fehler beim Anlegen.';
} finally {
this.creating = false;
}
},
// === Detail / Complete ===
openDetail(a) {
this.selected = a;
this.showDetail = true;
},
closeDetail() {
this.showDetail = false;
this.selected = null;
},
async completeSelected() {
if (!this.selected) return;
this.completing = true;
try {
await api.patch(`/activities/${this.selected.id}/complete`, { outcome: '' });
this._notify('success', 'Activity abgeschlossen.');
this.closeDetail();
await this.load();
} catch (err) {
this._notify('error', err.message || 'Fehler beim Abschließen.');
} finally {
this.completing = false;
}
},
// === Internals ===
_initForm() {
return {
type: 'task',
subject: '',
body: '',
due_date: '',
account_id: '',
contact_id: '',
deal_id: '',
};
},
_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('activityList', activityList);
}
});
}
export default activityList;