passwort anzeigen
This commit is contained in:
85
src/components/CopyableCredential.jsx
Normal file
85
src/components/CopyableCredential.jsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import { useState } from 'react'
|
||||
import { FaEye, FaEyeSlash, FaCheck } from 'react-icons/fa6'
|
||||
|
||||
/**
|
||||
* Anzeige mit Auge: Klick kopiert den Wert und schaltet Sichtbarkeit um.
|
||||
*/
|
||||
export default function CopyableCredential({ value }) {
|
||||
const [visible, setVisible] = useState(false)
|
||||
const [copied, setCopied] = useState(false)
|
||||
const text = (value || '').trim()
|
||||
|
||||
if (!text) {
|
||||
return <span style={{ color: '#9ca3af' }}>–</span>
|
||||
}
|
||||
|
||||
const handleClick = async (e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
setVisible((v) => !v)
|
||||
try {
|
||||
await navigator.clipboard.writeText(text)
|
||||
} catch {
|
||||
const ta = document.createElement('textarea')
|
||||
ta.value = text
|
||||
ta.setAttribute('readonly', '')
|
||||
ta.style.position = 'fixed'
|
||||
ta.style.left = '-9999px'
|
||||
document.body.appendChild(ta)
|
||||
ta.select()
|
||||
document.execCommand('copy')
|
||||
document.body.removeChild(ta)
|
||||
}
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
}
|
||||
|
||||
const masked = '•'.repeat(Math.min(Math.max(text.length, 8), 16))
|
||||
const display = visible ? text : masked
|
||||
|
||||
return (
|
||||
<span
|
||||
className="copyable-credential"
|
||||
style={{ display: 'inline-flex', alignItems: 'center', gap: '6px', maxWidth: '100%' }}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontFamily: visible ? 'inherit' : 'monospace',
|
||||
letterSpacing: visible ? 'normal' : '1px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
maxWidth: '180px',
|
||||
}}
|
||||
title={visible ? text : 'Klick auf das Auge zum Anzeigen und Kopieren'}
|
||||
>
|
||||
{display}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
className="btn"
|
||||
onClick={handleClick}
|
||||
title={copied ? 'Kopiert!' : 'Anzeigen und in Zwischenablage kopieren'}
|
||||
style={{
|
||||
padding: '2px 6px',
|
||||
minWidth: '28px',
|
||||
lineHeight: 1,
|
||||
flexShrink: 0,
|
||||
}}
|
||||
aria-label={copied ? 'Kopiert' : 'Anzeigen und kopieren'}
|
||||
>
|
||||
{copied ? (
|
||||
<FaCheck style={{ color: '#059669' }} />
|
||||
) : visible ? (
|
||||
<FaEyeSlash />
|
||||
) : (
|
||||
<FaEye />
|
||||
)}
|
||||
</button>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
export function getCustomerPortalPassword(customer) {
|
||||
return customer?.portalPassword || customer?.password || ''
|
||||
}
|
||||
@@ -54,8 +54,12 @@ export function useCustomers() {
|
||||
|
||||
const createCustomer = async (data) => {
|
||||
if (DEMO_MODE) {
|
||||
const { password: _pw, ...rest } = data
|
||||
const newCustomer = { ...rest, $id: Date.now().toString() }
|
||||
const { password, ...rest } = data
|
||||
const newCustomer = {
|
||||
...rest,
|
||||
portalPassword: password || rest.portalPassword || '',
|
||||
$id: Date.now().toString(),
|
||||
}
|
||||
setCustomers(prev => [...prev, newCustomer])
|
||||
return { success: true, data: newCustomer }
|
||||
}
|
||||
@@ -63,7 +67,10 @@ export function useCustomers() {
|
||||
try {
|
||||
const { password, ...fields } = data
|
||||
const result = await createCustomerWithPortalAccess({ ...fields, password })
|
||||
const customer = result.customer
|
||||
const customer = {
|
||||
...result.customer,
|
||||
portalPassword: result.customer?.portalPassword || password || '',
|
||||
}
|
||||
setCustomers(prev => [...prev, customer])
|
||||
return { success: true, data: customer }
|
||||
} catch (err) {
|
||||
@@ -73,8 +80,15 @@ export function useCustomers() {
|
||||
|
||||
const updateCustomer = async (id, data) => {
|
||||
if (DEMO_MODE) {
|
||||
const { password: _pw, ...rest } = data
|
||||
setCustomers(prev => prev.map(c => c.$id === id ? { ...c, ...rest } : c))
|
||||
const { password, ...rest } = data
|
||||
setCustomers(prev =>
|
||||
prev.map(c => {
|
||||
if (c.$id !== id) return c
|
||||
const next = { ...c, ...rest }
|
||||
if (password) next.portalPassword = password
|
||||
return next
|
||||
})
|
||||
)
|
||||
return { success: true }
|
||||
}
|
||||
|
||||
@@ -84,8 +98,22 @@ export function useCustomers() {
|
||||
if (password) payload.password = password
|
||||
|
||||
const result = await updateCustomerWithPortalAccess(id, payload)
|
||||
const customer = result.customer
|
||||
setCustomers(prev => prev.map(c => c.$id === id ? customer : c))
|
||||
const customer = {
|
||||
...result.customer,
|
||||
portalPassword:
|
||||
result.customer?.portalPassword ||
|
||||
password ||
|
||||
undefined,
|
||||
}
|
||||
setCustomers(prev =>
|
||||
prev.map(c => {
|
||||
if (c.$id !== id) return c
|
||||
return {
|
||||
...customer,
|
||||
portalPassword: customer.portalPassword ?? c.portalPassword,
|
||||
}
|
||||
})
|
||||
)
|
||||
return { success: true, data: customer }
|
||||
} catch (err) {
|
||||
return { success: false, error: err.message }
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useCustomers } from '../hooks/useCustomers'
|
||||
import { useEmployees } from '../hooks/useEmployees'
|
||||
import { FaPlus, FaTrash, FaFloppyDisk, FaSpinner } from 'react-icons/fa6'
|
||||
import { FaEdit } from 'react-icons/fa'
|
||||
import CopyableCredential, { getCustomerPortalPassword } from '../components/CopyableCredential'
|
||||
|
||||
export default function AdminPage() {
|
||||
const { user, isAdmin } = useAuth()
|
||||
@@ -398,6 +399,7 @@ export default function AdminPage() {
|
||||
<th>Name</th>
|
||||
<th>Location</th>
|
||||
<th>Email</th>
|
||||
<th>Passwort</th>
|
||||
<th>Phone</th>
|
||||
<th>Portal</th>
|
||||
<th>Aktionen</th>
|
||||
@@ -499,7 +501,12 @@ export default function AdminPage() {
|
||||
<td>{customer.code || '-'}</td>
|
||||
<td>{customer.name || '-'}</td>
|
||||
<td>{customer.location || '-'}</td>
|
||||
<td>{customer.email || '-'}</td>
|
||||
<td>
|
||||
<CopyableCredential value={customer.email} />
|
||||
</td>
|
||||
<td>
|
||||
<CopyableCredential value={getCustomerPortalPassword(customer)} />
|
||||
</td>
|
||||
<td>{customer.phone || '-'}</td>
|
||||
<td>
|
||||
{customer.portalAccessEnabled ? (
|
||||
|
||||
Reference in New Issue
Block a user