"use client";
import { useState, useRef, useEffect } from "react";
import {
FileText,
Check,
ExternalLink,
ScrollText,
AlertTriangle,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Checkbox } from "@/components/ui/checkbox";
import { Label } from "@/components/ui/label";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Separator } from "@/components/ui/separator";
import { cn } from "@/lib/utils";
const termsContent = `Terms of Service
Last updated: December 2024
1. Acceptance of Terms
By accessing and using this service, you accept and agree to be bound by the terms and provision of this agreement.
2. Use License
Permission is granted to temporarily use this service for personal, non-commercial transitory viewing only. This is the grant of a license, not a transfer of title.
3. User Account
You are responsible for maintaining the confidentiality of your account and password. You agree to accept responsibility for all activities that occur under your account.
4. Privacy Policy
Your use of this service is also governed by our Privacy Policy. Please review our Privacy Policy, which also governs the site and informs users of our data collection practices.
5. Prohibited Uses
You may not use the service for any unlawful purpose or to solicit others to perform or participate in any unlawful acts.
6. Intellectual Property
The service and its original content, features, and functionality are and will remain the exclusive property of the company.
7. Termination
We may terminate or suspend your account immediately, without prior notice or liability, for any reason whatsoever, including without limitation if you breach the Terms.
8. Limitation of Liability
In no event shall the company, nor its directors, employees, partners, agents, suppliers, or affiliates, be liable for any indirect, incidental, special, consequential, or punitive damages.
9. Changes to Terms
We reserve the right, at our sole discretion, to modify or replace these Terms at any time. We will provide notice of any changes by posting the new Terms on this page.
10. Contact Us
If you have any questions about these Terms, please contact us at [email protected].`;
export const title = "React Dialog Block Terms Accept";
export default function DialogTermsAccept() {
const [open, setOpen] = useState(false);
const [hasScrolledToBottom, setHasScrolledToBottom] = useState(false);
const [termsAccepted, setTermsAccepted] = useState(false);
const [privacyAccepted, setPrivacyAccepted] = useState(false);
const scrollRef = useRef<HTMLDivElement>(null);
const handleScroll = (event: React.UIEvent<HTMLDivElement>) => {
const target = event.target as HTMLDivElement;
const isAtBottom =
Math.abs(target.scrollHeight - target.scrollTop - target.clientHeight) < 10;
if (isAtBottom) {
setHasScrolledToBottom(true);
}
};
const handleAccept = () => {
// In real app: record acceptance with timestamp
const acceptanceRecord = {
timestamp: new Date().toISOString(),
termsVersion: "2024.12",
privacyVersion: "2024.12",
};
console.log("Terms accepted:", acceptanceRecord);
setOpen(false);
};
const handleClose = () => {
setOpen(false);
setTimeout(() => {
setHasScrolledToBottom(false);
setTermsAccepted(false);
setPrivacyAccepted(false);
}, 200);
};
const canAccept = hasScrolledToBottom && termsAccepted && privacyAccepted;
return (
<AlertDialog open={open} onOpenChange={setOpen}>
<div className="flex min-h-[350px] items-center justify-center">
<AlertDialogTrigger asChild>
<Button variant="outline">
<FileText className="mr-2 h-4 w-4" />
Terms & Conditions
</Button>
</AlertDialogTrigger>
</div>
<AlertDialogContent className="sm:max-w-lg">
<AlertDialogHeader>
<AlertDialogTitle className="flex items-center gap-2">
<ScrollText className="h-5 w-5" />
Terms of Service
</AlertDialogTitle>
<AlertDialogDescription>
Please review and accept our terms to continue.
</AlertDialogDescription>
</AlertDialogHeader>
<div className="space-y-4 py-4">
{/* Version Info */}
<div className="flex items-center justify-between">
<Badge variant="secondary">Version 2024.12</Badge>
<Button variant="link" size="sm" className="h-auto p-0 text-xs">
View full document
<ExternalLink className="ml-1 h-3 w-3" />
</Button>
</div>
{/* Scrollable Terms Content */}
<div className="relative">
<ScrollArea
className="h-[200px] rounded-lg border p-4"
onScrollCapture={handleScroll}
>
<div className="text-sm text-muted-foreground whitespace-pre-line">
{termsContent}
</div>
</ScrollArea>
{!hasScrolledToBottom && (
<div className="absolute bottom-0 left-0 right-0 h-12 bg-gradient-to-t from-background to-transparent pointer-events-none flex items-end justify-center pb-2">
<span className="text-xs text-muted-foreground">
Scroll to read all terms
</span>
</div>
)}
</div>
{hasScrolledToBottom && (
<div className="flex items-center gap-2 rounded-lg border border-primary/50 p-2">
<Check className="h-4 w-4 text-primary" />
<span className="text-xs text-muted-foreground">
You've reviewed the terms
</span>
</div>
)}
<Separator />
{/* Agreement Checkboxes */}
<div className="space-y-3">
<div className="flex items-start gap-3">
<Checkbox
id="terms"
checked={termsAccepted}
onCheckedChange={(checked) => setTermsAccepted(checked === true)}
disabled={!hasScrolledToBottom}
/>
<div className="space-y-1">
<Label
htmlFor="terms"
className={cn(
"text-sm cursor-pointer",
!hasScrolledToBottom && "text-muted-foreground"
)}
>
I agree to the Terms of Service{" "}
<span className="text-destructive">*</span>
</Label>
<p className="text-xs text-muted-foreground">
By checking this box, you acknowledge that you have read and agree
to be bound by our terms.
</p>
</div>
</div>
<div className="flex items-start gap-3">
<Checkbox
id="privacy"
checked={privacyAccepted}
onCheckedChange={(checked) => setPrivacyAccepted(checked === true)}
disabled={!hasScrolledToBottom}
/>
<div className="space-y-1">
<Label
htmlFor="privacy"
className={cn(
"text-sm cursor-pointer",
!hasScrolledToBottom && "text-muted-foreground"
)}
>
I agree to the Privacy Policy{" "}
<span className="text-destructive">*</span>
</Label>
<p className="text-xs text-muted-foreground">
Your data will be processed according to our{" "}
<Button variant="link" className="h-auto p-0 text-xs">
Privacy Policy
</Button>
.
</p>
</div>
</div>
</div>
{/* Warning for declining */}
<div className="flex items-start gap-2 rounded-lg border p-3">
<AlertTriangle className="h-4 w-4 text-muted-foreground mt-0.5" />
<p className="text-xs text-muted-foreground">
If you decline, you won't be able to use certain features of the
service. You can review and accept these terms later from your account
settings.
</p>
</div>
</div>
<AlertDialogFooter>
<AlertDialogCancel onClick={handleClose}>Decline</AlertDialogCancel>
<AlertDialogAction onClick={handleAccept} disabled={!canAccept}>
<Check className="mr-2 h-4 w-4" />
Accept & Continue
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
}