feat(data-entry): Bulk Receipt Upload cu Mobile UX Android Nativ
## Funcționalități Principale ### Bulk Upload & Processing - Drag & drop pentru upload bonuri multiple oriunde pe pagină - Batch processing cu job queue și worker pool - Real-time updates via SSE (Server-Sent Events) cu fallback polling - Duplicate detection via SHA-256 file hash - Auto-retry pentru job-uri failed - Cancel individual jobs sau batch complet ### Mobile UX - Android Native Style - Top bar fixă cu hamburger, titlu centrat, acțiuni (search/filter) - Bottom navigation cu 4 tab-uri (Bonuri, Upload, Rapoarte, Setări) - FAB (Floating Action Button) cu hide/show on scroll - Filter chips orizontal scrollabile - Selecție multiplă prin long-press (500ms) - Select All + Bulk Delete cu confirmare - Layout Android pentru Create/Edit/View bon (Gmail compose style) ### Bug Fixes - Refresh individual via SSE în loc de refresh total pagină - Bonurile cu eroare OCR rămân vizibile pentru editare manuală - Afișare nume fișier original pentru toate bonurile - Upload stabil pe mobil (fix race condition File API) - Păstrare ordine bonuri la refresh (nu se reordonează) ### Backend - SSE endpoint pentru status updates real-time - Bulk delete endpoint cu partial success - Auto-cleanup bonuri failed după 7 zile - Batch model cu tracking complet ### Testing - E2E tests cu Playwright - Unit tests pentru bulk upload, auto-create, cleanup ## Commits Squashed: 43 user stories (US-001 → US-043) ## Branch: ralph/bulk-receipt-upload ## Timp dezvoltare: ~3 zile (Ralph autonomous) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
190
src/assets/css/vendor/primevue-overrides.css
vendored
190
src/assets/css/vendor/primevue-overrides.css
vendored
@@ -243,3 +243,193 @@
|
||||
background-color: var(--surface-hover, #e3f2fd) !important;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* ===== US-010: Row Lock for Processing Receipts ===== */
|
||||
/**
|
||||
* Processing row styling - visual indicator that the row is read-only.
|
||||
* Uses border-left accent and reduced opacity per acceptance criteria.
|
||||
*/
|
||||
.p-datatable .p-datatable-tbody > tr.row-processing {
|
||||
opacity: 0.7;
|
||||
border-left: 3px solid var(--blue-500);
|
||||
background-color: var(--blue-50) !important;
|
||||
transition: opacity var(--transition-fast, 150ms ease),
|
||||
background-color var(--transition-fast, 150ms ease);
|
||||
}
|
||||
|
||||
/* Dark mode support for processing rows */
|
||||
[data-theme="dark"] .p-datatable .p-datatable-tbody > tr.row-processing {
|
||||
background-color: color-mix(in srgb, var(--blue-500) 15%, var(--surface-card)) !important;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-theme]) .p-datatable .p-datatable-tbody > tr.row-processing {
|
||||
background-color: color-mix(in srgb, var(--blue-500) 15%, var(--surface-card)) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure hover doesn't override processing row styling too aggressively */
|
||||
.p-datatable .p-datatable-tbody > tr.row-processing:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* Disabled buttons in processing rows show not-allowed cursor */
|
||||
.p-datatable .p-datatable-tbody > tr.row-processing .p-button:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* ============ US-018: Failed Job Row Styling ============ */
|
||||
|
||||
/* Failed job row - red styling similar to processing but with red accent */
|
||||
.p-datatable .p-datatable-tbody > tr.row-failed {
|
||||
opacity: 0.85;
|
||||
border-left: 3px solid var(--red-500);
|
||||
background-color: var(--red-50) !important;
|
||||
transition: opacity var(--transition-fast, 150ms ease),
|
||||
background-color var(--transition-fast, 150ms ease);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .p-datatable .p-datatable-tbody > tr.row-failed {
|
||||
background-color: color-mix(in srgb, var(--red-500) 15%, var(--surface-card)) !important;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-theme]) .p-datatable .p-datatable-tbody > tr.row-failed {
|
||||
background-color: color-mix(in srgb, var(--red-500) 15%, var(--surface-card)) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hover effect for failed rows */
|
||||
.p-datatable .p-datatable-tbody > tr.row-failed:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* ============ US-019: Status Change Animations ============ */
|
||||
|
||||
/**
|
||||
* Keyframe animations for row highlight on status change.
|
||||
* - highlightGreen: Plays when a job completes successfully (2s duration)
|
||||
* - highlightRed: Plays when a job fails (2s duration)
|
||||
*
|
||||
* The animations are subtle - starting with a colored background that
|
||||
* fades to transparent. Uses design tokens for colors.
|
||||
*/
|
||||
|
||||
/* Success highlight animation - green fade */
|
||||
@keyframes highlightGreen {
|
||||
0% {
|
||||
background-color: var(--green-100);
|
||||
}
|
||||
100% {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
/* Error highlight animation - red fade */
|
||||
@keyframes highlightRed {
|
||||
0% {
|
||||
background-color: var(--red-100);
|
||||
}
|
||||
100% {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Row highlight class for completed status.
|
||||
* Applied when a row transitions to 'completed' status.
|
||||
* The animation runs once and fills forwards to stay at the end state.
|
||||
*/
|
||||
.p-datatable .p-datatable-tbody > tr.row-highlight-completed {
|
||||
animation: highlightGreen 2s ease-out forwards;
|
||||
}
|
||||
|
||||
/**
|
||||
* Row highlight class for failed status.
|
||||
* Applied when a row transitions to 'failed' status.
|
||||
*/
|
||||
.p-datatable .p-datatable-tbody > tr.row-highlight-failed {
|
||||
animation: highlightRed 2s ease-out forwards;
|
||||
}
|
||||
|
||||
/* Dark mode adjustments for highlight animations */
|
||||
[data-theme="dark"] .p-datatable .p-datatable-tbody > tr.row-highlight-completed {
|
||||
animation-name: highlightGreenDark;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .p-datatable .p-datatable-tbody > tr.row-highlight-failed {
|
||||
animation-name: highlightRedDark;
|
||||
}
|
||||
|
||||
@keyframes highlightGreenDark {
|
||||
0% {
|
||||
background-color: color-mix(in srgb, var(--green-500) 25%, var(--surface-card));
|
||||
}
|
||||
100% {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes highlightRedDark {
|
||||
0% {
|
||||
background-color: color-mix(in srgb, var(--red-500) 25%, var(--surface-card));
|
||||
}
|
||||
100% {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
/* System preference dark mode animations */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-theme]) .p-datatable .p-datatable-tbody > tr.row-highlight-completed {
|
||||
animation-name: highlightGreenDark;
|
||||
}
|
||||
|
||||
:root:not([data-theme]) .p-datatable .p-datatable-tbody > tr.row-highlight-failed {
|
||||
animation-name: highlightRedDark;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* US-019: Accessibility - Disable animations for users who prefer reduced motion.
|
||||
* This respects the prefers-reduced-motion media query as per acceptance criteria.
|
||||
*/
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.p-datatable .p-datatable-tbody > tr.row-highlight-completed,
|
||||
.p-datatable .p-datatable-tbody > tr.row-highlight-failed {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============ US-020: Cancel Job Row Animation ============ */
|
||||
|
||||
/**
|
||||
* Fade-out animation for job rows being cancelled.
|
||||
* Applied when user cancels a pending/processing file.
|
||||
* Animation runs for 300ms before row is removed from DOM.
|
||||
*/
|
||||
@keyframes rowFadeOut {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translateX(-10px);
|
||||
}
|
||||
}
|
||||
|
||||
.p-datatable .p-datatable-tbody > tr.row-cancelling {
|
||||
animation: rowFadeOut 300ms ease-out forwards;
|
||||
pointer-events: none; /* Prevent interaction during animation */
|
||||
}
|
||||
|
||||
/**
|
||||
* US-020: Accessibility - simplified animation for reduced motion preference.
|
||||
*/
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
@keyframes rowFadeOut {
|
||||
0% { opacity: 1; }
|
||||
100% { opacity: 0; }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user