Improve employee and partner engagement workspace

This commit is contained in:
A R R R Associates
2026-09-02 12:31:27 +05:30
parent fbb9a62eb8
commit 2a4fd2bfdd
6 changed files with 259 additions and 1 deletions
@@ -0,0 +1,157 @@
{% set resources = resources or {} %}
<section class="rounded-2xl border border-slate-200 bg-white shadow-soft">
<div class="border-b border-slate-200 px-5 py-4">
<div class="flex flex-wrap items-start justify-between gap-3">
<div>
<h3 class="font-semibold text-slate-900">Engagement Data & Documents</h3>
<p class="mt-1 text-sm text-slate-500">Tally/accounting storage and engagement files are available here before the task checklist.</p>
</div>
<a href="/documents/engagements/{{ engagement_id }}" class="rounded-xl border border-slate-300 px-3 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50">Open Full Document Register</a>
</div>
</div>
<div class="grid gap-4 p-5 lg:grid-cols-[minmax(0,1fr)_minmax(360px,0.8fr)]">
<div class="space-y-4">
<div class="rounded-2xl border border-indigo-200 bg-indigo-50 p-4">
<div class="flex flex-wrap items-start justify-between gap-3">
<div>
<div class="text-xs font-semibold uppercase tracking-wide text-indigo-600">Tally / Accounting Data</div>
<div class="mt-1 font-semibold text-indigo-950">Client accounting .act path</div>
</div>
{% if resources.storage_node_name %}<span class="rounded-full bg-white px-2.5 py-1 text-xs font-semibold text-indigo-700">{{ resources.storage_node_name }}</span>{% endif %}
</div>
{% if resources.accounting_local_path %}
<div class="mt-3 flex gap-2">
<input id="engagement-accounting-path" readonly value="{{ resources.accounting_local_path }}" class="min-w-0 flex-1 rounded-xl border border-indigo-200 bg-white px-3 py-2 font-mono text-xs text-slate-700">
<button type="button" data-copy-target="engagement-accounting-path" class="rounded-xl bg-indigo-600 px-3 py-2 text-sm font-semibold text-white hover:bg-indigo-700">Copy Path</button>
</div>
<p class="mt-2 text-xs text-indigo-700">This follows the existing branch Storage Node → FY → Client → Accounting convention used by the Local Agent.</p>
{% elif resources.accounting_relative_path %}
<div class="mt-3 rounded-xl border border-indigo-200 bg-white px-3 py-2 font-mono text-xs text-slate-700">{{ resources.accounting_relative_path }}</div>
<p class="mt-2 text-xs text-amber-700">The relative accounting path is known, but this branch does not currently expose a configured local storage root.</p>
{% else %}
<p class="mt-3 text-sm text-slate-600">Accounting path is unavailable for this engagement.</p>
{% endif %}
</div>
<div>
<div class="mb-2 flex items-center justify-between gap-3">
<div class="text-sm font-semibold text-slate-900">Current Engagement Files</div>
<span class="rounded-full bg-slate-100 px-2.5 py-1 text-xs font-semibold text-slate-600">{{ resources.document_options|length }}</span>
</div>
<div class="max-h-72 space-y-2 overflow-y-auto pr-1">
{% for doc in resources.document_options %}
<div class="rounded-xl border border-slate-200 p-3">
<div class="flex flex-wrap items-start justify-between gap-2">
<div class="min-w-0">
<div class="truncate text-sm font-semibold text-slate-900">{{ doc.title }}</div>
<div class="mt-1 text-xs text-slate-500">{{ doc.document_type|replace('_',' ')|title }} · v{{ doc.version_no }}{% if doc.latest_filename %} · {{ doc.latest_filename }}{% endif %}</div>
</div>
{% if doc.storage_status %}<span class="rounded-full bg-slate-100 px-2 py-1 text-[11px] font-semibold text-slate-600">{{ doc.storage_status|replace('_',' ')|title }}</span>{% endif %}
</div>
{% if doc.latest_relative_path %}<div class="mt-2 break-all font-mono text-[11px] text-slate-400">{{ doc.latest_relative_path }}</div>{% endif %}
</div>
{% else %}
<div class="rounded-xl border border-dashed border-slate-300 p-5 text-center text-sm text-slate-500">No engagement documents uploaded yet.</div>
{% endfor %}
</div>
</div>
</div>
<div class="rounded-2xl border border-slate-200 bg-slate-50 p-4">
<div class="text-sm font-semibold text-slate-900">Upload from Engagement Page</div>
<p class="mt-1 text-xs text-slate-500">Choose the task category so the file remains linked to the existing task/evidence workflow.</p>
{% if resources.default_upload_task_id %}
<form method="post" enctype="multipart/form-data" action="/documents/tasks/{{ resources.default_upload_task_id }}/upload" class="mt-4 space-y-3" data-engagement-upload-form>
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<input type="hidden" name="return_to" value="{{ return_to }}">
<div>
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Task Category</label>
<select class="w-full rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm" data-upload-task-select>
{% for category in resources.task_categories %}<option value="{{ category.task_id }}">{{ category.name }}</option>{% endfor %}
</select>
</div>
<div>
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Upload Action</label>
<select class="w-full rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm" data-upload-mode>
<option value="new">New Document</option>
<option value="replace">Replace Current Document</option>
<option value="version">Add Another Version</option>
</select>
</div>
<div data-existing-document-wrap class="hidden">
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Existing Document</label>
<select name="existing_document_id" class="w-full rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm" data-existing-document-select>
<option value="">Select document</option>
{% for doc in resources.document_options %}<option value="{{ doc.id }}">{{ doc.title }} · v{{ doc.version_no }}</option>{% endfor %}
</select>
<p class="mt-1 text-[11px] text-slate-500">Replacement/version uploads use the existing document container and retain the ERP audit/version history.</p>
</div>
<div class="grid gap-3 sm:grid-cols-2">
<div>
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Document Type</label>
<select name="document_type" class="w-full rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm">
{% for item in ['GENERAL','CLIENT_DOCUMENT','WORKING_PAPER','BANK_STATEMENT','TRIAL_BALANCE','LEDGER','GST_RETURN','INCOME_TAX','ROC','AUDIT_REPORT','SIGNED_OUTPUT','ACKNOWLEDGEMENT'] %}<option value="{{ item }}">{{ item|replace('_',' ')|title }}</option>{% endfor %}
</select>
</div>
<div>
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Title</label>
<input name="title" class="w-full rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm" placeholder="Defaults to file name">
</div>
</div>
<div>
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">File</label>
<input type="file" name="file" required class="block w-full rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm">
</div>
<div>
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Description / Remarks</label>
<input name="remarks" class="w-full rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm" placeholder="Optional">
</div>
<button class="w-full rounded-xl bg-brand-600 px-4 py-2.5 text-sm font-semibold text-white hover:bg-brand-700">Upload Document</button>
</form>
{% else %}
<div class="mt-4 rounded-xl border border-dashed border-slate-300 bg-white p-5 text-center text-sm text-slate-500">No task is available to receive an engagement upload.</div>
{% endif %}
</div>
</div>
</section>
<script>
(function () {
const form = document.querySelector('[data-engagement-upload-form]');
if (form) {
const taskSelect = form.querySelector('[data-upload-task-select]');
const mode = form.querySelector('[data-upload-mode]');
const wrap = form.querySelector('[data-existing-document-wrap]');
const existing = form.querySelector('[data-existing-document-select]');
const updateAction = function () {
form.action = '/documents/tasks/' + taskSelect.value + '/upload';
};
const updateMode = function () {
const useExisting = mode.value !== 'new';
wrap.classList.toggle('hidden', !useExisting);
existing.required = useExisting;
if (!useExisting) existing.value = '';
};
taskSelect.addEventListener('change', updateAction);
mode.addEventListener('change', updateMode);
updateAction();
updateMode();
}
document.querySelectorAll('[data-copy-target]').forEach(function (button) {
button.addEventListener('click', async function () {
const field = document.getElementById(button.getAttribute('data-copy-target'));
if (!field) return;
try {
await navigator.clipboard.writeText(field.value);
const oldText = button.textContent;
button.textContent = 'Copied';
setTimeout(function () { button.textContent = oldText; }, 1200);
} catch (err) {
field.select();
document.execCommand('copy');
}
});
});
})();
</script>