* feat: reorganize admin panel around accounts * feat: expand admin customer account controls * feat: add admin account detail management * fix: remove unused admin account icon import * refactor: design critique fixes for account pages - Admin accounts: replace dense card grid with compact DataTable - Account settings: remove redundant hero card, stat grid, header pills - Fix bg-accent (orange) misuse on decorative elements across 7 files - Add ConfirmButton for destructive actions (deactivate, remove member) - Replace single-field modals with inline editing (plan, trial) - Add contextual help: display code tooltip, improved empty states - Non-owner aside explanation for hidden owner-only sections - Admin sidebar: group 11 items into 5 labeled sections - Rename UsersPage.tsx → AccountsPage.tsx to match route - Fix border radius consistency, hide zero-count badges Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use get_admin_db for all new admin account endpoints All admin endpoints query across tenants without a tenant context. get_db (app-role, subject to RLS) was never imported and would crash at runtime — replace all 6 occurrences with get_admin_db (BYPASSRLS). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
70 lines
1.5 KiB
TypeScript
70 lines
1.5 KiB
TypeScript
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
import { cn } from '@/lib/utils'
|
|
|
|
interface ConfirmButtonProps {
|
|
onConfirm: () => void
|
|
children: React.ReactNode
|
|
confirmLabel?: string
|
|
className?: string
|
|
confirmClassName?: string
|
|
timeoutMs?: number
|
|
'aria-label'?: string
|
|
}
|
|
|
|
/**
|
|
* Two-click inline confirm button.
|
|
* First click arms the button (shows confirm state).
|
|
* Second click executes the action.
|
|
* Auto-resets after timeoutMs (default 3000ms).
|
|
*/
|
|
export function ConfirmButton({
|
|
onConfirm,
|
|
children,
|
|
confirmLabel = 'Confirm?',
|
|
className,
|
|
confirmClassName,
|
|
timeoutMs = 3000,
|
|
'aria-label': ariaLabel,
|
|
}: ConfirmButtonProps) {
|
|
const [armed, setArmed] = useState(false)
|
|
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
|
|
const reset = useCallback(() => {
|
|
setArmed(false)
|
|
if (timerRef.current) {
|
|
clearTimeout(timerRef.current)
|
|
timerRef.current = null
|
|
}
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
if (timerRef.current) clearTimeout(timerRef.current)
|
|
}
|
|
}, [])
|
|
|
|
const handleClick = () => {
|
|
if (armed) {
|
|
reset()
|
|
onConfirm()
|
|
} else {
|
|
setArmed(true)
|
|
timerRef.current = setTimeout(reset, timeoutMs)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<button
|
|
type="button"
|
|
onClick={handleClick}
|
|
onBlur={reset}
|
|
aria-label={ariaLabel}
|
|
className={cn(armed ? confirmClassName : className)}
|
|
>
|
|
{armed ? confirmLabel : children}
|
|
</button>
|
|
)
|
|
}
|
|
|
|
export default ConfirmButton
|