chore: clean up codebase for production readiness
Mirror to GitHub / mirror (push) Successful in 44s
Test and Lint / backend-test (push) Successful in 1m42s
Test and Lint / frontend-test (push) Has been cancelled
Version and Release / version-bump (push) Has been cancelled
Version and Release / trigger-drone (push) Has been cancelled
Mirror to GitHub / mirror (push) Successful in 44s
Test and Lint / backend-test (push) Successful in 1m42s
Test and Lint / frontend-test (push) Has been cancelled
Version and Release / version-bump (push) Has been cancelled
Version and Release / trigger-drone (push) Has been cancelled
- Remove all console.log/debug statements from production code - Add NODE_ENV checks for development-only logging - Remove test scripts (test-feedback, test-image-security, test-backup-*, test-restore) - Remove one-time fix scripts (fix-temp-photos, fix-migration-state, mark-migration-applied) - Remove sensitive files (.env.backup, ADMIN_CREDENTIALS.txt) - Update package.json to remove references to deleted scripts - Replace console statements with logger utility in backend - Secure error boundaries to not expose stack traces in production This makes the codebase production-ready with no debug output or test scripts. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import { Key, RefreshCw, Copy, Check, Zap } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { generateEventPassword, generatePasswordSuggestions, validatePassword } from '../../utils/passwordGenerator';
|
||||
import { Button } from './Button';
|
||||
|
||||
interface PasswordGeneratorProps {
|
||||
eventName?: string;
|
||||
eventDate?: string;
|
||||
eventType?: string;
|
||||
onPasswordGenerated: (password: string) => void;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
passwordComplexity?: 'simple' | 'moderate' | 'strong' | 'very_strong';
|
||||
}
|
||||
|
||||
export const PasswordGenerator: React.FC<PasswordGeneratorProps> = ({
|
||||
eventName = '',
|
||||
eventDate = '',
|
||||
eventType = 'wedding',
|
||||
onPasswordGenerated,
|
||||
className = '',
|
||||
disabled = false,
|
||||
passwordComplexity = 'moderate'
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [isGenerating, setIsGenerating] = useState(false);
|
||||
const [showSuggestions, setShowSuggestions] = useState(false);
|
||||
const [suggestions, setSuggestions] = useState<string[]>([]);
|
||||
const [copiedIndex, setCopiedIndex] = useState<number | null>(null);
|
||||
|
||||
const generatePassword = useCallback(() => {
|
||||
setIsGenerating(true);
|
||||
|
||||
// Simulate some processing time for better UX
|
||||
setTimeout(() => {
|
||||
const config = {
|
||||
complexity: passwordComplexity,
|
||||
minLength: passwordComplexity === 'simple' ? 6 : passwordComplexity === 'moderate' ? 8 : 12,
|
||||
requireSpecialChars: passwordComplexity === 'very_strong'
|
||||
};
|
||||
|
||||
const password = generateEventPassword({
|
||||
eventName,
|
||||
eventDate,
|
||||
eventType,
|
||||
config
|
||||
});
|
||||
|
||||
onPasswordGenerated(password);
|
||||
setIsGenerating(false);
|
||||
}, 300);
|
||||
}, [eventName, eventDate, eventType, passwordComplexity, onPasswordGenerated]);
|
||||
|
||||
const generateSuggestions = useCallback(() => {
|
||||
const newSuggestions = generatePasswordSuggestions({
|
||||
eventName,
|
||||
eventDate,
|
||||
eventType
|
||||
});
|
||||
setSuggestions(newSuggestions);
|
||||
setShowSuggestions(true);
|
||||
}, [eventName, eventDate, eventType]);
|
||||
|
||||
const copyToClipboard = async (password: string, index: number) => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(password);
|
||||
setCopiedIndex(index);
|
||||
setTimeout(() => setCopiedIndex(null), 2000);
|
||||
} catch (err) {
|
||||
// Fallback for older browsers
|
||||
const textArea = document.createElement('textarea');
|
||||
textArea.value = password;
|
||||
document.body.appendChild(textArea);
|
||||
textArea.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(textArea);
|
||||
setCopiedIndex(index);
|
||||
setTimeout(() => setCopiedIndex(null), 2000);
|
||||
}
|
||||
};
|
||||
|
||||
const selectPassword = (password: string) => {
|
||||
onPasswordGenerated(password);
|
||||
setShowSuggestions(false);
|
||||
};
|
||||
|
||||
const getPasswordStrength = (password: string) => {
|
||||
const validation = validatePassword(password, {
|
||||
complexity: passwordComplexity,
|
||||
minLength: passwordComplexity === 'simple' ? 6 : passwordComplexity === 'moderate' ? 8 : 12
|
||||
});
|
||||
|
||||
if (validation.score <= 1) return { label: t('passwordGenerator.weak'), color: 'text-red-600' };
|
||||
if (validation.score <= 2) return { label: t('passwordGenerator.fair'), color: 'text-yellow-600' };
|
||||
if (validation.score <= 3) return { label: t('passwordGenerator.good'), color: 'text-blue-600' };
|
||||
return { label: t('passwordGenerator.strong'), color: 'text-green-600' };
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`relative ${className}`}>
|
||||
{/* Generate Button */}
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={generatePassword}
|
||||
disabled={disabled || isGenerating}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
{isGenerating ? (
|
||||
<RefreshCw className="w-4 h-4 animate-spin" />
|
||||
) : (
|
||||
<Key className="w-4 h-4" />
|
||||
)}
|
||||
{isGenerating ? t('passwordGenerator.generating') : t('passwordGenerator.generatePassword')}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={generateSuggestions}
|
||||
disabled={disabled}
|
||||
className="flex items-center gap-2"
|
||||
title={t('passwordGenerator.showSuggestions')}
|
||||
>
|
||||
<Zap className="w-4 h-4" />
|
||||
{t('passwordGenerator.moreOptions')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Password Suggestions Modal */}
|
||||
{showSuggestions && (
|
||||
<div className="absolute top-full left-0 right-0 mt-2 z-50">
|
||||
<div className="bg-white border border-neutral-200 rounded-lg shadow-lg p-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h3 className="text-sm font-medium text-neutral-900">{t('passwordGenerator.suggestions')}</h3>
|
||||
<button
|
||||
onClick={() => setShowSuggestions(false)}
|
||||
className="text-neutral-400 hover:text-neutral-600"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
{suggestions.map((password, index) => {
|
||||
const strength = getPasswordStrength(password);
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center justify-between p-2 border border-neutral-100 rounded-md hover:bg-neutral-50"
|
||||
>
|
||||
<div className="flex-1 min-w-0">
|
||||
<code className="text-sm font-mono text-neutral-800 break-all">
|
||||
{password}
|
||||
</code>
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<span className={`text-xs font-medium ${strength.color}`}>
|
||||
{strength.label}
|
||||
</span>
|
||||
<span className="text-xs text-neutral-500">
|
||||
{password.length} {t('passwordGenerator.characters')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1 ml-2">
|
||||
<button
|
||||
onClick={() => copyToClipboard(password, index)}
|
||||
className="p-1 text-neutral-400 hover:text-neutral-600"
|
||||
title={t('passwordGenerator.copyPassword')}
|
||||
>
|
||||
{copiedIndex === index ? (
|
||||
<Check className="w-4 h-4 text-green-600" />
|
||||
) : (
|
||||
<Copy className="w-4 h-4" />
|
||||
)}
|
||||
</button>
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="xs"
|
||||
onClick={() => selectPassword(password)}
|
||||
>
|
||||
{t('passwordGenerator.use')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="mt-3 p-2 bg-blue-50 rounded-md">
|
||||
<p className="text-xs text-blue-800">
|
||||
<strong>{t('passwordGenerator.pattern')}</strong> {t('passwordGenerator.patternDescription')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PasswordGenerator;
|
||||
Reference in New Issue
Block a user