ShadcnUI Vaults

Back to Blocks

Confidential Access System

Unknown Block

UnknownComponent

Confidential Access System

Premium content access control with authentication, upgrade prompts, and animated purchase flow

Preview

Full width desktop view

Code

pricing-2.tsx
1"use client";
2
3import React, { useState, useEffect } from "react";
4import { motion, AnimatePresence } from "framer-motion";
5import {
6  Lock,
7  Shield,
8  Crown,
9  Check,
10  X,
11  ChevronRight,
12  Star,
13  Zap,
14  Users,
15  FileText,
16  AlertCircle,
17  Loader2,
18  Eye,
19  EyeOff,
20} from "lucide-react";
21import { Button } from "@/components/ui/button";
22import { Card } from "@/components/ui/card";
23import { Badge } from "@/components/ui/badge";
24import { Alert, AlertDescription } from "@/components/ui/alert";
25import { Separator } from "@/components/ui/separator";
26import { Progress } from "@/components/ui/progress";
27
28interface User {
29  id: string;
30  name: string;
31  email: string;
32  isLoggedIn: boolean;
33  permissions: string[];
34  subscriptionTier: "free" | "premium" | "enterprise";
35}
36
37interface PricingTier {
38  id: string;
39  name: string;
40  price: number;
41  period: string;
42  features: string[];
43  popular?: boolean;
44  icon: React.ReactNode;
45}
46
47interface ConfidentialAccessSystemProps {
48  user?: User;
49  requiredPermission?: string;
50  contentTitle?: string;
51  contentDescription?: string;
52  onPurchase?: (tierId: string) => Promise<void>;
53  onLogin?: () => void;
54}
55
56const ConfidentialAccessSystem: React.FC<ConfidentialAccessSystemProps> = ({
57  user = {
58    id: "1",
59    name: "John Doe",
60    email: "john@example.com",
61    isLoggedIn: true,
62    permissions: ["basic"],
63    subscriptionTier: "free",
64  },
65  requiredPermission = "premium",
66  contentTitle = "Confidential Business Report",
67  contentDescription = "Access exclusive insights and strategic analysis reserved for premium members.",
68  onPurchase = async (tierId: string) => {
69    await new Promise((resolve) => setTimeout(resolve, 2000));
70  },
71  onLogin = () => console.log("Login triggered"),
72}) => {
73  const [currentView, setCurrentView] = useState<
74    "access-denied" | "upgrade-prompt" | "loading" | "error"
75  >("access-denied");
76  const [isLoading, setIsLoading] = useState(false);
77  const [selectedTier, setSelectedTier] = useState<string | null>(null);
78  const [showPreview, setShowPreview] = useState(false);
79  const [purchaseProgress, setPurchaseProgress] = useState(0);
80
81  const pricingTiers: PricingTier[] = [
82    {
83      id: "premium",
84      name: "Premium",
85      price: 29,
86      period: "month",
87      icon: <Crown className="h-6 w-6" />,
88      features: [
89        "Access to confidential reports",
90        "Advanced analytics dashboard",
91        "Priority customer support",
92        "Monthly strategy sessions",
93        "Export capabilities",
94      ],
95    },
96    {
97      id: "enterprise",
98      name: "Enterprise",
99      price: 99,
100      period: "month",
101      popular: true,
102      icon: <Shield className="h-6 w-6" />,
103      features: [
104        "Everything in Premium",
105        "Custom integrations",
106        "Dedicated account manager",
107        "Advanced security features",
108        "API access",
109        "White-label options",
110      ],
111    },
112  ];
113
114  const hasAccess =
115    user.isLoggedIn && user.permissions.includes(requiredPermission);
116
117  useEffect(() => {
118    if (!user.isLoggedIn) {
119      setCurrentView("access-denied");
120    } else if (!hasAccess) {
121      setCurrentView("upgrade-prompt");
122    }
123  }, [user, hasAccess]);
124
125  const handlePurchase = async (tierId: string) => {
126    alert(`Tier ID ${tierId}`);
127  };
128
129  const AccessDeniedView = () => (
130    <motion.div
131      initial={{ opacity: 0, y: 20 }}
132      animate={{ opacity: 1, y: 0 }}
133      exit={{ opacity: 0, y: -20 }}
134      className="min-h-screen bg-gradient-to-br from-background via-background to-muted/20 flex items-center justify-center p-4"
135    >
136      <Card className="w-full max-w-2xl p-8 text-center border-2 border-border/50 shadow-2xl">
137        <motion.div
138          initial={{ scale: 0 }}
139          animate={{ scale: 1 }}
140          transition={{ delay: 0.2, type: "spring", stiffness: 200 }}
141          className="mx-auto w-20 h-20 bg-destructive/10 rounded-full flex items-center justify-center mb-6"
142        >
143          <Lock className="h-10 w-10 text-destructive" />
144        </motion.div>
145
146        <motion.h1
147          initial={{ opacity: 0 }}
148          animate={{ opacity: 1 }}
149          transition={{ delay: 0.3 }}
150          className="text-3xl font-bold text-foreground mb-4"
151        >
152          {!user.isLoggedIn ? "Authentication Required" : "Access Restricted"}
153        </motion.h1>
154
155        <motion.p
156          initial={{ opacity: 0 }}
157          animate={{ opacity: 1 }}
158          transition={{ delay: 0.4 }}
159          className="text-muted-foreground text-lg mb-6"
160        >
161          {!user.isLoggedIn
162            ? "Please log in to view this confidential content."
163            : `This content requires ${requiredPermission} access level.`}
164        </motion.p>
165
166        <motion.div
167          initial={{ opacity: 0 }}
168          animate={{ opacity: 1 }}
169          transition={{ delay: 0.5 }}
170          className="bg-muted/30 rounded-lg p-6 mb-8"
171        >
172          <h3 className="font-semibold text-foreground mb-2">{contentTitle}</h3>
173          <p className="text-muted-foreground text-sm">{contentDescription}</p>
174
175          <div className="mt-4 flex items-center justify-center gap-2">
176            <Button
177              variant="ghost"
178              size="sm"
179              onClick={() => setShowPreview(!showPreview)}
180              className="text-muted-foreground hover:text-foreground"
181            >
182              {showPreview ? (
183                <EyeOff className="h-4 w-4 mr-2" />
184              ) : (
185                <Eye className="h-4 w-4 mr-2" />
186              )}
187              {showPreview ? "Hide Preview" : "Show Preview"}
188            </Button>
189          </div>
190
191          <AnimatePresence>
192            {showPreview && (
193              <motion.div
194                initial={{ opacity: 0, height: 0 }}
195                animate={{ opacity: 1, height: "auto" }}
196                exit={{ opacity: 0, height: 0 }}
197                className="mt-4 p-4 bg-background/50 rounded border border-border/50 relative overflow-hidden"
198              >
199                <div className="blur-sm">
200                  <div className="h-4 bg-muted rounded mb-2"></div>
201                  <div className="h-4 bg-muted rounded w-3/4 mb-2"></div>
202                  <div className="h-4 bg-muted rounded w-1/2"></div>
203                </div>
204                <div className="absolute inset-0 flex items-center justify-center bg-background/80">
205                  <Badge variant="secondary" className="font-medium">
206                    <Lock className="h-3 w-3 mr-1" />
207                    Premium Content
208                  </Badge>
209                </div>
210              </motion.div>
211            )}
212          </AnimatePresence>
213        </motion.div>
214
215        <motion.div
216          initial={{ opacity: 0, y: 20 }}
217          animate={{ opacity: 1, y: 0 }}
218          transition={{ delay: 0.6 }}
219          className="flex flex-col sm:flex-row gap-4 justify-center"
220        >
221          {!user.isLoggedIn ? (
222            <Button onClick={onLogin} size="lg" className="group">
223              Sign In
224              <ChevronRight className="ml-2 h-4 w-4 group-hover:translate-x-1 transition-transform" />
225            </Button>
226          ) : (
227            <>
228              <Button
229                onClick={() => setCurrentView("upgrade-prompt")}
230                size="lg"
231                className="group"
232              >
233                <Crown className="mr-2 h-4 w-4" />
234                Upgrade Access
235                <ChevronRight className="ml-2 h-4 w-4 group-hover:translate-x-1 transition-transform" />
236              </Button>
237              <Button variant="outline" size="lg">
238                Contact Support
239              </Button>
240            </>
241          )}
242        </motion.div>
243
244        {user.isLoggedIn && (
245          <motion.div
246            initial={{ opacity: 0 }}
247            animate={{ opacity: 1 }}
248            transition={{ delay: 0.7 }}
249            className="mt-6 pt-6 border-t border-border/50"
250          >
251            <p className="text-sm text-muted-foreground">
252              Logged in as{" "}
253              <span className="font-medium text-foreground">{user.name}</span>
254            </p>
255            <Badge variant="outline" className="mt-2">
256              {user.subscriptionTier} plan
257            </Badge>
258          </motion.div>
259        )}
260      </Card>
261    </motion.div>
262  );
263
264  const UpgradePromptView = () => (
265    <motion.div
266      initial={{ opacity: 0 }}
267      animate={{ opacity: 1 }}
268      exit={{ opacity: 0 }}
269      className="min-h-screen bg-gradient-to-br from-background via-background to-primary/5 p-4"
270    >
271      <div className="max-w-6xl mx-auto py-12">
272        <motion.div
273          initial={{ opacity: 0, y: 20 }}
274          animate={{ opacity: 1, y: 0 }}
275          className="text-center mb-12"
276        >
277          <Badge variant="secondary" className="mb-4">
278            <Zap className="h-3 w-3 mr-1" />
279            Upgrade Required
280          </Badge>
281          <h1 className="text-4xl font-bold text-foreground mb-4">
282            Unlock Premium Content
283          </h1>
284          <p className="text-xl text-muted-foreground max-w-2xl mx-auto">
285            Get access to exclusive reports, advanced analytics, and premium
286            features
287          </p>
288        </motion.div>
289
290        <div className="grid md:grid-cols-2 gap-8 mb-12">
291          {pricingTiers.map((tier, index) => (
292            <motion.div
293              key={tier.id}
294              initial={{ opacity: 0, y: 20 }}
295              animate={{ opacity: 1, y: 0 }}
296              transition={{ delay: index * 0.1 }}
297            >
298              <Card
299                className={`relative p-8 h-full ${
300                  tier.popular
301                    ? "border-primary shadow-lg scale-105"
302                    : "border-border"
303                }`}
304              >
305                {tier.popular && (
306                  <Badge className="absolute -top-3 left-1/2 transform -translate-x-1/2">
307                    <Star className="h-3 w-3 mr-1" />
308                    Most Popular
309                  </Badge>
310                )}
311
312                <div className="flex items-center gap-3 mb-6">
313                  <div className="p-2 bg-primary/10 rounded-lg text-primary">
314                    {tier.icon}
315                  </div>
316                  <div>
317                    <h3 className="text-2xl font-bold text-foreground">
318                      {tier.name}
319                    </h3>
320                    <div className="flex items-baseline gap-1">
321                      <span className="text-3xl font-bold text-foreground">
322                        ${tier.price}
323                      </span>
324                      <span className="text-muted-foreground">
325                        /{tier.period}
326                      </span>
327                    </div>
328                  </div>
329                </div>
330
331                <ul className="space-y-3 mb-8">
332                  {tier.features.map((feature, featureIndex) => (
333                    <li key={featureIndex} className="flex items-center gap-3">
334                      <Check className="h-4 w-4 text-primary flex-shrink-0" />
335                      <span className="text-muted-foreground">{feature}</span>
336                    </li>
337                  ))}
338                </ul>
339
340                <Button
341                  onClick={() => handlePurchase(tier.id)}
342                  disabled={isLoading}
343                  className={`w-full ${
344                    tier.popular ? "bg-primary hover:bg-primary/90" : ""
345                  }`}
346                  variant={tier.popular ? "default" : "outline"}
347                >
348                  {isLoading && selectedTier === tier.id ? (
349                    <Loader2 className="h-4 w-4 mr-2 animate-spin" />
350                  ) : (
351                    <Crown className="h-4 w-4 mr-2" />
352                  )}
353                  Choose {tier.name}
354                </Button>
355              </Card>
356            </motion.div>
357          ))}
358        </div>
359
360        <motion.div
361          initial={{ opacity: 0, y: 20 }}
362          animate={{ opacity: 1, y: 0 }}
363          transition={{ delay: 0.3 }}
364          className="text-center"
365        >
366          <Card className="p-6 bg-muted/30">
367            <div className="flex flex-col sm:flex-row items-center justify-center gap-6">
368              <div className="flex items-center gap-2 text-muted-foreground">
369                <Users className="h-5 w-5" />
370                <span>Trusted by 10,000+ professionals</span>
371              </div>
372              <Separator
373                orientation="vertical"
374                className="hidden sm:block h-6"
375              />
376              <div className="flex items-center gap-2 text-muted-foreground">
377                <Shield className="h-5 w-5" />
378                <span>Enterprise-grade security</span>
379              </div>
380              <Separator
381                orientation="vertical"
382                className="hidden sm:block h-6"
383              />
384              <div className="flex items-center gap-2 text-muted-foreground">
385                <FileText className="h-5 w-5" />
386                <span>Cancel anytime</span>
387              </div>
388            </div>
389          </Card>
390        </motion.div>
391
392        <motion.div
393          initial={{ opacity: 0 }}
394          animate={{ opacity: 1 }}
395          transition={{ delay: 0.4 }}
396          className="text-center mt-8"
397        >
398          <Button
399            variant="ghost"
400            onClick={() => setCurrentView("access-denied")}
401            className="text-muted-foreground hover:text-foreground"
402          >
403            ← Back to content
404          </Button>
405        </motion.div>
406      </div>
407    </motion.div>
408  );
409
410  const LoadingView = () => (
411    <motion.div
412      initial={{ opacity: 0 }}
413      animate={{ opacity: 1 }}
414      exit={{ opacity: 0 }}
415      className="min-h-screen bg-background flex items-center justify-center p-4"
416    >
417      <Card className="w-full max-w-md p-8 text-center">
418        <motion.div
419          animate={{ rotate: 360 }}
420          transition={{ duration: 2, repeat: Infinity, ease: "linear" }}
421          className="mx-auto w-16 h-16 bg-primary/10 rounded-full flex items-center justify-center mb-6"
422        >
423          <Loader2 className="h-8 w-8 text-primary animate-spin" />
424        </motion.div>
425
426        <h2 className="text-2xl font-bold text-foreground mb-4">
427          Processing Your Upgrade
428        </h2>
429        <p className="text-muted-foreground mb-6">
430          Please wait while we activate your premium access...
431        </p>
432
433        <div className="space-y-4">
434          <Progress value={purchaseProgress} className="w-full" />
435          <p className="text-sm text-muted-foreground">
436            {purchaseProgress < 30 && "Verifying payment..."}
437            {purchaseProgress >= 30 &&
438              purchaseProgress < 60 &&
439              "Activating features..."}
440            {purchaseProgress >= 60 &&
441              purchaseProgress < 90 &&
442              "Setting up your account..."}
443            {purchaseProgress >= 90 && "Almost done!"}
444          </p>
445        </div>
446      </Card>
447    </motion.div>
448  );
449
450  const ErrorView = () => (
451    <motion.div
452      initial={{ opacity: 0, y: 20 }}
453      animate={{ opacity: 1, y: 0 }}
454      exit={{ opacity: 0, y: -20 }}
455      className="min-h-screen bg-background flex items-center justify-center p-4"
456    >
457      <Card className="w-full max-w-md p-8 text-center">
458        <motion.div
459          initial={{ scale: 0 }}
460          animate={{ scale: 1 }}
461          transition={{ type: "spring", stiffness: 200 }}
462          className="mx-auto w-16 h-16 bg-destructive/10 rounded-full flex items-center justify-center mb-6"
463        >
464          <X className="h-8 w-8 text-destructive" />
465        </motion.div>
466
467        <h2 className="text-2xl font-bold text-foreground mb-4">
468          Purchase Failed
469        </h2>
470        <p className="text-muted-foreground mb-6">
471          We encountered an issue processing your payment. Please try again.
472        </p>
473
474        <Alert className="mb-6">
475          <AlertCircle className="h-4 w-4" />
476          <AlertDescription>
477            If the problem persists, please contact our support team for
478            assistance.
479          </AlertDescription>
480        </Alert>
481
482        <div className="flex flex-col gap-3">
483          <Button onClick={() => setCurrentView("upgrade-prompt")}>
484            Try Again
485          </Button>
486          <Button
487            variant="outline"
488            onClick={() => setCurrentView("access-denied")}
489          >
490            Go Back
491          </Button>
492        </div>
493      </Card>
494    </motion.div>
495  );
496
497  return (
498    <div className="font-sans antialiased">
499      <AnimatePresence mode="wait">
500        {currentView === "access-denied" && <AccessDeniedView />}
501        {currentView === "upgrade-prompt" && <UpgradePromptView />}
502        {currentView === "loading" && <LoadingView />}
503        {currentView === "error" && <ErrorView />}
504      </AnimatePresence>
505    </div>
506  );
507};
508
509export default ConfidentialAccessSystem;

