- Update logo colors in PDF icons (logo.png, logo_new.png, stamp-logo.png) - Restore original landing page design - Update audit trail generation in generate_audit_trail.rb - Update active storage blobs proxy controller - Add documentation and testing files Co-Authored-By: Claude <noreply@anthropic.com>pull/565/head
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 484 KiB |
|
After Width: | Height: | Size: 482 KiB |
|
After Width: | Height: | Size: 92 KiB |
@ -0,0 +1,4 @@
|
||||
# Conversation Extract - feature/brand-colors
|
||||
|
||||
| Time | Content |
|
||||
|------|---------|
|
||||
@ -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"; "<br>") | 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"
|
||||
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 85 KiB |
@ -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);
|
||||
});
|
||||
@ -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.
|
||||