File size: 8,055 Bytes
d988ae4 3bbb98d d988ae4 3bbb98d d988ae4 3bbb98d d988ae4 3bbb98d d988ae4 3bbb98d d988ae4 3bbb98d d988ae4 3bbb98d d988ae4 3bbb98d d988ae4 3bbb98d d988ae4 3bbb98d d988ae4 3bbb98d d988ae4 3bbb98d d988ae4 3bbb98d d988ae4 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
'use client';
import { useState } from 'react';
import Modal from './ui/Modal';
interface PasswordModalProps {
isOpen: boolean;
onClose: () => void;
onSubmit: (password: string) => void;
isLoading: boolean;
}
export default function PasswordModal({
isOpen,
onClose,
onSubmit,
isLoading
}: PasswordModalProps) {
const [password, setPassword] = useState('');
const [passwordError, setPasswordError] = useState('');
const [showPassword, setShowPassword] = useState(false);
const handleSubmit = () => {
setPasswordError('');
if (!password.trim()) {
setPasswordError('Please enter a password');
return;
}
if (password.length < 4) {
setPasswordError('Password must be at least 4 characters');
return;
}
onSubmit(password);
};
const handleClose = () => {
setPassword('');
setPasswordError('');
setShowPassword(false);
onClose();
};
const passwordIcon = (
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
</svg>
);
return (
<Modal
isOpen={isOpen}
onClose={handleClose}
title="Set Password"
icon={passwordIcon}
>
<div className="space-y-5">
<div className="rounded-lg border border-primary/15 bg-primary/5 px-4 py-3 flex items-start gap-3">
<div className="p-2 rounded-full bg-primary/10 text-primary">
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M12 20a8 8 0 100-16 8 8 0 000 16z" />
</svg>
</div>
<div>
<p className="text-text-primary font-medium">Add a passcode for extra privacy</p>
<p className="text-text-secondary text-sm">Anyone accessing this clipboard will need the code you choose.</p>
</div>
</div>
<div className="space-y-3">
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 text-text-secondary/60" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
</svg>
</div>
<input
type={showPassword ? 'text' : 'password'}
value={password}
onChange={(e) => {
setPassword(e.target.value);
if (passwordError) setPasswordError('');
}}
placeholder="Create a passcode"
className="w-full pl-10 pr-12 py-3 rounded-lg bg-surface/80 border border-surface-hover focus:border-primary focus:ring-4 focus:ring-primary/20 focus:outline-none transition-colors duration-300 ease-in-out text-text-primary placeholder-text-secondary/50 font-mono shadow-inner"
autoFocus
autoComplete="off"
/>
<button
type="button"
onClick={() => setShowPassword((prev) => !prev)}
className="absolute inset-y-0 right-0 px-3 flex items-center text-text-secondary/70 hover:text-text-primary transition-colors"
aria-label={showPassword ? 'Hide password' : 'Show password'}
>
{showPassword ? (
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13.875 18.825A10.05 10.05 0 0112 19c-5 0-9.27-3.11-11-7 1.05-2.38 2.9-4.36 5.1-5.66m4.02-1.09A9.98 9.98 0 0112 5c5 0 9.27 3.11 11 7a11.72 11.72 0 01-1.67 2.63M15 12a3 3 0 00-3-3m0 0a3 3 0 013 3m-3-3c-.64 0-1.26.19-1.77.52m0 0L3 21m7.23-8.48a3 3 0 104.24 4.24" />
</svg>
) : (
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
)}
</button>
</div>
<div className="grid grid-cols-2 gap-3 text-xs text-text-secondary">
<div className="flex items-center gap-2 rounded-md bg-surface/60 border border-surface-hover px-3 py-2">
<span className="inline-flex h-5 w-5 items-center justify-center rounded-full bg-primary/10 text-primary">4+</span>
<span>At least 4 characters</span>
</div>
<div className="flex items-center gap-2 rounded-md bg-surface/60 border border-surface-hover px-3 py-2">
<span className="inline-flex h-5 w-5 items-center justify-center rounded-full bg-primary/10 text-primary">✦</span>
<span>Mix letters & numbers</span>
</div>
</div>
{passwordError && (
<div className="mt-1 text-error text-sm flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 mr-1 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span>{passwordError}</span>
</div>
)}
</div>
<div className="flex space-x-3">
<button
type="button"
onClick={handleClose}
className="flex-1 py-2.5 px-4 border border-surface-hover bg-surface/70 hover:bg-surface-hover text-text-primary font-medium rounded-lg transition-all duration-200 hover:-translate-y-0.5"
>
Cancel
</button>
<button
type="button"
onClick={handleSubmit}
disabled={isLoading}
className="flex-1 py-2.5 px-4 bg-gradient-to-r from-primary to-primary-dark hover:brightness-110 text-white font-medium rounded-lg flex items-center justify-center transition-all duration-200 shadow-lg shadow-primary/20 disabled:opacity-50 disabled:cursor-not-allowed"
>
{isLoading ? (
<span className="flex items-center justify-center">
<svg className="animate-spin -ml-1 mr-2 h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Creating...
</span>
) : (
<span className="flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
</svg>
Create & Lock
</span>
)}
</button>
</div>
</div>
</Modal>
);
}
|