Dependencies

External Libraries

framer-motionlucide-reactreact

Shadcn/UI Components

alertbadgebuttoncardprogressseparator

LICENSE

MIT License

Copyright (c) 2025 Aldhaneka

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Shadcn Vaults Project (CC BY-NC 4.0 with Internal Use Exception)

All user-submitted components in the '/blocks' directory are licensed under the Creative Commons Attribution-NonCommercial 4.0 International License (CC BY-NC 4.0),
with the following clarification and exception:

You are free to:
- Share — copy and redistribute the material in any medium or format
- Adapt — remix, transform, and build upon the material

Under these conditions:
- Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made.
- NonCommercial — You may NOT use the material for commercial redistribution, resale, or monetization.

🚫 You MAY NOT:
- Sell or redistribute the components individually or as part of a product (e.g. a UI kit, template marketplace, SaaS component library)
- Offer the components or derivative works in any paid tool, theme pack, or design system

✅ You MAY:
- Use the components in internal company tools, dashboards, or applications that are not sold as products
- Remix or adapt components for private or enterprise projects
- Use them in open-source non-commercial projects

This license encourages sharing, learning, and internal innovation — but prohibits using these components as a basis for monetized products.

Full license text: https://creativecommons.org/licenses/by-nc/4.0/

By submitting a component, contributors agree to these terms.

Contributors

Ramiro Godoy@milogodoy

Review Form Block

Aldhaneka@Aldhanekaa

Project Creator

For questions about licensing, please contact the project maintainers.