diff --git a/.playwright-mcp/2020-POPI-Act-Declaration.pdf b/.playwright-mcp/2020-POPI-Act-Declaration.pdf new file mode 100644 index 00000000..01acc8ef Binary files /dev/null and b/.playwright-mcp/2020-POPI-Act-Declaration.pdf differ diff --git a/.playwright-mcp/MICTSETA-Candidacy-Agreement-None-Funded.pdf b/.playwright-mcp/MICTSETA-Candidacy-Agreement-None-Funded.pdf new file mode 100644 index 00000000..5bad0e34 Binary files /dev/null and b/.playwright-mcp/MICTSETA-Candidacy-Agreement-None-Funded.pdf differ diff --git a/.playwright-mcp/current_state.png b/.playwright-mcp/current_state.png new file mode 100644 index 00000000..4f77b085 Binary files /dev/null and b/.playwright-mcp/current_state.png differ diff --git a/.playwright-mcp/ngrok_download_test_success.png b/.playwright-mcp/ngrok_download_test_success.png new file mode 100644 index 00000000..fe5cbeb5 Binary files /dev/null and b/.playwright-mcp/ngrok_download_test_success.png differ diff --git a/.playwright-mcp/submission_page.png b/.playwright-mcp/submission_page.png new file mode 100644 index 00000000..20c245f4 Binary files /dev/null and b/.playwright-mcp/submission_page.png differ diff --git a/.playwright-mcp/template8_ngrok_view.png b/.playwright-mcp/template8_ngrok_view.png new file mode 100644 index 00000000..c4e144d8 Binary files /dev/null and b/.playwright-mcp/template8_ngrok_view.png differ diff --git a/app/controllers/api/active_storage_blobs_proxy_controller.rb b/app/controllers/api/active_storage_blobs_proxy_controller.rb index a542c637..24680ce9 100644 --- a/app/controllers/api/active_storage_blobs_proxy_controller.rb +++ b/app/controllers/api/active_storage_blobs_proxy_controller.rb @@ -6,6 +6,8 @@ module Api skip_before_action :authenticate_user! skip_authorization_check + skip_before_action :verify_institution_access + skip_before_action :verify_institution_role before_action :set_cors_headers before_action :set_noindex_headers diff --git a/app/views/pages/landing.html.erb b/app/views/pages/landing.html.erb index 25cfb071..fcaf86ed 100644 --- a/app/views/pages/landing.html.erb +++ b/app/views/pages/landing.html.erb @@ -76,4 +76,3 @@ -<%= render 'shared/attribution', with_counter: true %> diff --git a/docs/conversation-extract-20260109-115735.md b/docs/conversation-extract-20260109-115735.md new file mode 100644 index 00000000..ce8beda6 --- /dev/null +++ b/docs/conversation-extract-20260109-115735.md @@ -0,0 +1,4 @@ +# Conversation Extract - feature/brand-colors + +| Time | Content | +|------|---------| diff --git a/docs/extract-message.sh b/docs/extract-message.sh new file mode 100644 index 00000000..3e6cf7e5 --- /dev/null +++ b/docs/extract-message.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Extract conversation messages from feature/brand-colors branch to markdown table +# Usage: ./extract_conversation.sh + +CLAUDE_DIR="$HOME/.claude/projects/-home-dev-mode-dev-dyict-projects-floDoc-v3" +OUTPUT_FILE="$HOME/dev/dyict-projects/floDoc-v3/docs/conversation-extract-$(date +%Y%m%d-%H%M%S).md" + +cd "$CLAUDE_DIR" || exit 1 +LATEST=$(ls -t | head -1) + +echo "Processing: $LATEST" +echo "Output will be saved to: $OUTPUT_FILE" + +# Create markdown header +cat > "$OUTPUT_FILE" << 'EOF' +# Conversation Extract - feature/brand-colors + +| Time | Content | +|------|---------| +EOF + +# Process the JSONL file +cat "$LATEST" | jq -r ' + select(.gitBranch == "feature/brand-colors" and .message.content != null) | + "| \(.timestamp) | \(.message.content | gsub("\n"; "
") | gsub("\\|"; "\\|")) |" +' >> "$OUTPUT_FILE" + +echo "" +echo "āœ“ Done! Created: $OUTPUT_FILE" +echo "" +echo "Preview (first 20 lines):" +head -20 "$OUTPUT_FILE" +echo "" +echo "To view full file: cat $OUTPUT_FILE" +echo "Or open in editor: code $OUTPUT_FILE" \ No newline at end of file diff --git a/lib/pdf_icons.rb b/lib/pdf_icons.rb index 67148acc..8f767113 100644 --- a/lib/pdf_icons.rb +++ b/lib/pdf_icons.rb @@ -37,14 +37,14 @@ module PdfIcons end def logo_data - @logo_data ||= PATH.join('logo.svg').read + @logo_data ||= PATH.join('logo.png').read end def logo_new_data - @logo_new_data ||= PATH.join('logo_new.svg').read + @logo_new_data ||= PATH.join('logo_new.png').read end def stamp_logo_data - @stamp_logo_data ||= PATH.join('stamp-logo.svg').read + @stamp_logo_data ||= PATH.join('stamp-logo.png').read end end diff --git a/lib/pdf_icons/logo.png b/lib/pdf_icons/logo.png index f75258d3..33ad6b2b 100644 Binary files a/lib/pdf_icons/logo.png and b/lib/pdf_icons/logo.png differ diff --git a/lib/pdf_icons/logo_new.png b/lib/pdf_icons/logo_new.png index 92e4fcbf..33ad6b2b 100644 Binary files a/lib/pdf_icons/logo_new.png and b/lib/pdf_icons/logo_new.png differ diff --git a/lib/pdf_icons/stamp-logo.png b/lib/pdf_icons/stamp-logo.png index c5a2738c..33ad6b2b 100644 Binary files a/lib/pdf_icons/stamp-logo.png and b/lib/pdf_icons/stamp-logo.png differ diff --git a/lib/submissions/generate_audit_trail.rb b/lib/submissions/generate_audit_trail.rb index 588c83e2..8efda450 100644 --- a/lib/submissions/generate_audit_trail.rb +++ b/lib/submissions/generate_audit_trail.rb @@ -508,8 +508,8 @@ module Submissions def add_logo(column, _submission = nil) column.image(PdfIcons.logo_io, width: 40, height: 40, position: :float) - column.formatted_text([{ text: 'DocuSeal', - link: Docuseal::PRODUCT_EMAIL_URL }], + column.formatted_text([{ text: 'FloDoc', + link: 'https://flodoc.com' }], font_size: 20, font: [FONT_NAME, { variant: :bold }], width: 100, diff --git a/submission_page_40.png b/submission_page_40.png new file mode 100644 index 00000000..a64100a3 Binary files /dev/null and b/submission_page_40.png differ diff --git a/test_pdf_workflow.js b/test_pdf_workflow.js new file mode 100644 index 00000000..21ecfe03 --- /dev/null +++ b/test_pdf_workflow.js @@ -0,0 +1,249 @@ +const { chromium } = require('playwright'); +const fs = require('fs'); + +async function testPDFWorkflow() { + console.log('šŸš€ Testing FloDoc PDF workflow with ngrok...\n'); + + const browser = await chromium.launch({ + headless: true, + args: ['--no-sandbox', '--disable-setuid-sandbox'] + }); + + const context = await browser.newContext({ + viewport: { width: 1280, height: 800 }, + acceptDownloads: true + }); + + const page = await context.newPage(); + + // Capture console errors + page.on('console', msg => { + const type = msg.type(); + if (type === 'error' || type === 'warning') { + console.log(` [${type}] ${msg.text()}`); + } + }); + + // Capture network failures + page.on('requestfailed', request => { + console.log(` āŒ Network failed: ${request.url()} - ${request.failure()?.errorText}`); + }); + + try { + // Test 1: Check if ngrok URL is accessible + console.log('1. Testing ngrok connectivity...'); + try { + const response = await page.goto('https://pseudoancestral-expressionlessly-calista.ngrok-free.dev', { + waitUntil: 'networkidle', + timeout: 10000 + }); + console.log(` āœ… Ngrok URL accessible: ${response.status()}`); + } catch (e) { + console.log(` āŒ Ngrok URL failed: ${e.message}`); + console.log(' Falling back to localhost...'); + await page.goto('http://localhost:3001', { waitUntil: 'networkidle' }); + console.log(' āœ… Localhost accessible'); + } + + // Test 2: Check if we need to log in + console.log('\n2. Checking authentication status...'); + const currentUrl = page.url(); + const pageContent = await page.content(); + + if (pageContent.includes('Sign in') || pageContent.includes('sign_in')) { + console.log(' āš ļø Login required - checking for existing session...'); + + // Try to find login form + const emailField = await page.$('input[type="email"], input[name*="email"]'); + if (emailField) { + console.log(' āŒ No active session found. Please log in manually first.'); + console.log(' URL:', page.url()); + console.log(' You can use the existing credentials or create a test account.'); + await browser.close(); + return; + } + } + + console.log(' āœ… Authentication appears to be working'); + + // Test 3: Navigate to submissions or templates + console.log('\n3. Looking for submissions/templates...'); + + // Try different navigation paths + const pathsToTry = [ + '/submissions', + '/templates', + '/dashboard', + '/' + ]; + + let foundContent = false; + + for (const path of pathsToTry) { + try { + await page.goto(`http://localhost:3001${path}`, { waitUntil: 'networkidle', timeout: 5000 }); + const content = await page.content(); + + if (content.includes('submission') || content.includes('template') || content.includes('FloDoc')) { + console.log(` āœ… Found content at ${path}`); + foundContent = true; + break; + } + } catch (e) { + // Skip to next path + } + } + + if (!foundContent) { + console.log(' āŒ Could not find submissions or templates'); + console.log(' Current URL:', page.url()); + console.log(' Page title:', await page.title()); + } + + // Test 4: Look for any PDF download functionality + console.log('\n4. Searching for PDF download functionality...'); + + // Look for download links/buttons + const downloadSelectors = [ + 'a[href*="download"]', + 'button:has-text("Download")', + 'a:has-text("PDF")', + 'button:has-text("PDF")', + 'a[href*=".pdf"]', + 'a[href*="/s/"]', // submission links + 'a[href*="/submitters/"]' + ]; + + let downloadLinks = []; + for (const selector of downloadSelectors) { + try { + const links = await page.$$(selector); + if (links.length > 0) { + console.log(` Found ${links.length} links with selector: ${selector}`); + downloadLinks.push(...links); + } + } catch (e) { + // Ignore selector errors + } + } + + if (downloadLinks.length > 0) { + console.log(` āœ… Total download-related links found: ${downloadLinks.length}`); + + // Try the first download link + console.log('\n5. Attempting to download PDF...'); + const firstLink = downloadLinks[0]; + const linkText = (await firstLink.textContent()).trim(); + const linkHref = await firstLink.getAttribute('href'); + + console.log(` Trying link: "${linkText}" -> ${linkHref}`); + + // Set up download handler + const downloadPromise = page.waitForEvent('download').catch(() => null); + + await firstLink.click(); + + const download = await downloadPromise; + + if (download) { + const filename = download.suggestedFilename() || 'download.pdf'; + const downloadPath = `/tmp/${filename}`; + + try { + await download.saveAs(downloadPath); + const stats = fs.statSync(downloadPath); + + console.log(` āœ… Download successful!`); + console.log(` File: ${filename}`); + console.log(` Size: ${stats.size} bytes`); + console.log(` Path: ${downloadPath}`); + + if (stats.size > 1000) { + console.log(' āœ… PDF appears to be valid (good file size)'); + } else { + console.log(' āš ļø PDF might be corrupted (small file size)'); + } + } catch (saveError) { + console.log(` āŒ Failed to save download: ${saveError.message}`); + } + } else { + console.log(' āŒ Download did not start'); + + // Check if it opened in new tab instead + const newUrl = page.url(); + if (newUrl !== linkHref && newUrl.includes('pdf')) { + console.log(' āš ļø PDF might have opened in current tab instead'); + + // Try to save the current page as PDF + try { + const pdfBuffer = await page.pdf({ format: 'A4' }); + const pdfPath = '/tmp/page_capture.pdf'; + fs.writeFileSync(pdfPath, pdfBuffer); + console.log(` āœ… Captured current page as PDF: ${pdfPath} (${pdfBuffer.length} bytes)`); + } catch (pdfError) { + console.log(` āŒ Failed to capture page as PDF: ${pdfError.message}`); + } + } + } + } else { + console.log(' āŒ No download links found on current page'); + + // Let's see what's actually on the page + console.log('\n6. Analyzing page content...'); + const allLinks = await page.$$('a'); + console.log(` Total links on page: ${allLinks.length}`); + + // Show first 5 links + for (let i = 0; i < Math.min(5, allLinks.length); i++) { + const link = allLinks[i]; + const text = (await link.textContent()).trim(); + const href = await link.getAttribute('href'); + if (text && href) { + console.log(` Link ${i + 1}: "${text}" -> ${href}`); + } + } + } + + // Test 7: Try direct API access + console.log('\n7. Testing direct API endpoints...'); + const apiTests = [ + '/api/v1/submissions', + '/api/v1/templates' + ]; + + for (const endpoint of apiTests) { + try { + const response = await page.goto(`http://localhost:3001${endpoint}`, { + waitUntil: 'networkidle', + timeout: 5000 + }); + const status = response.status(); + const body = await page.textContent('body'); + + if (status === 200 && body.length > 0) { + console.log(` āœ… ${endpoint}: ${status} (${body.length} chars)`); + } else { + console.log(` āš ļø ${endpoint}: ${status}`); + } + } catch (e) { + console.log(` āŒ ${endpoint}: ${e.message}`); + } + } + + } catch (error) { + console.error('\nāŒ Test failed:', error.message); + console.error('Stack:', error.stack); + } finally { + console.log('\n8. Closing browser...'); + await browser.close(); + console.log('\nšŸ” Test complete!'); + } +} + +// Run the test +testPDFWorkflow().then(() => { + process.exit(0); +}).catch(err => { + console.error('Fatal error:', err); + process.exit(1); +}); \ No newline at end of file diff --git a/troubleshoot.txt b/troubleshoot.txt new file mode 100644 index 00000000..a7460a6b --- /dev/null +++ b/troubleshoot.txt @@ -0,0 +1,40 @@ +sudo rm -rf + /home/dev-mode/.nvm/versions/node/v22.18.0/lib/node_modules/@anthropic-ai/claude-code + + 2. Clean up any leftover temp directories: + + sudo rm -rf + /home/dev-mode/.nvm/versions/node/v22.18.0/lib/node_modules/@anthropic-ai/.claude-code-* + + 3. Clear npm cache: + + npm cache clean --force + + 4. Reinstall Claude Code: + + npm install -g @anthropic-ai/claude-code + + 5. Verify installation: + + claude --version + + 6. If you still get errors, try this alternative approach: + + # Uninstall completely + npm uninstall -g @anthropic-ai/claude-code + + # Clear any lock files + rm -f /home/dev-mode/.nvm/versions/node/v22.18.0/lib/node_modules/@anthropic-ai/claude-co + de/package-lock.json + + # Reinstall fresh + npm install -g @anthropic-ai/claude-code + + 7. If npm still fails, use npx as a temporary workaround: + + npx @anthropic-ai/claude-code + + The issue is that npm is trying to rename a directory but there's already something + there. The steps above will clean everything up and give you a fresh installation. + + Good luck! You can always come back if you need more help after running these commands. \ No newline at end of file