Files
TaskTracker/TaskTracker.Api/wwwroot/js/pages/mappings.js
AJ Isaacs e12f78c479 chore: initial commit of TaskTracker project
Existing ASP.NET API with vanilla JS SPA, WindowWatcher, Chrome extension, and MCP server.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 22:08:45 -05:00

120 lines
5.4 KiB
JavaScript

import * as api from '../api.js';
import { showModal } from '../components/modal.js';
const el = () => document.getElementById('page-mappings');
export async function initMappings() {
el().innerHTML = `
<h1 class="page-title">App Mappings</h1>
<div class="flex-between mb-16">
<div class="text-muted text-sm">Map process names, window titles, or URLs to categories</div>
<button class="btn btn-primary" id="btn-new-mapping">+ New Mapping</button>
</div>
<div id="mapping-list" class="card table-wrap"></div>`;
document.getElementById('btn-new-mapping').addEventListener('click', () => showMappingForm());
await loadMappings();
}
async function loadMappings() {
try {
const mappings = await api.mappings.list();
const container = document.getElementById('mapping-list');
if (!mappings || !mappings.length) {
container.innerHTML = `<div class="empty-state">No mappings configured</div>`;
return;
}
container.innerHTML = `
<table>
<thead><tr><th>Pattern</th><th>Match Type</th><th>Category</th><th>Friendly Name</th><th>Actions</th></tr></thead>
<tbody>
${mappings.map(m => `
<tr>
<td><code>${esc(m.pattern)}</code></td>
<td><span class="badge badge-pending">${m.matchType}</span></td>
<td>${esc(m.category)}</td>
<td>${esc(m.friendlyName) || '<span class="text-muted">-</span>'}</td>
<td>
<div class="btn-group">
<button class="btn btn-sm" data-edit="${m.id}">Edit</button>
<button class="btn btn-sm btn-danger" data-delete="${m.id}">Delete</button>
</div>
</td>
</tr>`).join('')}
</tbody>
</table>`;
container.querySelectorAll('[data-edit]').forEach(btn => {
btn.addEventListener('click', () => {
const m = mappings.find(x => x.id === parseInt(btn.dataset.edit));
if (m) showMappingForm(m);
});
});
container.querySelectorAll('[data-delete]').forEach(btn => {
btn.addEventListener('click', () => confirmDelete(parseInt(btn.dataset.delete)));
});
} catch (e) {
document.getElementById('mapping-list').innerHTML = `<div class="empty-state">Failed to load mappings</div>`;
}
}
function showMappingForm(existing = null) {
const title = existing ? 'Edit Mapping' : 'New Mapping';
showModal(title, `
<div class="form-group">
<label class="form-label">Pattern *</label>
<input type="text" class="form-input" id="map-pattern" value="${esc(existing?.pattern || '')}">
</div>
<div class="form-group">
<label class="form-label">Match Type *</label>
<select class="form-select" id="map-match-type">
<option value="ProcessName" ${existing?.matchType === 'ProcessName' ? 'selected' : ''}>Process Name</option>
<option value="TitleContains" ${existing?.matchType === 'TitleContains' ? 'selected' : ''}>Title Contains</option>
<option value="UrlContains" ${existing?.matchType === 'UrlContains' ? 'selected' : ''}>URL Contains</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Category *</label>
<input type="text" class="form-input" id="map-category" value="${esc(existing?.category || '')}">
</div>
<div class="form-group">
<label class="form-label">Friendly Name</label>
<input type="text" class="form-input" id="map-friendly" value="${esc(existing?.friendlyName || '')}">
</div>`,
[
{ label: 'Cancel', onClick: () => {} },
{
label: existing ? 'Save' : 'Create', cls: 'btn-primary', onClick: async (modal) => {
const pattern = modal.querySelector('#map-pattern').value.trim();
const matchType = modal.querySelector('#map-match-type').value;
const category = modal.querySelector('#map-category').value.trim();
const friendlyName = modal.querySelector('#map-friendly').value.trim() || null;
if (!pattern || !category) { alert('Pattern and Category are required'); throw new Error('cancel'); }
const body = { pattern, matchType, category, friendlyName };
if (existing) await api.mappings.update(existing.id, body);
else await api.mappings.create(body);
loadMappings();
},
},
]);
setTimeout(() => document.getElementById('map-pattern')?.focus(), 100);
}
function confirmDelete(id) {
showModal('Delete Mapping', `<p>Are you sure you want to delete this mapping?</p>`, [
{ label: 'Cancel', onClick: () => {} },
{
label: 'Delete', cls: 'btn-danger', onClick: async () => {
await api.mappings.remove(id);
loadMappings();
},
},
]);
}
function esc(str) {
if (!str) return '';
const d = document.createElement('div');
d.textContent = str;
return d.innerHTML;
}