fix: refine validation UI with updated colors and spacing in upload component, and other fixes

This commit is contained in:
vee1e 2025-07-29 01:07:47 +05:30
parent 6841e1e3a4
commit 30167af8d4
11 changed files with 56 additions and 37 deletions

1
.gitignore vendored
View file

@ -22,3 +22,4 @@ dist/
tests/ tests/
project-plans/ project-plans/
.DS_Store .DS_Store
.cursorrules

View file

@ -163,18 +163,19 @@ mongosh --eval "db.runCommand('ping')"
Below is a real example of metrics collected for uploading 10 forms (each with ~400 questions and 3-10 options per question) based on the latest performance data: Below is a real example of metrics collected for uploading 10 forms (each with ~400 questions and 3-10 options per question) based on the latest performance data:
| Description | Time | | Description | Time |
| ------------------------------------------------ | ----------------------- | | ------------------------------------------------ | -------------------------- |
| Time to validate each form file | 45.80-123.20ms (59.98ms) | | Time to validate each form file | 45.80-123.20ms (59.98ms) |
| Time to process and save one form | 0.49-288.43ms (126.84ms) | | Time to process and save one form | 0.49-288.43ms (126.84ms) |
| Time to process and save all questions in a form | 59.64-82.80ms (64.59ms) | | Time to process and save all questions in a form | 59.64-82.80ms (64.59ms) |
| Average time to process one question | 0.15-0.20ms (0.16ms) | | Average time to process one question | 0.15-0.20ms (0.16ms) |
| Average time to process one option | 0.15-0.17ms (0.16ms) | | Average time to process one option | 0.15-0.17ms (0.16ms) |
| Time to process all forms in the batch | 228.62-288.43ms (253.00ms) | | Time to process all forms in the batch | 228.62-288.43ms (253.00ms) |
| Number of forms processed in the batch | 10 | | Number of forms processed in the batch | 10 |
| Average time to process one form in the batch | 228.62-288.43ms (253.00ms) | | Average time to process one form in the batch | 228.62-288.43ms (253.00ms) |
**Notes:** **Notes:**
- Metrics collected from backend/metrics.txt on 2025-07-28 - Metrics collected from backend/metrics.txt on 2025-07-28
- All times are in ms unless specified otherwise - All times are in ms unless specified otherwise
- Hardware used is an M3 Pro Macbook Pro, with 18GB unified memory and 512GB of storage. - Hardware used is an M3 Pro Macbook Pro, with 18GB unified memory and 512GB of storage.

View file

@ -26,4 +26,4 @@ async def connect_to_mongo():
async def close_mongo_connection(): async def close_mongo_connection():
"""Close MongoDB connection""" """Close MongoDB connection"""
async_client.close() async_client.close()

View file

@ -1,3 +1,3 @@
""" """
Models package for form-related data structures Models package for form-related data structures
""" """

View file

@ -59,4 +59,4 @@ class ParsedForm(BaseModel):
version: str version: str
groups: List[FormGroup] groups: List[FormGroup]
settings: Optional[Dict[str, Any]] = None settings: Optional[Dict[str, Any]] = None
metadata: Optional[Dict[str, Any]] = None metadata: Optional[Dict[str, Any]] = None

View file

@ -1,3 +1,3 @@
""" """
Services package for business logic implementations Services package for business logic implementations
""" """

View file

@ -129,7 +129,7 @@ class DatabaseService:
return True return True
except Exception as e: except Exception as e:
logger.error(f"Error deleting all forms: {e}") logger.error(f"Error deleting all forms: {e}")
return False return False
async def update_form(self, form_id: str, form_data: Dict[str, Any], questions: List[Dict[str, Any]], options: List[Dict[str, Any]]) -> bool: async def update_form(self, form_id: str, form_data: Dict[str, Any], questions: List[Dict[str, Any]], options: List[Dict[str, Any]]) -> bool:
"""Update form metadata, questions, and options by form_id""" """Update form metadata, questions, and options by form_id"""
@ -159,4 +159,4 @@ class DatabaseService:
return True return True
except Exception as e: except Exception as e:
logger.error(f"Error updating form: {e}") logger.error(f"Error updating form: {e}")
return False return False

View file

