feat(ui): survey/NudgePrompt to shadcn Button

- antd Button (primary + outlined-danger) \u2192 shadcn Button default
  + destructive-tinted outline variants.
- Inline gray palette replaced with semantic bg-background/border-
  border/text-muted-foreground tokens.
- Confirmation state's emerald check-mark tile uses categorical
  palette (existing overrides cover this).

Gates: TS \u2713 | Build \u2713.

Co-authored-by: yuneng-jiang <yuneng-berri@users.noreply.github.com>
This commit is contained in:
Cursor Agent 2026-04-23 12:51:39 +00:00
parent 7b725e498f
commit 2b5034cab3
No known key found for this signature in database

View file

@ -1,8 +1,12 @@
import React, { useEffect, useState } from "react";
import { X, LucideIcon, Check } from "lucide-react";
import { Button } from "antd";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { useDisableShowPrompts } from "@/app/(dashboard)/hooks/useDisableShowPrompts";
import { setLocalStorageItem, emitLocalStorageChange } from "@/utils/localStorageUtils";
import {
setLocalStorageItem,
emitLocalStorageChange,
} from "@/utils/localStorageUtils";
interface NudgePromptProps {
onOpen: () => void;
@ -16,8 +20,8 @@ interface NudgePromptProps {
buttonStyle?: React.CSSProperties;
}
const DISMISS_DURATION = 15000; // 15 seconds
const CONFIRMATION_DURATION = 5000; // 5 seconds
const DISMISS_DURATION = 15000;
const CONFIRMATION_DURATION = 5000;
export function NudgePrompt({
onOpen,
@ -44,7 +48,10 @@ export function NudgePrompt({
const startTime = Date.now();
const interval = setInterval(() => {
const elapsed = Date.now() - startTime;
const remaining = Math.max(0, 100 - (elapsed / DISMISS_DURATION) * 100);
const remaining = Math.max(
0,
100 - (elapsed / DISMISS_DURATION) * 100,
);
setProgress(remaining);
if (remaining <= 0) {
@ -72,21 +79,25 @@ export function NudgePrompt({
setShowConfirmation(true);
};
// Show confirmation even if disableShowPrompts is true (since we just set it)
if (showConfirmation) {
return (
<div
className={`fixed bottom-6 right-6 z-40 w-80 bg-white rounded-lg shadow-xl border border-gray-200 overflow-hidden transform transition-all duration-300 ease-out ${isVisible ? "translate-y-0 opacity-100 scale-100" : "translate-y-4 opacity-0 scale-95"
}`}
className={cn(
"fixed bottom-6 right-6 z-40 w-80 bg-background rounded-lg shadow-xl border border-border overflow-hidden transform transition-all duration-300 ease-out",
isVisible
? "translate-y-0 opacity-100 scale-100"
: "translate-y-4 opacity-0 scale-95",
)}
>
<div className="p-4">
<div className="flex items-center gap-3">
<div className="flex-shrink-0 w-8 h-8 rounded-full bg-green-100 flex items-center justify-center">
<Check className="h-5 w-5 text-green-600" />
<div className="flex-shrink-0 w-8 h-8 rounded-full bg-emerald-100 dark:bg-emerald-950 flex items-center justify-center">
<Check className="h-5 w-5 text-emerald-600 dark:text-emerald-400" />
</div>
<div className="flex-1">
<p className="text-sm text-gray-700 font-medium">
Got it, we will not ask again. Reactivate this at any time in the User Menu.
<p className="text-sm text-foreground font-medium">
Got it, we will not ask again. Reactivate this at any time in
the User Menu.
</p>
</div>
</div>
@ -95,16 +106,18 @@ export function NudgePrompt({
);
}
// Don't show the prompt if disabled (unless we're showing confirmation)
if (!isVisible || disableShowPrompts) return null;
return (
<div
className={`fixed bottom-6 right-6 z-40 w-80 bg-white rounded-lg shadow-xl border border-gray-200 overflow-hidden transform transition-all duration-300 ease-out ${isVisible ? "translate-y-0 opacity-100 scale-100" : "translate-y-4 opacity-0 scale-95"
}`}
className={cn(
"fixed bottom-6 right-6 z-40 w-80 bg-background rounded-lg shadow-xl border border-border overflow-hidden transform transition-all duration-300 ease-out",
isVisible
? "translate-y-0 opacity-100 scale-100"
: "translate-y-4 opacity-0 scale-95",
)}
>
{/* Progress bar at top showing time remaining */}
<div className="h-1 bg-gray-100 w-full">
<div className="h-1 bg-muted w-full">
<div
className="h-full transition-all duration-100 ease-linear"
style={{ width: `${progress}%`, backgroundColor: accentColor }}
@ -113,30 +126,32 @@ export function NudgePrompt({
<div className="p-4">
<div className="flex items-start justify-between mb-2">
<div className="flex items-center gap-2" style={{ color: accentColor }}>
<div
className="flex items-center gap-2"
style={{ color: accentColor }}
>
<Icon className="h-5 w-5" />
<span className="font-semibold text-sm">{title}</span>
</div>
<button
onClick={onDismiss}
className="text-gray-400 hover:text-gray-600 transition-colors p-0.5 rounded hover:bg-gray-100"
className="text-muted-foreground hover:text-foreground transition-colors p-0.5 rounded hover:bg-muted"
aria-label="Dismiss"
>
<X className="h-4 w-4" />
</button>
</div>
<p className="text-sm text-gray-600 mb-3">{description}</p>
<p className="text-sm text-muted-foreground mb-3">{description}</p>
<div className="space-y-2">
<Button type="primary" block onClick={onOpen} style={buttonStyle}>
<Button className="w-full" onClick={onOpen} style={buttonStyle}>
{buttonText}
</Button>
<Button
variant="outlined"
danger
block
variant="outline"
className="w-full text-xs text-destructive border-destructive/30 hover:bg-destructive/10"
onClick={handleDontAskAgain}
className="text-xs"
>
Don&apos;t ask me again
</Button>
@ -145,4 +160,3 @@ export function NudgePrompt({
</div>
);
}