Files
roa2web-service-auto/scripts/ralph/ralph.sh
Claude Agent 7b3541403f 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>
2026-01-12 08:33:17 +00:00

204 lines
5.7 KiB
Bash
Executable File

#!/bin/bash
# Ralph - Autonomous Loop for PRD Implementation
# Usage: ./ralph.sh [max_iterations]
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
PRD_FILE="$SCRIPT_DIR/prd.json"
PROGRESS_FILE="$SCRIPT_DIR/progress.txt"
PROMPT_FILE="$SCRIPT_DIR/prompt.md"
LOG_DIR="$SCRIPT_DIR/logs"
MAX_ITERATIONS=${1:-50}
ITERATION=0
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
log() {
echo -e "${BLUE}[Ralph]${NC} $1"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$PROGRESS_FILE"
}
error() {
echo -e "${RED}[Ralph ERROR]${NC} $1"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $1" >> "$PROGRESS_FILE"
}
success() {
echo -e "${GREEN}[Ralph]${NC} $1"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] SUCCESS: $1" >> "$PROGRESS_FILE"
}
# Check prerequisites
if [ ! -f "$PRD_FILE" ]; then
error "prd.json not found at $PRD_FILE"
exit 1
fi
if ! command -v claude &> /dev/null; then
error "claude CLI not found. Install with: npm install -g @anthropic-ai/claude-code"
exit 1
fi
if ! command -v jq &> /dev/null; then
error "jq not found. Install with: apt install jq"
exit 1
fi
# Create log directory
mkdir -p "$LOG_DIR"
# Get project info
PROJECT_NAME=$(jq -r '.projectName' "$PRD_FILE")
BRANCH_NAME=$(jq -r '.branchName' "$PRD_FILE")
log "Starting Ralph for project: $PROJECT_NAME"
log "Max iterations: $MAX_ITERATIONS"
# Create branch if not exists
cd "$PROJECT_ROOT"
CURRENT_BRANCH=$(git branch --show-current)
if [ "$CURRENT_BRANCH" != "$BRANCH_NAME" ]; then
if git show-ref --verify --quiet "refs/heads/$BRANCH_NAME"; then
log "Switching to existing branch: $BRANCH_NAME"
git checkout "$BRANCH_NAME"
else
log "Creating new branch: $BRANCH_NAME"
git checkout -b "$BRANCH_NAME"
fi
fi
# Function to get next pending story
get_next_story() {
jq -r '.userStories | map(select(.passes == false)) | sort_by(.priority) | .[0] | .id // empty' "$PRD_FILE"
}
# Function to get story details
get_story_details() {
local story_id=$1
jq -r --arg id "$story_id" '.userStories[] | select(.id == $id)' "$PRD_FILE"
}
# Function to mark story as passed
mark_story_passed() {
local story_id=$1
local notes=$2
local tmp_file=$(mktemp)
jq --arg id "$story_id" --arg notes "$notes" \
'(.userStories[] | select(.id == $id)) |= (.passes = true | .notes = $notes)' \
"$PRD_FILE" > "$tmp_file" && mv "$tmp_file" "$PRD_FILE"
}
# Function to count stories
count_stories() {
local total=$(jq '.userStories | length' "$PRD_FILE")
local passed=$(jq '[.userStories[] | select(.passes == true)] | length' "$PRD_FILE")
echo "$passed/$total"
}
# Main loop
while [ $ITERATION -lt $MAX_ITERATIONS ]; do
ITERATION=$((ITERATION + 1))
log "=== Iteration $ITERATION/$MAX_ITERATIONS ==="
# Get next story
NEXT_STORY=$(get_next_story)
if [ -z "$NEXT_STORY" ]; then
success "All stories completed! 🎉"
break
fi
log "Working on story: $NEXT_STORY"
# Get story details for prompt
STORY_JSON=$(get_story_details "$NEXT_STORY")
STORY_TITLE=$(echo "$STORY_JSON" | jq -r '.title')
# Create iteration prompt
ITERATION_PROMPT="You are implementing user story $NEXT_STORY: $STORY_TITLE
Read the full PRD at scripts/ralph/prd.json for context and CSS rules.
Story details:
$STORY_JSON
IMPORTANT CSS RULES (from PRD):
- NEVER use hardcoded values - always use design tokens
- Check docs/DESIGN_TOKENS.md and docs/CSS_PATTERNS.md before writing CSS
- Test in BOTH light and dark mode
- NEVER use :deep() in components
Your task:
1. Implement this story following all acceptance criteria
2. Run tests/typecheck to verify
3. If ALL criteria pass, respond with: STORY_PASSED
4. If blocked or need clarification, respond with: STORY_BLOCKED: <reason>
Do NOT move to other stories. Focus only on $NEXT_STORY."
# Run Claude
LOG_FILE="$LOG_DIR/iteration_${ITERATION}_${NEXT_STORY}.log"
log "Running Claude... (log: $LOG_FILE)"
# Run claude with the prompt (--output-format json avoids streaming mode issues)
CLAUDE_OUTPUT=$(cd "$PROJECT_ROOT" && claude -p "$ITERATION_PROMPT" --output-format json 2>&1 | tee "$LOG_FILE")
# Check result
if echo "$CLAUDE_OUTPUT" | grep -q "STORY_PASSED"; then
success "Story $NEXT_STORY passed!"
mark_story_passed "$NEXT_STORY" "Completed in iteration $ITERATION"
# Commit changes
cd "$PROJECT_ROOT"
if [ -n "$(git status --porcelain)" ]; then
git add -A
git commit -m "feat($PROJECT_NAME): Complete $NEXT_STORY - $STORY_TITLE
Implemented by Ralph autonomous loop.
Iteration: $ITERATION
Co-Authored-By: Claude <noreply@anthropic.com>"
log "Changes committed"
fi
elif echo "$CLAUDE_OUTPUT" | grep -q "STORY_BLOCKED"; then
BLOCK_REASON=$(echo "$CLAUDE_OUTPUT" | grep "STORY_BLOCKED" | sed 's/STORY_BLOCKED://')
error "Story $NEXT_STORY blocked: $BLOCK_REASON"
log "Stopping loop due to blocked story"
break
else
log "Story $NEXT_STORY not yet complete, continuing..."
fi
# Progress update
PROGRESS=$(count_stories)
log "Progress: $PROGRESS stories completed"
# Small delay between iterations
sleep 2
done
# Final summary
echo ""
log "=== Ralph Session Complete ==="
PROGRESS=$(count_stories)
log "Final progress: $PROGRESS stories completed"
log "Branch: $BRANCH_NAME"
log "Logs: $LOG_DIR"
# Show remaining stories
REMAINING=$(jq -r '[.userStories[] | select(.passes == false)] | length' "$PRD_FILE")
if [ "$REMAINING" -gt 0 ]; then
echo -e "\n${YELLOW}Remaining stories:${NC}"
jq -r '.userStories[] | select(.passes == false) | " - \(.id): \(.title)"' "$PRD_FILE"
fi