@ -273,9 +273,9 @@ import { Subscription } from 'rxjs';
border-left: 3px solid #ffffff; border-left: 3px solid #ffffff;
border: 1px solid rgba(255, 255, 255, 0.1); border: 1px solid rgba(255, 255, 255, 0.1);
&.current-question { &.current-question {
border: 2px solid #ffffff; border: 2px solid #ffffff;
background: rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.1);
} }
.question-header { .question-header {

View file

@ -177,7 +177,7 @@ import { MatIconModule } from '@angular/material/icon';
export class SearchComponent implements OnInit, OnDestroy { export class SearchComponent implements OnInit, OnDestroy {
@Output() search = new EventEmitter<string>(); @Output() search = new EventEmitter<string>();
@ViewChild('searchInput') searchInput!: ElementRef<HTMLInputElement>; @ViewChild('searchInput') searchInput!: ElementRef<HTMLInputElement>;
searchQuery = ''; searchQuery = '';
ngOnInit() { ngOnInit() {

View file

@ -309,15 +309,15 @@ import { FormPreviewService } from '../../services/form-preview.service';
} }
.invalid { .invalid {
color: white; color: #ff0000;
background: #F44336; background: #1c0000;
border: 1px solid #F44336; border: 1px solid #660000;
} }
} }
.validation-details { .validation-details {
margin-top: 0.75rem; margin-top: 0.00rem;
padding-top: 0.75rem; padding-top: 0.30rem;
border-top: 1px solid rgba(255, 255, 255, 0.2); border-top: 1px solid rgba(255, 255, 255, 0.2);
min-height: 0; min-height: 0;
transition: all 0.3s ease; transition: all 0.3s ease;
@ -352,9 +352,9 @@ import { FormPreviewService } from '../../services/form-preview.service';
} }
&.warning { &.warning {
background: rgba(255, 255, 255, 0.1); background: rgba(255, 193, 7, 0.12);
border-left: 3px solid #ffffff; border-left: 3px solid #FFC107;
border: 1px solid rgba(255, 255, 255, 0.2); border: 1px solid rgba(255, 193, 7, 0.25);
} }
.validation-header { .validation-header {
@ -365,8 +365,8 @@ import { FormPreviewService } from '../../services/form-preview.service';
.validation-type { .validation-type {
background: rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.1);
padding: 0.125rem 0.375rem; padding: 0.0 0.375rem;
border-radius: 12px; border-radius: 6px;
font-size: 0.7rem; font-size: 0.7rem;
font-weight: 600; font-weight: 600;
text-transform: uppercase; text-transform: uppercase;
@ -513,6 +513,24 @@ import { FormPreviewService } from '../../services/form-preview.service';
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.5rem; gap: 0.5rem;
::ng-deep .mat-mdc-icon-button {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
.mat-icon {
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
width: 20px;
height: 20px;
margin: 0;
}
}
} }
::ng-deep { ::ng-deep {
@ -521,12 +539,12 @@ import { FormPreviewService } from '../../services/form-preview.service';
background-color: #ffffff !important; background-color: #ffffff !important;
color: #000000 !important; color: #000000 !important;
} }
.mdc-snackbar__label { .mdc-snackbar__label {
color: #000000 !important; color: #000000 !important;
font-weight: 500 !important; font-weight: 500 !important;
} }
.mdc-snackbar__actions .mdc-button { .mdc-snackbar__actions .mdc-button {
color: #000000 !important; color: #000000 !important;
font-weight: 600 !important; font-weight: 600 !important;
@ -538,12 +556,12 @@ import { FormPreviewService } from '../../services/form-preview.service';
background-color: #F44336 !important; background-color: #F44336 !important;
color: white !important; color: white !important;
} }
.mdc-snackbar__label { .mdc-snackbar__label {
color: white !important; color: white !important;
font-weight: 500 !important; font-weight: 500 !important;
} }
.mdc-snackbar__actions .mdc-button { .mdc-snackbar__actions .mdc-button {
color: white !important; color: white !important;
font-weight: 600 !important; font-weight: 600 !important;
@ -889,7 +907,7 @@ export class UploadComponent implements OnInit, OnChanges {
if (validationResult.valid) { if (validationResult.valid) {
// Store old form details for comparison // Store old form details for comparison
const oldFormTitle = this.updateTargetForm!.title; const oldFormTitle = this.updateTargetForm!.title;
// File is valid, proceed with update // File is valid, proceed with update
this.formService.updateForm(this.updateTargetForm!.id, file).subscribe({ this.formService.updateForm(this.updateTargetForm!.id, file).subscribe({
next: (updatedFormDetails) => { next: (updatedFormDetails) => {
@ -897,10 +915,10 @@ export class UploadComponent implements OnInit, OnChanges {
this.showFormDetails(updatedFormDetails.form); // Show updated details this.showFormDetails(updatedFormDetails.form); // Show updated details
this.loadingFormId = null; this.loadingFormId = null;
this.updateTargetForm = null; this.updateTargetForm = null;
// Show success toast with details // Show success toast with details
const toastMessage = `Successfully updated "${oldFormTitle}" to "${updatedFormDetails.form.title}".`; const toastMessage = `Successfully updated "${oldFormTitle}" to "${updatedFormDetails.form.title}".`;
this.snackBar.open(toastMessage, 'Close', { this.snackBar.open(toastMessage, 'Close', {
duration: 5000, duration: 5000,
panelClass: ['custom-snackbar'], panelClass: ['custom-snackbar'],

3
run.sh
View file

@ -3,7 +3,7 @@
PROJECT_ROOT="/Users/verma/Projects/bulk-questionnaire-upload" PROJECT_ROOT="/Users/verma/Projects/bulk-questionnaire-upload"
FRONTEND_PATH="$PROJECT_ROOT/frontend" FRONTEND_PATH="$PROJECT_ROOT/frontend"
BACKEND_PATH="$PROJECT_ROOT/backend" BACKEND_PATH="$PROJECT_ROOT/backend"
SESSION_NAME="dev-project" SESSION_NAME="bulk-questionnaire-upload"
if ! tmux has-session -t "$SESSION_NAME"; then if ! tmux has-session -t "$SESSION_NAME"; then
tmux new-session -s "$SESSION_NAME" -d tmux new-session -s "$SESSION_NAME" -d
@ -20,7 +20,6 @@ if ! tmux has-session -t "$SESSION_NAME"; then
tmux send-keys -t "$SESSION_NAME:0.1" "uvicorn main:app --reload" C-m tmux send-keys -t "$SESSION_NAME:0.1" "uvicorn main:app --reload" C-m
tmux attach-session -t "$SESSION_NAME" tmux attach-session -t "$SESSION_NAME"
open "http://localhost:4200"
else else
echo "Tmux session '$SESSION_NAME' already exists. Attaching..." echo "Tmux session '$SESSION_NAME' already exists. Attaching..."
tmux attach-session -t "$SESSION_NAME" tmux attach-session -t "$SESSION_NAME"