chore: seminarhof customer project
This commit is contained in:
72
apps/app/src/components/ConfirmButton.tsx
Normal file
72
apps/app/src/components/ConfirmButton.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { Button, type ButtonProps } from '@pikku/mantine/core'
|
||||
import { m } from '@/i18n/messages'
|
||||
import { useLocale } from '@/i18n/config'
|
||||
import { type I18nString, type I18nNode } from '@pikku/react'
|
||||
|
||||
/**
|
||||
* A button that requires two clicks to fire, guarding against accidental
|
||||
* state transitions. The first click "arms" it — the label swaps to a
|
||||
* confirm prompt and the colour goes red; the second click commits and
|
||||
* disarms. If left untouched it auto-reverts after `resetMs`.
|
||||
*
|
||||
* Disarming happens synchronously on the confirming click (not on mutation
|
||||
* success), so a failed mutation leaves a normal, retryable button — the
|
||||
* caller's `loading` prop drives the in-flight visual.
|
||||
*/
|
||||
type ConfirmButtonProps = ButtonProps & {
|
||||
onConfirm: () => void
|
||||
/** Label shown in the armed state. Defaults to the localized "Confirm?". */
|
||||
confirmLabel?: I18nString
|
||||
/** Colour while armed. */
|
||||
confirmColor?: string
|
||||
/** Auto-revert delay in ms. */
|
||||
resetMs?: number
|
||||
children?: I18nNode
|
||||
'data-testid'?: string
|
||||
}
|
||||
|
||||
export function ConfirmButton({
|
||||
onConfirm,
|
||||
confirmLabel,
|
||||
confirmColor = 'red',
|
||||
resetMs = 3000,
|
||||
color,
|
||||
variant,
|
||||
children,
|
||||
'data-testid': testId,
|
||||
...rest
|
||||
}: ConfirmButtonProps) {
|
||||
useLocale()
|
||||
const [armed, setArmed] = useState(false)
|
||||
const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)
|
||||
|
||||
useEffect(() => () => clearTimeout(timer.current), [])
|
||||
|
||||
const handleClick = () => {
|
||||
if (armed) {
|
||||
clearTimeout(timer.current)
|
||||
setArmed(false)
|
||||
onConfirm()
|
||||
return
|
||||
}
|
||||
setArmed(true)
|
||||
timer.current = setTimeout(() => setArmed(false), resetMs)
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
{...rest}
|
||||
key={armed ? 'armed' : 'idle'}
|
||||
data-testid={armed && testId ? `${testId}-confirm` : testId}
|
||||
data-confirm-state={armed ? 'armed' : 'idle'}
|
||||
color={armed ? confirmColor : color}
|
||||
variant={armed ? 'filled' : variant}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{armed
|
||||
? confirmLabel ?? m.common_pages_admin_booking_status_actions_confirm()
|
||||
: children}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user