Compare commits

...

2 Commits

Author SHA1 Message Date
8dd7ae8c95 feat(dashboard): add admin-only Last Synced column
Adds a "Last Synced" column visible only to admins, positioned as the
last column. Displays the sync timestamp formatted as YYYY-MM-DD.
Includes the column in the visibility toggle modal.
2026-05-12 23:57:26 -07:00
616ffde402 fix: exclude archived projects from oldest-percent sync selection
Archived projects should not count toward the N% fraction nor be selected for syncing.
2026-05-12 23:44:52 -07:00
3 changed files with 20 additions and 5 deletions

3
app.py
View File

@@ -278,7 +278,8 @@ def dashboard(page=1):
current_page=page,
total_pages=total_pages,
total_projects=total_projects,
per_page=per_page)
per_page=per_page,
is_admin=is_admin)

View File

@@ -353,15 +353,18 @@ def get_oldest_unsynced_projects(db, fraction: float = 0.2) -> List[int]:
try:
projects_ref = db.collection("projects")
all_docs = list(projects_ref.stream())
total = len(all_docs)
# Exclude archived projects from the sync pool
active_docs = [doc for doc in all_docs if not doc.to_dict().get("is_archived")]
total = len(active_docs)
count_to_sync = max(1, int(total * fraction))
# Sort by last_synced_at ascending (empty strings first, then oldest timestamps)
sorted_docs = sorted(all_docs, key=lambda doc: doc.to_dict().get("last_synced_at", ""))
sorted_docs = sorted(active_docs, key=lambda doc: doc.to_dict().get("last_synced_at", ""))
selected_docs = sorted_docs[:count_to_sync]
result_ids = [int(doc.id) for doc in selected_docs if doc.id and doc.id != "None"]
print(f"[SYNC STRATEGY] {total} projects in Firestore, will sync oldest {len(result_ids)} ({fraction*100:.0f}%)")
print(f"[SYNC STRATEGY] {total} active projects in Firestore, will sync oldest {len(result_ids)} ({fraction*100:.0f}%)")
if selected_docs:
sample = selected_docs[0].to_dict()
print(f"[SYNC STRATEGY] Oldest: ID={result_ids[0]}, last_synced_at='{sample.get('last_synced_at', 'N/A')}'")

View File

@@ -167,6 +167,9 @@
<th style="background-color: rgb(89, 121, 142);" class="px-4 py-3 w-32 sticky top-0 z-[40]" :class="{'hidden': !isColumnVisible('Date Possession Recovered')}">Date Possession Recovered</th>
<th style="background-color: rgb(89, 121, 142);" class="px-4 py-3 w-32 sticky top-0 z-[40]" :class="{'hidden': !isColumnVisible('Attorney\'s Fees')}">Attorney's Fees</th>
<th style="background-color: rgb(89, 121, 142);" class="px-4 py-3 w-32 sticky top-0 z-[40]" :class="{'hidden': !isColumnVisible('Costs')}">Costs</th>
{% if is_admin %}
<th style="background-color: rgb(89, 121, 142);" class="px-4 py-3 w-32 sticky top-0 z-[40]" :class="{'hidden': !isColumnVisible('Last Synced')}">Last Synced</th>
{% endif %}
</tr>
</thead>
<tbody class="bg-slate-100 divide-y divide-slate-300">
@@ -542,6 +545,13 @@
{{ r.costs }}
{% endcall %}
</td>
{% if is_admin %}
<td class="px-4 py-3 text-sm" :class="{'hidden': !isColumnVisible('Last Synced')}">
{% call expander() %}
{% if r.last_synced_at %}{{ r.last_synced_at.split('T')[0] }}{% endif %}
{% endcall %}
</td>
{% endif %}
</tr>
{% else %}
<tr>
@@ -610,7 +620,8 @@
'Matter Gate or Entry Code',
'Date Possession Recovered',
'Attorney\'s Fees',
'Costs'
'Costs',
'Last Synced'
],
selectAll: true,
visibleColumns: [],