-
Notifications
You must be signed in to change notification settings - Fork 1
up #47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
up #47
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -0,0 +1,249 @@ | ||||
| "use client" | ||||
|
|
||||
| import { useState } from "react" | ||||
| import Image from "next/image" | ||||
| import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" | ||||
| import { Container } from "@/components/ui/container" | ||||
| import { Typography } from "@/components/ui/typography" | ||||
| import { Grid } from "@/components/ui/grid" | ||||
| import { Stack } from "@/components/ui/stack" | ||||
| import { Button } from "@/components/ui/button" | ||||
| import { | ||||
| Dialog, | ||||
| DialogContent, | ||||
| DialogDescription, | ||||
| DialogHeader, | ||||
| DialogTitle, | ||||
| DialogTrigger, | ||||
| } from "@/components/ui/dialog" | ||||
| import { ExternalLinkIcon, RocketIcon } from "@radix-ui/react-icons" | ||||
|
|
||||
| // Product data interface - easily extensible for more products | ||||
| interface Product { | ||||
| id: string | ||||
| name: string | ||||
| tagline: string | ||||
| shortDescription: string | ||||
| fullDescription: string | ||||
| features: string[] | ||||
| productUrl: string | ||||
| dashboardImage: string | ||||
| status: "active" | "coming-soon" | "beta" | ||||
| category: string | ||||
| } | ||||
|
|
||||
| // Products data - add more products here as they are developed | ||||
| const products: Product[] = [ | ||||
| { | ||||
| id: "stormcom", | ||||
| name: "StormCom", | ||||
| tagline: "AI-Powered E-commerce Platform", | ||||
| shortDescription: "A comprehensive AI-based SaaS solution designed to empower e-commerce vendors of all sizes with intelligent analytics, automated operations, and data-driven insights.", | ||||
| fullDescription: `StormCom is a next-generation AI-powered e-commerce management platform built for modern online businesses. Whether you're a small boutique store or a large-scale enterprise, StormCom provides the tools you need to streamline operations, boost sales, and make smarter business decisions. | ||||
|
|
||||
| Our platform leverages cutting-edge artificial intelligence to analyze your store's performance, predict trends, and automate repetitive tasks—giving you more time to focus on what matters most: growing your business. | ||||
|
|
||||
| With real-time dashboards, intelligent reporting, and seamless integrations, StormCom transforms the way you manage your online store.`, | ||||
| features: [ | ||||
| "Real-time Revenue & Order Analytics", | ||||
| "AI-Powered Sales Predictions", | ||||
| "Smart Inventory Management", | ||||
| "Customer Behavior Insights", | ||||
| "Multi-Store Management", | ||||
| "Automated Marketing Campaigns", | ||||
| "Advanced Reporting & Data Export", | ||||
| "Team Collaboration Tools", | ||||
| "Secure Document Management", | ||||
| "24/7 Performance Monitoring" | ||||
| ], | ||||
| productUrl: "https://codestormhub.live", | ||||
| dashboardImage: "/products/stormcom-dashboard.png", | ||||
| status: "active", | ||||
| category: "E-commerce" | ||||
| }, | ||||
| // Add more products here as they are developed | ||||
| // { | ||||
| // id: "another-product", | ||||
| // name: "Product Name", | ||||
| // ... | ||||
| // } | ||||
| ] | ||||
|
|
||||
| interface ProductsSectionProps { | ||||
| className?: string | ||||
| } | ||||
|
|
||||
| export default function ProductsSection({ className }: ProductsSectionProps) { | ||||
| const [selectedProduct, setSelectedProduct] = useState<Product | null>(null) | ||||
|
Check warning on line 77 in src/components/home/products-section.tsx
|
||||
|
|
||||
| return ( | ||||
| <section className={className} aria-labelledby="products-heading"> | ||||
| <Container className="py-16 sm:py-24"> | ||||
| <Stack gap={8}> | ||||
| {/* Section Header */} | ||||
| <Stack gap={4} align="center" className="text-center"> | ||||
| <div className="inline-flex items-center gap-2 rounded-full bg-primary/10 px-4 py-1.5 text-sm font-medium text-primary"> | ||||
| <RocketIcon className="h-4 w-4" /> | ||||
| Our Products | ||||
| </div> | ||||
| <Typography variant="h2" id="products-heading"> | ||||
| Products We've Built | ||||
| </Typography> | ||||
| <Typography variant="lead" className="max-w-2xl"> | ||||
| Discover our suite of AI-powered products designed to solve real-world business challenges and drive growth | ||||
| </Typography> | ||||
| </Stack> | ||||
|
|
||||
| {/* Products Grid */} | ||||
| <Grid cols={2} gap={8} className="w-full"> | ||||
| {products.map((product) => ( | ||||
| <Dialog key={product.id}> | ||||
| <Card className="h-full overflow-hidden hover:shadow-xl transition-all duration-300 group cursor-pointer border-2 hover:border-primary/50"> | ||||
| {/* Product Preview Image */} | ||||
| <div className="relative h-48 overflow-hidden bg-gradient-to-br from-primary/20 via-primary/10 to-background"> | ||||
| <div className="absolute inset-0 bg-[url('/grid-pattern.svg')] opacity-10" /> | ||||
| <div className="absolute inset-0 flex items-center justify-center"> | ||||
| <div className="text-center p-6"> | ||||
| <div className="w-16 h-16 mx-auto mb-3 rounded-xl bg-primary/20 flex items-center justify-center group-hover:scale-110 transition-transform"> | ||||
| <RocketIcon className="h-8 w-8 text-primary" /> | ||||
| </div> | ||||
| <span className="text-xl font-bold text-foreground">{product.name}</span> | ||||
| </div> | ||||
| </div> | ||||
| {/* Status Badge */} | ||||
| <div className="absolute top-4 right-4"> | ||||
| <span className={`inline-flex items-center rounded-full px-3 py-1 text-xs font-semibold ${ | ||||
| product.status === "active" | ||||
| ? "bg-green-500/20 text-green-400 border border-green-500/30" | ||||
| : product.status === "beta" | ||||
| ? "bg-yellow-500/20 text-yellow-400 border border-yellow-500/30" | ||||
| : "bg-muted text-muted-foreground" | ||||
| }`}> | ||||
| {product.status === "active" ? "● Live" : product.status === "beta" ? "● Beta" : "Coming Soon"} | ||||
| </span> | ||||
| </div> | ||||
| </div> | ||||
|
|
||||
| <CardHeader className="pb-2"> | ||||
| <Stack gap={2}> | ||||
| <div className="flex items-center justify-between"> | ||||
| <CardTitle className="text-xl">{product.name}</CardTitle> | ||||
| <span className="inline-flex items-center rounded-md bg-muted px-2 py-1 text-xs font-medium text-muted-foreground"> | ||||
| {product.category} | ||||
| </span> | ||||
| </div> | ||||
| <span className="text-sm font-medium text-primary">{product.tagline}</span> | ||||
| </Stack> | ||||
| </CardHeader> | ||||
|
|
||||
| <CardContent> | ||||
| <Stack gap={4}> | ||||
| <CardDescription className="line-clamp-2"> | ||||
| {product.shortDescription} | ||||
| </CardDescription> | ||||
|
|
||||
| {/* Action Buttons */} | ||||
| <div className="flex gap-2 pt-2"> | ||||
| <DialogTrigger asChild> | ||||
| <Button | ||||
| variant="outline" | ||||
| size="sm" | ||||
| className="flex-1" | ||||
| onClick={() => setSelectedProduct(product)} | ||||
|
||||
| onClick={() => setSelectedProduct(product)} |
Copilot
AI
Jan 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The priority prop should be used for images that are visible above the fold on initial page load. Since this image is inside a modal dialog that only opens on user interaction, it doesn't meet the criteria for priority loading. Setting priority=true on non-critical images can negatively impact Core Web Vitals by delaying more important resources. Remove the priority prop to optimize performance.
| priority |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| "use client" | ||
|
|
||
| import * as React from "react" | ||
| import * as DialogPrimitive from "@radix-ui/react-dialog" | ||
| import { Cross2Icon } from "@radix-ui/react-icons" | ||
|
|
||
| import { cn } from "@/lib/utils" | ||
|
|
||
| const Dialog = DialogPrimitive.Root | ||
|
|
||
| const DialogTrigger = DialogPrimitive.Trigger | ||
|
|
||
| const DialogPortal = DialogPrimitive.Portal | ||
|
|
||
| const DialogClose = DialogPrimitive.Close | ||
|
|
||
| const DialogOverlay = React.forwardRef< | ||
| React.ElementRef<typeof DialogPrimitive.Overlay>, | ||
| React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay> | ||
| >(({ className, ...props }, ref) => ( | ||
| <DialogPrimitive.Overlay | ||
| ref={ref} | ||
| className={cn( | ||
| "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", | ||
| className | ||
| )} | ||
| {...props} | ||
| /> | ||
| )) | ||
| DialogOverlay.displayName = DialogPrimitive.Overlay.displayName | ||
|
|
||
| const DialogContent = React.forwardRef< | ||
| React.ElementRef<typeof DialogPrimitive.Content>, | ||
| React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> | ||
| >(({ className, children, ...props }, ref) => ( | ||
| <DialogPortal> | ||
| <DialogOverlay /> | ||
| <DialogPrimitive.Content | ||
| ref={ref} | ||
| className={cn( | ||
| "fixed left-[50%] top-[50%] z-50 grid w-full max-w-4xl translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg", | ||
| className | ||
| )} | ||
| {...props} | ||
| > | ||
| {children} | ||
| <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"> | ||
| <Cross2Icon className="h-4 w-4" /> | ||
| <span className="sr-only">Close</span> | ||
| </DialogPrimitive.Close> | ||
| </DialogPrimitive.Content> | ||
| </DialogPortal> | ||
| )) | ||
| DialogContent.displayName = DialogPrimitive.Content.displayName | ||
|
|
||
| const DialogHeader = ({ | ||
| className, | ||
| ...props | ||
| }: React.HTMLAttributes<HTMLDivElement>) => ( | ||
| <div | ||
| className={cn( | ||
| "flex flex-col space-y-1.5 text-center sm:text-left", | ||
| className | ||
| )} | ||
| {...props} | ||
| /> | ||
| ) | ||
| DialogHeader.displayName = "DialogHeader" | ||
|
Comment on lines
+56
to
+68
|
||
|
|
||
| const DialogFooter = ({ | ||
| className, | ||
| ...props | ||
| }: React.HTMLAttributes<HTMLDivElement>) => ( | ||
| <div | ||
| className={cn( | ||
| "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", | ||
| className | ||
| )} | ||
| {...props} | ||
| /> | ||
| ) | ||
| DialogFooter.displayName = "DialogFooter" | ||
|
Comment on lines
+70
to
+82
|
||
|
|
||
| const DialogTitle = React.forwardRef< | ||
| React.ElementRef<typeof DialogPrimitive.Title>, | ||
| React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title> | ||
| >(({ className, ...props }, ref) => ( | ||
| <DialogPrimitive.Title | ||
| ref={ref} | ||
| className={cn( | ||
| "text-lg font-semibold leading-none tracking-tight", | ||
| className | ||
| )} | ||
| {...props} | ||
| /> | ||
| )) | ||
| DialogTitle.displayName = DialogPrimitive.Title.displayName | ||
|
|
||
| const DialogDescription = React.forwardRef< | ||
| React.ElementRef<typeof DialogPrimitive.Description>, | ||
| React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description> | ||
| >(({ className, ...props }, ref) => ( | ||
| <DialogPrimitive.Description | ||
| ref={ref} | ||
| className={cn("text-sm text-muted-foreground", className)} | ||
| {...props} | ||
| /> | ||
| )) | ||
| DialogDescription.displayName = DialogPrimitive.Description.displayName | ||
|
|
||
| export { | ||
| Dialog, | ||
| DialogPortal, | ||
| DialogOverlay, | ||
| DialogClose, | ||
| DialogTrigger, | ||
| DialogContent, | ||
| DialogHeader, | ||
| DialogFooter, | ||
| DialogTitle, | ||
| DialogDescription, | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The selectedProduct state is set on line 152 when clicking "View Details", but it's never actually used. The Dialog component already receives the product data directly through closure scope within the map function, making this state redundant. Consider removing this unused state to simplify the component.