clip / apps /frontend /src /components /PasswordModal.tsx
Poki01
Require admin token for private access
3bbb98d
'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>
);
}