thatcher 1.0.49 → 1.0.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/ui/grid-view-renderer.js +49 -3
- package/src/ui/page-handler.js +15 -1
package/package.json
CHANGED
|
@@ -25,12 +25,58 @@ function gridRow(entityName, item, columns) {
|
|
|
25
25
|
const cells = columns.map(([key, field]) => {
|
|
26
26
|
const value = item[key];
|
|
27
27
|
const rendered = fmtVal(value, key, item);
|
|
28
|
-
const
|
|
29
|
-
|
|
28
|
+
const editableAttrs = isEditable(field)
|
|
29
|
+
? ` data-editable="${esc(key)}" data-entity="${esc(entityName)}" data-record-id="${esc(item.id)}"`
|
|
30
|
+
: '';
|
|
31
|
+
return `<td data-col="${esc(key)}"${editableAttrs}>${rendered}</td>`;
|
|
30
32
|
}).join('');
|
|
31
33
|
return `<tr data-row data-navigate="/${esc(entityName)}/${esc(item.id)}" style="cursor:pointer">${cells}</tr>`;
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
const GRID_EDIT_SCRIPT = `(function(){
|
|
37
|
+
function commitCell(td){
|
|
38
|
+
var input=td.querySelector('input,select');
|
|
39
|
+
if(!input)return;
|
|
40
|
+
var value=input.value;
|
|
41
|
+
var field=td.dataset.editable, entity=td.dataset.entity, id=td.dataset.recordId;
|
|
42
|
+
var original=td.dataset.originalHtml;
|
|
43
|
+
fetch('/api/'+entity+'/'+id,{method:'PATCH',headers:{'Content-Type':'application/json'},body:JSON.stringify({[field]:value})})
|
|
44
|
+
.then(function(r){if(!r.ok)throw new Error('Save failed');return r.json()})
|
|
45
|
+
.then(function(){location.reload()})
|
|
46
|
+
.catch(function(err){td.innerHTML=original;if(window.showToast)showToast(err.message,'error')});
|
|
47
|
+
}
|
|
48
|
+
function cancelCell(td){
|
|
49
|
+
var original=td.dataset.originalHtml;
|
|
50
|
+
if(original!==undefined)td.innerHTML=original;
|
|
51
|
+
}
|
|
52
|
+
document.addEventListener('dblclick',function(e){
|
|
53
|
+
var td=e.target.closest('[data-editable]');
|
|
54
|
+
if(!td||td.querySelector('input,select'))return;
|
|
55
|
+
e.stopPropagation();
|
|
56
|
+
var field=td.dataset.editable;
|
|
57
|
+
var current=(td.textContent||'').trim();
|
|
58
|
+
td.dataset.originalHtml=td.innerHTML;
|
|
59
|
+
var input=document.createElement('input');
|
|
60
|
+
input.type='text';
|
|
61
|
+
input.value=current;
|
|
62
|
+
input.className='grid-cell-input';
|
|
63
|
+
td.innerHTML='';
|
|
64
|
+
td.appendChild(input);
|
|
65
|
+
input.focus();
|
|
66
|
+
input.select();
|
|
67
|
+
input.addEventListener('blur',function(){commitCell(td)});
|
|
68
|
+
input.addEventListener('keydown',function(ke){
|
|
69
|
+
if(ke.key==='Enter'){ke.preventDefault();input.blur()}
|
|
70
|
+
else if(ke.key==='Escape'){ke.preventDefault();cancelCell(td)}
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
document.addEventListener('click',function(e){
|
|
74
|
+
if(e.target.closest('[data-editable]')&&e.target.closest('[data-editable]').querySelector('input,select')){
|
|
75
|
+
e.stopPropagation();
|
|
76
|
+
}
|
|
77
|
+
},true);
|
|
78
|
+
})();`;
|
|
79
|
+
|
|
34
80
|
export function renderGridView(user, entityName, spec, records, options = {}) {
|
|
35
81
|
const label = getEntityLabel(spec, true) || entityName;
|
|
36
82
|
const columns = getColumns(spec);
|
|
@@ -74,5 +120,5 @@ export function renderGridView(user, entityName, spec, records, options = {}) {
|
|
|
74
120
|
</table>
|
|
75
121
|
</div>`;
|
|
76
122
|
|
|
77
|
-
return page(user, `${label} | Thatcher`, null, content, [TABLE_SCRIPT]);
|
|
123
|
+
return page(user, `${label} | Thatcher`, null, content, [TABLE_SCRIPT, GRID_EDIT_SCRIPT]);
|
|
78
124
|
}
|
package/src/ui/page-handler.js
CHANGED
|
@@ -6,6 +6,9 @@ import { renderLogin, renderDashboard, renderAccessDenied, renderPasswordReset,
|
|
|
6
6
|
import { renderClientDashboard, renderClientList } from '@/ui/client-renderer.js';
|
|
7
7
|
import { canList, canView, canCreate, canEdit, isPartner, isClerk, isClientUser, canClientAccessEntity } from '@/ui/permissions-ui.js';
|
|
8
8
|
import { renderEngagementGrid } from '@/ui/engagement-grid-renderer.js';
|
|
9
|
+
import { renderBoardView } from '@/ui/board-view-renderer.js';
|
|
10
|
+
import { renderGridView } from '@/ui/grid-view-renderer.js';
|
|
11
|
+
import { renderCalendarView, renderTimelineView } from '@/ui/calendar-view-renderer.js';
|
|
9
12
|
import { renderClientProgress } from '@/ui/client-progress-renderer.js';
|
|
10
13
|
import { renderLetterWorkflow } from '@/ui/letter-workflow-renderer.js';
|
|
11
14
|
import { renderAdvancedSearch } from '@/ui/advanced-search-renderer.js';
|
|
@@ -220,8 +223,19 @@ export async function handlePage(pathname, req, res) {
|
|
|
220
223
|
if (!canList(user, entityName)) return renderAccessDenied(user, entityName, 'list');
|
|
221
224
|
let items = await list(entityName, {});
|
|
222
225
|
if (isClientUser(user) && user.client_id) items = items.filter(item => { if (item.client_id) return item.client_id === user.client_id; if (item.assigned_to) return item.assigned_to === user.id; return true; });
|
|
226
|
+
items = resolveRefFields(items, spec);
|
|
227
|
+
const view = reqUrl(req).searchParams.get('view');
|
|
228
|
+
if (view === 'board') return renderBoardView(user, entityName, spec, items);
|
|
229
|
+
if (view === 'grid') return renderGridView(user, entityName, spec, items);
|
|
230
|
+
if (view === 'calendar') {
|
|
231
|
+
const params = reqUrl(req).searchParams;
|
|
232
|
+
const month = params.has('month') ? Number(params.get('month')) : undefined;
|
|
233
|
+
const year = params.has('year') ? Number(params.get('year')) : undefined;
|
|
234
|
+
return renderCalendarView(user, entityName, spec, items, { month, year });
|
|
235
|
+
}
|
|
236
|
+
if (view === 'timeline') return renderTimelineView(user, entityName, spec, items);
|
|
223
237
|
const { renderEntityList: lazyEntityList } = await lazyRenderer('entity-renderer.js');
|
|
224
|
-
return lazyEntityList(entityName,
|
|
238
|
+
return lazyEntityList(entityName, items, spec, user);
|
|
225
239
|
}
|
|
226
240
|
if (segments.length === 2 && segments[0] === 'client' && segments[1] !== 'new') {
|
|
227
241
|
if (!canView(user, 'client')) return renderAccessDenied(user, 'client', 'view');
|