Kundenseite: Kundenansicht oeffnen, letzter Login, Preview per Einmal-Link
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
370724a9a7
commit
fce2f41463
7 files changed
+92
-7
No files matched your search
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect, useCallback } from 'react'
|
||||
import { useParams, Link, useNavigate, useSearchParams } from 'react-router-dom'
|
||||
import { FaSpinner, FaStar, FaArrowLeft, FaFloppyDisk, FaArrowUp, FaKey, FaArrowUpRightFromSquare, FaBoxArchive, FaBoxOpen, FaTrash } from 'react-icons/fa6'
|
||||
import { FaSpinner, FaStar, FaArrowLeft, FaFloppyDisk, FaArrowUp, FaKey, FaBoxArchive, FaBoxOpen, FaTrash } from 'react-icons/fa6'
|
||||
import { databases, DATABASE_ID, COLLECTIONS, Query } from '../lib/appwrite'
|
||||
import { PageHeader, Card, Button, Badge, Tabs, EmptyState, Field } from '../components/ui'
|
||||
import { StatusPill, UrgentPill } from '../components/ui/StatusPill'
|
||||
@@ -13,6 +13,9 @@ import { useWebsiteProjects } from '../hooks/useWebsiteProjects'
|
||||
import { customerTabs, resolveCustomerTab } from '../lib/customerTabs'
|
||||
import { customerStage, isArchived, isInvoiceReady, setCustomerArchived, upgradeToCustomer } from '../lib/leads'
|
||||
import CustomerViewLog from '../components/customer/CustomerViewLog'
|
||||
import KundenansichtButton from '../components/customer/KundenansichtButton'
|
||||
import { getCustomerPortalStatus } from '../lib/teamApi'
|
||||
import { formatLastLogin } from '../lib/lastLogin'
|
||||
|
||||
const PORTAL_URL = 'https://project.webklar.com'
|
||||
|
||||
@@ -85,6 +88,7 @@ export default function CustomerDetailPage() {
|
||||
subtitle={[customer.location, customer.email, customer.phone].filter(Boolean).join(' · ')}
|
||||
actions={
|
||||
<>
|
||||
<KundenansichtButton customer={customer} />
|
||||
<Button variant="ghost" onClick={toggleArchived}>
|
||||
{archived ? <><FaBoxOpen /> Wiederherstellen</> : <><FaBoxArchive /> Archivieren</>}
|
||||
</Button>
|
||||
@@ -200,6 +204,15 @@ function PortalAccessCard({ customer, onSaved }) {
|
||||
|
||||
const hasAccess = Boolean(customer.portalAccessEnabled && customer.appwriteUserId)
|
||||
const currentPassword = getCustomerPortalPassword(customer)
|
||||
// Letzter Login kommt vom Portal (customerPortalAccess), nicht aus dem Kunden-Dokument
|
||||
const [lastLogin, setLastLogin] = useState(undefined)
|
||||
useEffect(() => {
|
||||
let active = true
|
||||
getCustomerPortalStatus(customer.$id)
|
||||
.then((status) => active && setLastLogin(status.lastLoginAt || null))
|
||||
.catch(() => active && setLastLogin(false))
|
||||
return () => { active = false }
|
||||
}, [customer.$id])
|
||||
|
||||
const savePassword = async () => {
|
||||
if (!customer.email) {
|
||||
@@ -232,6 +245,11 @@ function PortalAccessCard({ customer, onSaved }) {
|
||||
: <Badge tone="muted">kein Zugang</Badge>}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between" style={{ marginBottom: 12 }}>
|
||||
<span className="muted">Letzter Login</span>
|
||||
<span>{lastLogin === undefined ? '…' : lastLogin === false ? 'nicht abrufbar' : formatLastLogin(lastLogin)}</span>
|
||||
</div>
|
||||
|
||||
<Field label="Login (E-Mail)">
|
||||
<CopyableCredential value={customer.email} label="Login" />
|
||||
</Field>
|
||||
@@ -254,9 +272,7 @@ function PortalAccessCard({ customer, onSaved }) {
|
||||
<Button variant="primary" onClick={savePassword} disabled={busy}>
|
||||
{busy ? <FaSpinner className="spinner" /> : <><FaKey /> {hasAccess ? 'Passwort ändern' : 'Zugang anlegen'}</>}
|
||||
</Button>
|
||||
<Button as="a" href={PORTAL_URL} target="_blank" rel="noreferrer" variant="ghost">
|
||||
<FaArrowUpRightFromSquare /> Portal öffnen
|
||||
</Button>
|
||||
<KundenansichtButton customer={customer} variant="ghost" />
|
||||
{msg && <span className="muted">{msg}</span>}
|
||||
</div>
|
||||
<p className="muted" style={{ marginTop: 8, fontSize: 13 }}>
|
||||
|
||||
Reference in new issue
Block a user