AI Review Center
Commit Review Workspace ?
Select a commit on the left to inspect quality, security, business value, findings, and suggested code changes.
Project AI Score
?
63%
Quality Avg
?
68%
Security Avg
?
45%
Reviews
?
56
Review Result ?
jattin01/avriti · 8bb9c632
The commit adds functionality for card checkout and order management, including mapping product attributes, formatting attribute labels, and navigation to checkout or login. It improves user experience by handling UI state (wishlist, cart) and redirects based on login status. However, it relies on any typed parameters without strict types, and there is some code repetition in formatting functions across files. Security score is moderate due to localStorage usage without encryption and redirect using window.location.href, which could lead to open redirect risks if input is not sanitized.
Quality
?
75%
Security
?
60%
Business Value
?
80%
Maintainability
?
78%
Issues & Suggested Fixes
?
1AI detects the issue
2Shows file, line, or evidence
3Suggests the fix to apply
Loose Typing With 'Any' Used In Mapping Pr...
Where
app/cart/page.tsx:88
Issue / Evidence
loose typing with 'any' used in mapping product attributes
Suggested Fix
define and use specific TypeScript interfaces to reduce runtime errors and improve maintainability
Insecure Redirect Via Window.location.href...
Where
app/cart/page.tsx:168
Issue / Evidence
insecure redirect via window.location.href with user-controlled redirect
Suggested Fix
sanitize redirect paths to avoid open redirect vulnerabilities
Duplicate Logic
Where
app/checkout/page.tsx:274
Issue / Evidence
duplicate code in formatAttributeLabel function present in multiple files
Suggested Fix
extract to a shared utility function to reduce duplication and improve maintainability
Missing Validation
Where
app/login/page.tsx:38
Issue / Evidence
redirection after login without validation may allow open redirect
Suggested Fix
validate or constrain redirect URLs to internal paths only
Missing Validation
Where
app/register/page.tsx:26
Issue / Evidence
inline validation only sets error without deeper input sanitation
Suggested Fix
consider more robust validation and sanitize inputs to prevent invalid data entry
Multiple Async Handlers Modifying Global S...
Where
app/shop/page.tsx:73
Issue / Evidence
multiple async handlers modifying global state and performing side effects
Suggested Fix
ensure proper error handling and possibly debounce or throttle user actions to avoid race conditions
Too Brief And Generic
Where
commit message
Issue / Evidence
too brief and generic
Suggested Fix
provide more details about what 'complete card checkout and order' entails, including key changes and intent, to improve code review and future reference
Code Change Preview · app/cart/page.tsx
?
Removed / Before Commit
- .then((data) => { - if (data.success && Array.isArray(data.data)) { - setFeaturedProducts( - data.data.map((p: any, i: number) => ({ - slug: p.slug, - name: p.title, - attrValue: p.attributes?.find((a: any) => a.sr_no === 1)?.value ?? "", - variantName: p.variant?.name ?? "", - sellingPrice: p.attributes?.find((a: any) => a.sr_no === 1)?.selling_price ?? null, - mrp: p.attributes?.find((a: any) => a.sr_no === 1)?.mrp ?? null, - image: p.thumbnail?.[0] - ? `${process.env.NEXT_PUBLIC_API_URL}/${p.thumbnail[0]}` - : "/product2.jpeg", - tone: tones[i % tones.length], - category: p.category?.name ?? "", - badge: "New", - description: p.short_description ?? "", - }))
Added / After Commit
+ .then((data) => { + if (data.success && Array.isArray(data.data)) { + setFeaturedProducts( + data.data.map((p: any, i: number) => { + const attr = p.attributes?.find((a: any) => a.in_stock) ?? p.attributes?.find((a: any) => a.sr_no === 1); + + return { + slug: p.slug, + name: p.title, + productId: p.id, + attrValue: attr?.value ?? "", + attributeName: attr?.name ?? "", + variantName: p.variant?.name ?? "", + sellingPrice: attr?.selling_price ?? null, + mrp: attr?.mrp ?? null, + image: p.thumbnail?.[0] + ? `${process.env.NEXT_PUBLIC_API_URL}/${p.thumbnail[0]}` + : "/product2.jpeg",
Removed / Before Commit
- - import { useEffect, useState } from "react"; - import InnerBanner from "@/components/inner-banner"; - import { apiGetCart, getToken } from "@/lib/cart"; - - type CheckoutItem = { - product_id: number; - title: string; - attribute_name: string; - attribute_value: string; - selling_price: number; - mrp: number; - quantity: number; - - // ── load cart items ───────────────────────────────────────── - useEffect(() => { - // prefill email from localStorage - try {
Added / After Commit
+ + import { useEffect, useState } from "react"; + import InnerBanner from "@/components/inner-banner"; + import { apiGetCart, getToken, isLoggedIn } from "@/lib/cart"; + + type CheckoutItem = { + product_id: number; + title: string; + attribute_name: string; + attribute_value: string; + variant_name?: string; + selling_price: number; + mrp: number; + quantity: number; + + // ── load cart items ───────────────────────────────────────── + useEffect(() => { + if (!isLoggedIn()) {
Removed / Before Commit
- } - try { - // const name = data.customer.email.split("@")[0]; - localStorage.setItem("avriti_customer", JSON.stringify({ name:data.customer.full_name, email: data.customer.email, token: data.token })); - } catch {} - await mergeLocalCartToApi(); - window.location.href = "/"; - } catch { - setError(" Please try again."); - } finally {
Added / After Commit
+ } + try { + // const name = data.customer.email.split("@")[0]; + localStorage.setItem("avriti_customer", JSON.stringify({ name:data.customer.full_name, email: data.customer.email, phone: data.customer.phone, token: data.token })); + } catch {} + await mergeLocalCartToApi(); + const redirectTo = new URLSearchParams(window.location.search).get("redirect") || "/"; + window.location.href = redirectTo.startsWith("/") ? redirectTo : "/"; + } catch { + setError(" Please try again."); + } finally {
Removed / Before Commit
- "use client"; - - import { useEffect, useState } from "react"; - import { useSearchParams } from "next/navigation"; - import Image from "next/image"; - import { CreditCard, Home, MapPin, Phone, User } from "lucide-react"; - interface OrderItem { - id: number; - product_name: string; - price: number; - quantity: number; - total: number; - return <span>₹{value}</span>; - } - - const statusClassMap: Record<string, string> = { - pending: "bg-[#E7F4D9] text-[#26732F]", - confirmed: "bg-[#E9F4FF] text-[#1068A9]",
Added / After Commit
+ "use client"; + + import { Suspense, useEffect, useState } from "react"; + import { useSearchParams } from "next/navigation"; + import Image from "next/image"; + import { CreditCard, Home, MapPin, Phone, User } from "lucide-react"; + interface OrderItem { + id: number; + product_name: string; + variant_name: string | null; + attribute_value: string | null; + price: number; + quantity: number; + total: number; + return <span>₹{value}</span>; + } + + const formatAttributeLabel = (item: OrderItem) => {
Removed / Before Commit
- - const API_BASE = process.env.NEXT_PUBLIC_API_URL; - - export default function OrderlistPage() { - const [orders, setOrders] = useState<Order[]>([]); - const [loading, setLoading] = useState(true); - id: String(order.id), - orderNumber: order.order_number, - items: order.items.length, - date: new Date(order.created_at).toLocaleString("en-IN"), - status: order.order_status as - | "pending" - | "processing"
Added / After Commit
+ + const API_BASE = process.env.NEXT_PUBLIC_API_URL; + + const formatOrderDate = (value: string) => + new Intl.DateTimeFormat("en-IN", { + day: "2-digit", + month: "short", + year: "numeric", + hour: "numeric", + minute: "2-digit", + hour12: true, + timeZone: "Asia/Kolkata", + }).format(new Date(value)); + + export default function OrderlistPage() { + const [orders, setOrders] = useState<Order[]>([]); + const [loading, setLoading] = useState(true); + id: String(order.id),
Removed / Before Commit
- .then((res) => res.json()) - .then((data) => { - if (data.success && Array.isArray(data.data)) { - const mapped: FeaturedProduct[] = data.data.map((p: any, i: number) => ({ - slug: p.slug, - name: p.title, - attrValue: p.attributes?.find((a: any) => a.sr_no === 1)?.value ?? "", - variantName: p.variant?.name ?? "", - sellingPrice: p.attributes?.find((a: any) => a.sr_no === 1)?.selling_price ?? null, - mrp: p.attributes?.find((a: any) => a.sr_no === 1)?.mrp ?? null, - image: p.thumbnail?.[0] - ? `${process.env.NEXT_PUBLIC_API_URL}/${p.thumbnail[0]}` - : "/product2.jpeg", - tone: tones[i % tones.length], - category: p.category?.name ?? "", - badge: "New", - description: p.short_description ?? "", - }));
Added / After Commit
+ .then((res) => res.json()) + .then((data) => { + if (data.success && Array.isArray(data.data)) { + const mapped: FeaturedProduct[] = data.data.map((p: any, i: number) => { + const attr = p.attributes?.find((a: any) => a.in_stock) ?? p.attributes?.find((a: any) => a.sr_no === 1); + + return { + slug: p.slug, + name: p.title, + productId: p.id, + attrValue: attr?.value ?? "", + attributeName: attr?.name ?? "", + variantName: p.variant?.name ?? "", + sellingPrice: attr?.selling_price ?? null, + mrp: attr?.mrp ?? null, + image: p.thumbnail?.[0] + ? `${process.env.NEXT_PUBLIC_API_URL}/${p.thumbnail[0]}` + : "/product2.jpeg",
Removed / Before Commit
- if (password.length < 8) { setError("Password must be at least 8 characters."); return; } - if (!confirmPassword) { setError("Please confirm your password."); return; } - if (password !== confirmPassword) { setError("Passwords do not match."); return; } - if (phone && !/^[0-9]{10}$/.test(phone)) { setError("Enter a valid 10-digit phone number."); return; } - setLoading(true); - try { - const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/customer/register`, { - } - try { - - localStorage.setItem("avriti_customer", JSON.stringify({ name: data.customer.full_name, email: data.customer.email, token: data.token })); - } catch {} - window.location.href = "/"; - } catch { - - <div className="mb-3 fade-up-3"> - <label style={{ display: "block", fontSize: "13px", fontWeight: "500", color: "#000", marginBottom: "6px" }}> - Phone <span style={{ color: "#9E9890", fontWeight: "400" }}>(optional)</span>
Added / After Commit
+ if (password.length < 8) { setError("Password must be at least 8 characters."); return; } + if (!confirmPassword) { setError("Please confirm your password."); return; } + if (password !== confirmPassword) { setError("Passwords do not match."); return; } + if (!phone.trim()) { setError("Phone number is required."); return; } + if (!/^[0-9]{10}$/.test(phone)) { setError("Enter a valid 10-digit phone number."); return; } + setLoading(true); + try { + const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/customer/register`, { + } + try { + + localStorage.setItem("avriti_customer", JSON.stringify({ name: data.customer.full_name, email: data.customer.email, phone: data.customer.phone, token: data.token })); + } catch {} + window.location.href = "/"; + } catch { + + <div className="mb-3 fade-up-3"> + <label style={{ display: "block", fontSize: "13px", fontWeight: "500", color: "#000", marginBottom: "6px" }}>
Removed / Before Commit
- type FeaturedProduct = { - slug: string; - name: string; - attrValue: string; - variantName: string; - sellingPrice: number | null; - mrp: number | null; - .then((res) => res.json()) - .then((data) => { - if (data.success && Array.isArray(data.data)) { - const mapped: FeaturedProduct[] = data.data.map((p: any, i: number) => ({ - slug: p.slug, - name: p.title, - attrValue: p.attributes?.find((a: any) => a.sr_no === 1)?.value ?? "", - variantName: p.variant?.name ?? "", - sellingPrice: p.attributes?.find((a: any) => a.sr_no === 1)?.selling_price ?? null, - mrp: p.attributes?.find((a: any) => a.sr_no === 1)?.mrp ?? null, - image: p.thumbnail?.[0]
Added / After Commit
+ type FeaturedProduct = { + slug: string; + name: string; + productId?: number; + attrValue: string; + attributeName?: string; + variantName: string; + sellingPrice: number | null; + mrp: number | null; + .then((res) => res.json()) + .then((data) => { + if (data.success && Array.isArray(data.data)) { + const mapped: FeaturedProduct[] = data.data.map((p: any, i: number) => { + const attr = p.attributes?.find((a: any) => a.in_stock) ?? p.attributes?.find((a: any) => a.sr_no === 1); + + return { + slug: p.slug, + name: p.title,
Removed / Before Commit
- "use client"; - - import { useState, useEffect } from "react"; - import Image from "next/image"; - import Link from "next/link"; - import { PdfBrowserFrame } from "@/components/pdf-browser-frame"; - - interface Category { - id: number; - const [categories, setCategories] = useState<Category[]>([]); - const [products, setProducts] = useState<Product[]>([]); - const [loading, setLoading] = useState(false); - - useEffect(() => { - const fetchCategories = async () => { - fetchProducts(); - }, [selected]); -
Added / After Commit
+ "use client"; + + import { useState, useEffect } from "react"; + import type React from "react"; + import Image from "next/image"; + import Link from "next/link"; + import { PdfBrowserFrame } from "@/components/pdf-browser-frame"; + import { addToLocalCart, apiAddToCart, isLoggedIn } from "@/lib/cart"; + import { apiGetWishlist, apiToggleWishlist } from "@/lib/wishlist"; + import { shareProduct } from "@/lib/share"; + + interface Category { + id: number; + const [categories, setCategories] = useState<Category[]>([]); + const [products, setProducts] = useState<Product[]>([]); + const [loading, setLoading] = useState(false); + const [wishlistIds, setWishlistIds] = useState<number[]>([]); +
Removed / Before Commit
Added / After Commit
+ "use client"; + + import { useEffect, useState } from "react"; + import Link from "next/link"; + import InnerBanner from "@/components/inner-banner"; + import { addToLocalCart, apiAddToCart, isLoggedIn } from "@/lib/cart"; + import { apiGetWishlist, apiRemoveWishlist, WishlistItem } from "@/lib/wishlist"; + import { shareProduct } from "@/lib/share"; + + const formatAttributeLabel = (item: WishlistItem) => { + if (!item.attribute_value) return ""; + + const numericValue = Number(item.attribute_value); + const value = Number.isFinite(numericValue) + ? Number.isInteger(numericValue) + ? String(numericValue) + : String(numericValue).replace(/0+$/, "").replace(/\.$/, "") + : item.attribute_value;
Removed / Before Commit
- import Link from "next/link"; - import { Button } from "@/components/ui/button"; - import { Card } from "@/components/ui/card"; - - type ProductCardProps = { - slug: string; - category?: string; - badge?: string; - description?: string; - }; - - export function ProductCard({ product }: { product: ProductCardProps }) { - return ( - <Card className="overflow-hidden"> - <Link href={`/shop/${product.slug}`} className="block h-full flex flex-col justify-between"> - <div className="mt-2 mb-[20px] flex gap-2 items-center justify-between"> - <h3 className="text-lg text-[var(--textcolor)] font-normal"> - {product.name}
Added / After Commit
+ "use client"; + + import Link from "next/link"; + import type React from "react"; + import { useEffect, useState } from "react"; + import { Button } from "@/components/ui/button"; + import { Card } from "@/components/ui/card"; + import { addToLocalCart, apiAddToCart, isLoggedIn } from "@/lib/cart"; + import { apiGetWishlist, apiToggleWishlist } from "@/lib/wishlist"; + import { shareProduct } from "@/lib/share"; + + type ProductCardProps = { + slug: string; + category?: string; + badge?: string; + description?: string; + productId?: number; + attributeName?: string;
Removed / Before Commit
- <button - disabled={!selectedAttr?.in_stock} - onClick={() => { - if (!isLoggedIn()) { window.location.href = "/login"; return; } - if (!product || !selectedAttr) return; - const buyNowItem = { - product_id: product.id, - quantity, - }; - localStorage.setItem("avriti_buy_now", JSON.stringify(buyNowItem)); - window.location.href = "/checkout"; - }} className="bg-[var(--color-primary-700)] inline-flex items-center justify-center gap-2 px-5 py-2.5 text-sm font-semibold transition border-[var(--color-primary-300)] text-[#fff] hover:bg-[var(--color-primary-100)]/35 hover:text-[var(--color-primary-700)] min-h-11 uppercase pl-[10px] pr-[0px] rounded-[10px] border-[1px] w-full addtocartButton relative cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed"> - Buy Now - </button>
Added / After Commit
+ <button + disabled={!selectedAttr?.in_stock} + onClick={() => { + if (!product || !selectedAttr) return; + const buyNowItem = { + product_id: product.id, + quantity, + }; + localStorage.setItem("avriti_buy_now", JSON.stringify(buyNowItem)); + window.location.href = isLoggedIn() + ? "/checkout" + : "/login?redirect=/checkout"; + }} className="bg-[var(--color-primary-700)] inline-flex items-center justify-center gap-2 px-5 py-2.5 text-sm font-semibold transition border-[var(--color-primary-300)] text-[#fff] hover:bg-[var(--color-primary-100)]/35 hover:text-[var(--color-primary-700)] min-h-11 uppercase pl-[10px] pr-[0px] rounded-[10px] border-[1px] w-full addtocartButton relative cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed"> + Buy Now + </button>
Removed / Before Commit
- import { useState, useEffect, useRef } from "react"; - import { CartButton } from "@/components/ui/cart-button"; - import { localCartCount, apiGetCart, isLoggedIn } from "@/lib/cart"; - import { cn } from "@/lib/utils"; - import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; - import { faUser } from "@fortawesome/free-solid-svg-icons"; - const [user, setUser] = useState<{ name?: string; email?: string } | null>(null); - const [acctOpen, setAcctOpen] = useState(false); - const [cartCount, setCartCount] = useState(0); - const [toast, setToast] = useState(false); - const accountMenuRef = useRef<HTMLDivElement>(null); - - useEffect(() => { - } catch {} - }; - - useEffect(() => { - refreshCartCount();
Added / After Commit
+ import { useState, useEffect, useRef } from "react"; + import { CartButton } from "@/components/ui/cart-button"; + import { localCartCount, apiGetCart, isLoggedIn } from "@/lib/cart"; + import { apiGetWishlist } from "@/lib/wishlist"; + import { cn } from "@/lib/utils"; + import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + import { faUser } from "@fortawesome/free-solid-svg-icons"; + const [user, setUser] = useState<{ name?: string; email?: string } | null>(null); + const [acctOpen, setAcctOpen] = useState(false); + const [cartCount, setCartCount] = useState(0); + const [wishlistCount, setWishlistCount] = useState(0); + const [toast, setToast] = useState(false); + const [wishlistToast, setWishlistToast] = useState(false); + const [shareToast, setShareToast] = useState(false); + const accountMenuRef = useRef<HTMLDivElement>(null); + + useEffect(() => { + } catch {}
Removed / Before Commit
Added / After Commit
+ export async function shareProduct(slug: string, title: string): Promise<void> { + const url = `${window.location.origin}/shop/${slug}`; + + if (navigator.share) { + await navigator.share({ title, url }); + } else { + await navigator.clipboard.writeText(url); + } + + window.dispatchEvent(new Event("product-shared")); + }
Removed / Before Commit
Added / After Commit
+ import { getToken } from "@/lib/cart"; + + export type WishlistItem = { + id: number; + product_id: number; + slug: string; + title: string; + thumbnail: string | null; + attribute_name: string | null; + attribute_value: string | null; + variant_name?: string | null; + selling_price: number; + mrp: number; + category?: string | null; + }; + + const API = process.env.NEXT_PUBLIC_API_URL ?? ""; +