CRUD operations with search, filter, pagination, and form validation
import { signal, computed, effect } from 'tina4js';
import { api } from 'tina4js/api';
// Configure API
api.configure({
baseUrl: '/api/v1',
auth: true,
headers: { 'X-App': 'contacts' }
});
// State
const contacts = signal([]);
const search = signal('');
const deptFilter = signal('');
const page = signal(1);
const perPage = 5;
// Filtered + paginated (computed)
const filtered = computed(() =>
contacts.value
.filter(c => !search.value || c.name.toLowerCase().includes(search.value.toLowerCase()))
.filter(c => !deptFilter.value || c.department === deptFilter.value)
);
const paged = computed(() => {
const start = (page.value - 1) * perPage;
return filtered.value.slice(start, start + perPage);
});
// CRUD
async function loadContacts() {
const data = await api.get('/contacts');
contacts.value = data;
}
async function createContact(c) {
const created = await api.post('/contacts', c);
contacts.value = [...contacts.value, created];
}
async function deleteContact(id) {
await api.delete(`/contacts/${id}`);
contacts.value = contacts.value.filter(c => c.id !== id);
}
// Render reacts to signal changes
effect(() => renderTable(paged.value));