Showing AI reviews for avriti
Project AI Score ?
63%
Quality Avg ?
68%
Security Avg ?
45%
Reviews ?
56

Review Result ?

jattin01/avriti · be60e61b
This commit improves the frontend by adding support for combo products in the cart and checkout pages. It introduces additional state management and UI elements to handle combo products and coupons, and updates shipping and payment options dynamically. The code shows good UI/UX improvements and logical handling of combos and coupons. However, error handling in fetch requests could be improved with more detailed feedback and retry logic. Also, some variable naming could be clearer to improve maintainability, and security could be enhanced by handling token retrieval and API errors more robustly.
Quality ?
85%
Security ?
70%
Business Value ?
90%
Maintainability ?
85%
Issues & Suggested Fixes ?
1AI detects the issue
2Shows file, line, or evidence
3Suggests the fix to apply
Empty Catch Block Silently Ignores Errors
Where app/checkout/page.tsx:142
Issue / Evidence empty catch block silently ignores errors
Suggested Fix add error logging or user feedback to improve bug detection
Generic Catch Block Without Logging Error
Where app/checkout/page.tsx:444
Issue / Evidence generic catch block without logging error
Suggested Fix log error details or report to monitoring for better debugging
Cancellation Flag Pattern Used But May Lea...
Where app/checkout/page.tsx:413
Issue / Evidence cancellation flag pattern used but may lead to race conditions
Suggested Fix consider using AbortController to cancel fetch requests
Gettoken() Usage Lacks Error Handling If T...
Where app/checkout/page.tsx:424
Issue / Evidence getToken() usage lacks error handling if token is missing or invalid
Suggested Fix add validation and fallback for token retrieval
Missing Test Coverage
Where app/cart/page.tsx:126-128
Issue / Evidence isSameItem function could be clarified and tested for edge cases
Suggested Fix add unit tests and improve comments for maintainability
Insufficient Detail And Typos ('Sipping' I...
Where commit message
Issue / Evidence insufficient detail and typos ('sipping' instead of 'shipping' or 'shopping')
Suggested Fix improve the commit message to clearly describe changes for better traceability
Shipping Calculation Fetch Call Could Be I...
Where app/checkout/page.tsx:387-406
Issue / Evidence shipping calculation fetch call could be improved with retries or fallback UI
Suggested Fix add retry logic or better error states to enhance user experience
Code Change Preview · app/cart/page.tsx ?
Removed / Before Commit
- getLocalCart,
- updateLocalQty,
- removeFromLocalCart,
- apiGetCart,
- apiUpdateQty,
- apiRemoveItem,
- } from "@/lib/cart";
- 
- type CartItem = {
- id?: number;
-   product_id: number;
- slug: string;
- title: string;
- thumbnail: string;
- setItems(
- data.map((d: any) => ({
- id: d.id,
-           product_id: d.product_id,
Added / After Commit
+ getLocalCart,
+ updateLocalQty,
+ removeFromLocalCart,
+   updateLocalComboQty,
+   removeComboFromLocalCart,
+ apiGetCart,
+ apiUpdateQty,
+ apiRemoveItem,
+ } from "@/lib/cart";
+ 
+ type CartItem = {
+ id?: number;
+   product_id: number | null;
+   combo_product_id?: number | null;
+   is_combo?: boolean;
+ slug: string;
+ title: string;
+ thumbnail: string;
Removed / Before Commit
- }
- 
- type CheckoutItem = {
-   product_id: number;
- title: string;
- attribute_name: string;
- attribute_value: string;
- variant_name?: string;
- const [payment, setPayment] = useState("cod");
- const [placing, setPlacing] = useState(false);
- const [error, setError] = useState("");
- 
- // tax
- const [taxBreakup, setTaxBreakup] = useState<{ name: string; rate_percent: number; amount: number }[]>([]);
- const [taxAmount, setTaxAmount] = useState(0);
- 
- // ── load Razorpay SDK ───────────────────────────────────────
- useEffect(() => {
Added / After Commit
+ }
+ 
+ type CheckoutItem = {
+   product_id: number | null;
+   combo_product_id?: number | null;
+   is_combo?: boolean;
+ title: string;
+   thumbnail?: string;
+ attribute_name: string;
+ attribute_value: string;
+ variant_name?: string;
+ const [payment, setPayment] = useState("cod");
+ const [placing, setPlacing] = useState(false);
+ const [error, setError] = useState("");
+   const [codEnabled, setCodEnabled] = useState(true);
+ 
+ // tax
+ const [taxBreakup, setTaxBreakup] = useState<{ name: string; rate_percent: number; amount: number }[]>([]);
Removed / Before Commit

                                                
Added / After Commit
+ "use client";
+ import { useParams } from "next/navigation";
+ import Breadcrumb from "@/components/common/Breadcrumb";
+ import ComboDetailSection from "@/components/ComboDetailSection";
+ 
+ export default function ComboDetailPage() {
+   const params = useParams();
+   const slug = Array.isArray(params?.slug) ? params.slug[0] : (params?.slug ?? "");
+ 
+   return (
+     <div>
+       <Breadcrumb />
+       <ComboDetailSection slug={slug} />
+     </div>
+   );
+ }
Removed / Before Commit
- style={{ paddingRight: "44px" }}
- />
- <button
- className="toggle-btn"
- onClick={() => setShowPassword(v => !v)}
- style={{ position: "absolute", right: "14px", top: "50%", transform: "translateY(-50%)" }}
Added / After Commit
+ style={{ paddingRight: "44px" }}
+ />
+ <button
+                 type="button"
+ className="toggle-btn"
+ onClick={() => setShowPassword(v => !v)}
+ style={{ position: "absolute", right: "14px", top: "50%", transform: "translateY(-50%)" }}
Removed / Before Commit
- category: string;
- badge: string;
- description: string;
- };
- 
- type HomeData = {
- const [featuredProducts, setFeaturedProducts] = useState<FeaturedProduct[]>([]);
- 
- useEffect(() => {
-     fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/product?featured=true`)
-       .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,
Added / After Commit
+ category: string;
+ badge: string;
+ description: string;
+   isCombo?: boolean;
+   comboId?: number;
+ };
+ 
+ type HomeData = {
+ const [featuredProducts, setFeaturedProducts] = useState<FeaturedProduct[]>([]);
+ 
+ useEffect(() => {
+     Promise.all([
+       fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/product?featured=true`).then((res) => res.json()),
+       fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/combo-products?featured=true`).then((res) => res.json()),
+     ])
+       .then(([productsData, combosData]) => {
+         const mapped: FeaturedProduct[] = [];
+ 
Removed / Before Commit
- 
- import { useState } from "react";
- import Link from "next/link";
- import { mergeLocalCartToApi } from "@/lib/cart";
- 
- export default function RegisterPage() {
- const [email, setEmail] = useState("");
- const [showConfirmPassword, setShowConfirmPassword] = useState(false);
- const [loading, setLoading] = useState(false);
- const [error, setError] = useState("");
- const [fullName, setFullName] = useState("");
- const [phone, setPhone] = useState("");
- const handleSubmit = async () => {
- setError(msg);
- return;
- }
-       try {
-         localStorage.setItem("avriti_customer", JSON.stringify({ name: data.customer.full_name, email: data.customer.email, phone: data.customer.phone, token: data.token }));
Added / After Commit
+ 
+ import { useState } from "react";
+ import Link from "next/link";
+ 
+ export default function RegisterPage() {
+ const [email, setEmail] = useState("");
+ const [showConfirmPassword, setShowConfirmPassword] = useState(false);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState("");
+   const [success, setSuccess] = useState("");
+ const [fullName, setFullName] = useState("");
+ const [phone, setPhone] = useState("");
+ const handleSubmit = async () => {
+ setError(msg);
+ return;
+ }
+       setSuccess(data?.message || "Registration successful. Please check your email to verify your account.");
+ } catch {
Removed / Before Commit
- 
- const [password, setPassword] = useState("");
- const [confirmPassword, setConfirmPassword] = useState("");
- const [loading, setLoading] = useState(false);
- const [error, setError] = useState("");
- const [success, setSuccess] = useState("");
- <label style={{ display: "block", fontSize: "13px", fontWeight: "500", color: "#000", marginBottom: "6px" }}>
- New Password
- </label>
-             <input
-               type="password"
-               className="bg-[var(--color-primary-100)] input-field w-full h-[44px] py-2 px-3 rounded-[10px]"
-               placeholder="Min. 8 characters"
-               value={password}
-               onChange={e => setPassword(e.target.value)}
-             />
- </div>
- 
Added / After Commit
+ 
+ const [password, setPassword] = useState("");
+ const [confirmPassword, setConfirmPassword] = useState("");
+   const [showPassword, setShowPassword] = useState(false);
+   const [showConfirmPassword, setShowConfirmPassword] = useState(false);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState("");
+ const [success, setSuccess] = useState("");
+ <label style={{ display: "block", fontSize: "13px", fontWeight: "500", color: "#000", marginBottom: "6px" }}>
+ New Password
+ </label>
+             <div style={{ position: "relative" }}>
+               <input
+                 type={showPassword ? "text" : "password"}
+                 className="bg-[var(--color-primary-100)] input-field w-full h-[44px] py-2 px-3 rounded-[10px]"
+                 placeholder="Min. 8 characters"
+                 value={password}
+                 onChange={e => setPassword(e.target.value)}
Removed / Before Commit
- }[];
- }
- 
- export default function ShopPage() {
-   const [selected, setSelected] = useState<number | null>(null);
- const [categories, setCategories] = useState<Category[]>([]);
- const [products, setProducts] = useState<Product[]>([]);
- const [loading, setLoading] = useState(false);
- const [wishlistIds, setWishlistIds] = useState<number[]>([]);
- 
- useEffect(() => {
- const fetchCategories = async () => {
- }, []);
- 
- useEffect(() => {
- const fetchProducts = async () => {
- setLoading(true);
- const url = selected
Added / After Commit
+ }[];
+ }
+ 
+ interface ComboProductData {
+   id: number;
+   title: string;
+   slug: string;
+   thumbnail: string[];
+   combo_price: number;
+   available_stock: number;
+ }
+ 
+ export default function ShopPage() {
+   const [selected, setSelected] = useState<number | "combo" | null>(null);
+ const [categories, setCategories] = useState<Category[]>([]);
+ const [products, setProducts] = useState<Product[]>([]);
+   const [comboProducts, setComboProducts] = useState<ComboProductData[]>([]);
+   const [hasCombos, setHasCombos] = useState(false);
Removed / Before Commit

                                                
Added / After Commit
+ "use client";
+ 
+ import { useEffect, useState, Suspense } from "react";
+ import { useSearchParams } from "next/navigation";
+ import Link from "next/link";
+ 
+ function VerifyEmailContent() {
+   const searchParams = useSearchParams();
+   const email = searchParams.get("email") || "";
+   const token = searchParams.get("token") || "";
+ 
+   const [status, setStatus] = useState<"loading" | "success" | "error">("loading");
+   const [message, setMessage] = useState("");
+ 
+   useEffect(() => {
+     if (!email || !token) {
+       setStatus("error");
+       setMessage("Invalid or incomplete verification link.");
Removed / Before Commit
- 
- const handleAddToCart = async (item: WishlistItem) => {
- const payload = {
-       product_id: item.product_id,
- attribute_name: item.attribute_name ?? "",
- attribute_value: item.attribute_value ?? "",
- quantity: 1,
Added / After Commit
+ 
+ const handleAddToCart = async (item: WishlistItem) => {
+ const payload = {
+       ...(item.combo_product_id ? { combo_product_id: item.combo_product_id } : { product_id: item.product_id ?? undefined }),
+ attribute_name: item.attribute_name ?? "",
+ attribute_value: item.attribute_value ?? "",
+ quantity: 1,
Removed / Before Commit

                                                
Added / After Commit
+ "use client";
+ 
+ import { useEffect, useState } from "react";
+ import Link from "next/link";
+ import { isLoggedIn, addToLocalCart, apiAddToCart } from "@/lib/cart";
+ import { shareProduct } from "@/lib/share";
+ 
+ type ComboItem = {
+   product_id: number;
+   title: string;
+   slug: string;
+   thumbnail: string[];
+   attribute_value: string | null;
+   variant_name?: string | null;
+   quantity: number;
+ };
+ 
+ type ComboProduct = {
Removed / Before Commit
- <label className="block"><span className={labelCls}>Landmark</span><input type="text" value={modalForm.landmark} onChange={e => setModalForm(f => ({ ...f, landmark: e.target.value }))} placeholder="Near metro station" className={inputCls} /></label>
- <label className="block"><span className={labelCls}>City *</span><input type="text" value={modalForm.city} onChange={e => setModalForm(f => ({ ...f, city: e.target.value }))} placeholder="New Delhi" className={inputCls} /></label>
- <label className="block"><span className={labelCls}>State *</span><input type="text" value={modalForm.state} onChange={e => setModalForm(f => ({ ...f, state: e.target.value }))} placeholder="Delhi" className={inputCls} /></label>
-               <label className="block"><span className={labelCls}>Pincode *</span><input type="text" value={modalForm.pincode} onChange={e => setModalForm(f => ({ ...f, pincode: e.target.value }))} placeholder="110001" className={inputCls} /></label>
- <label className="block"><span className={labelCls}>Country *</span><input type="text" value={modalForm.country} onChange={e => setModalForm(f => ({ ...f, country: e.target.value }))} placeholder="India" className={inputCls} /></label>
- <label className="block">
- <span className={labelCls}>Address Type</span>
Added / After Commit
+ <label className="block"><span className={labelCls}>Landmark</span><input type="text" value={modalForm.landmark} onChange={e => setModalForm(f => ({ ...f, landmark: e.target.value }))} placeholder="Near metro station" className={inputCls} /></label>
+ <label className="block"><span className={labelCls}>City *</span><input type="text" value={modalForm.city} onChange={e => setModalForm(f => ({ ...f, city: e.target.value }))} placeholder="New Delhi" className={inputCls} /></label>
+ <label className="block"><span className={labelCls}>State *</span><input type="text" value={modalForm.state} onChange={e => setModalForm(f => ({ ...f, state: e.target.value }))} placeholder="Delhi" className={inputCls} /></label>
+               <label className="block"><span className={labelCls}>Pincode *</span><input type="text" inputMode="numeric" maxLength={6} value={modalForm.pincode} onChange={e => setModalForm(f => ({ ...f, pincode: e.target.value.replace(/\D/g, "").slice(0, 6) }))} placeholder="110001" className={inputCls} /></label>
+ <label className="block"><span className={labelCls}>Country *</span><input type="text" value={modalForm.country} onChange={e => setModalForm(f => ({ ...f, country: e.target.value }))} placeholder="India" className={inputCls} /></label>
+ <label className="block">
+ <span className={labelCls}>Address Type</span>
Removed / Before Commit
- </div>
- ),
- },
-     {
-       title: "Care Instructions",
-       content: (
-         <div style={{ display: "flex", flexDirection: "column", gap: "20px" }}>
-           <div>
-             <p style={{ fontWeight: "600", color: "#3a2a1e", marginBottom: "6px", fontSize: "13px", textTransform: "uppercase", letterSpacing: "0.05em" }}>Cleansing</p>
-             <p style={{ marginBottom: "10px" }}>Your bracelet is cleansed and packed with care before dispatch. Crystals are believed to absorb energies from their surroundings, and many people choose to cleanse them periodically.</p>
-             <p style={{ marginBottom: "8px" }}>You may cleanse your crystals using:</p>
-             <div style={{ display: "flex", flexDirection: "column", gap: "6px" }}>
-               {["Moonlight (preferably Full Moon)", "Incense or smoke cleansing", "Sound cleansing using bells or singing bowls", "Selenite plates or Clear Quartz"].map((t, i) => (
-                 <div key={i} style={{ display: "flex", gap: "10px" }}><span style={{ color: "#c9a87c", marginTop: "2px" }}>✦</span><span>{t}</span></div>
-               ))}
-             </div>
-             <p style={{ marginTop: "10px", fontStyle: "italic", color: "#9e8a7a" }}>Note: Some crystals are sensitive to water and salt. If unsure, choose moonlight or smoke cleansing.</p>
-           </div>
Added / After Commit
+ </div>
+ ),
+ },
+ 
+ 
+     // {
+     //   title: "Care Instructions",
+     //   content: (
+     //     <div style={{ display: "flex", flexDirection: "column", gap: "20px" }}>
+     //       <div>
+     //         <p style={{ fontWeight: "600", color: "#3a2a1e", marginBottom: "6px", fontSize: "13px", textTransform: "uppercase", letterSpacing: "0.05em" }}>Cleansing</p>
+     //         <p style={{ marginBottom: "10px" }}>Your bracelet is cleansed and packed with care before dispatch. Crystals are believed to absorb energies from their surroundings, and many people choose to cleanse them periodically.</p>
+     //         <p style={{ marginBottom: "8px" }}>You may cleanse your crystals using:</p>
+     //         <div style={{ display: "flex", flexDirection: "column", gap: "6px" }}>
+     //           {["Moonlight (preferably Full Moon)", "Incense or smoke cleansing", "Sound cleansing using bells or singing bowls", "Selenite plates or Clear Quartz"].map((t, i) => (
+     //             <div key={i} style={{ display: "flex", gap: "10px" }}><span style={{ color: "#c9a87c", marginTop: "2px" }}>✦</span><span>{t}</span></div>
+     //           ))}
+     //         </div>
Removed / Before Commit
- productId?: number;
- attributeName?: string;
- attributeValue?: string;
- };
- 
- export function ProductCard({ product }: { product: ProductCardProps }) {
- const [wishlisted, setWishlisted] = useState(false);
- 
- useEffect(() => {
-     if (!isLoggedIn() || !product.productId) return;
- 
- apiGetWishlist()
- .then((items) => {
-         setWishlisted(items.some((item) => item.product_id === product.productId));
- })
- .catch(() => {});
-   }, [product.productId]);
- 
Added / After Commit
+ productId?: number;
+ attributeName?: string;
+ attributeValue?: string;
+   isCombo?: boolean;
+   comboId?: number;
+ };
+ 
+ export function ProductCard({ product }: { product: ProductCardProps }) {
+ const [wishlisted, setWishlisted] = useState(false);
+ 
+ useEffect(() => {
+     if (!isLoggedIn()) return;
+     if (!product.isCombo && !product.productId) return;
+     if (product.isCombo && !product.comboId) return;
+ 
+ apiGetWishlist()
+ .then((items) => {
+         setWishlisted(
Removed / Before Commit
- category: string;
- badge: string;
- description: string;
- };
- 
- interface ProductSliderProps {
Added / After Commit
+ category: string;
+ badge: string;
+ description: string;
+   isCombo?: boolean;
+   comboId?: number;
+ };
+ 
+ interface ProductSliderProps {
Removed / Before Commit
- import Link from "next/link";
- import { Camera, PlaySquare } from "lucide-react";
- import { Button } from "@/components/ui/button";
- import { Input } from "@/components/ui/input";
- import Image from "next/image";
- 
- export function SiteFooter() {
- return (
- <footer className="asddsa border-t border-[var(--color-border)] bg-[#f8fbff]">
- <div className="mx-auto grid max-w-7xl gap-10 px-4 py-12 sm:px-6 lg:grid-cols-[1.2fr_0.8fr_1fr] lg:px-8">
- <p className="mt-3 text-sm leading-6 text-[var(--color-text-secondary)]">
- Monthly notes on rituals, guidance windows, and new Avriti drops.
- </p>
-           <form className="mt-4 flex flex-col gap-3 flex-row border border-[#0B3DBA] rounded-[10px]">
-             <Input type="email" placeholder="Email address" aria-label="Email" className="border-none bg-transparent focus:outline-none focus:ring-transparent" />
-             <Button type="button" className="bg-transparent shadow-none hover:bg-transparent"><img
- src="/right-arrow.png"
- alt="Avriti"
Added / After Commit
+ "use client";
+ 
+ import { useState } from "react";
+ import Link from "next/link";
+ import { Camera, PlaySquare } from "lucide-react";
+ import { Button } from "@/components/ui/button";
+ import { Input } from "@/components/ui/input";
+ import Image from "next/image";
+ 
+ export function SiteFooter() {
+   const [subscribeEmail, setSubscribeEmail] = useState("");
+   const [subscribeStatus, setSubscribeStatus] = useState<"idle" | "loading" | "success" | "error">("idle");
+   const [subscribeMessage, setSubscribeMessage] = useState("");
+ 
+   const handleSubscribe = async (e: React.FormEvent) => {
+     e.preventDefault();
+     if (!subscribeEmail.trim()) return;
+ 
Removed / Before Commit
- export type LocalCartItem = {
-   product_id: number;
- slug: string;
- title: string;
- thumbnail: string;
-   attribute_name: string;
-   attribute_value: string;
- variant_name?: string;
- selling_price: number;
- mrp: number;
- quantity: number;
- };
- 
- const CART_KEY = "avriti_cart";
- localStorage.setItem(CART_KEY, JSON.stringify(items));
- }
- 
- export function addToLocalCart(item: LocalCartItem): void {
Added / After Commit
+ export type LocalCartItem = {
+   product_id?: number | null;
+   combo_product_id?: number | null;
+ slug: string;
+ title: string;
+ thumbnail: string;
+   attribute_name?: string;
+   attribute_value?: string;
+ variant_name?: string;
+ selling_price: number;
+ mrp: number;
+ quantity: number;
+   is_combo?: boolean;
+ };
+ 
+ const CART_KEY = "avriti_cart";
+ localStorage.setItem(CART_KEY, JSON.stringify(items));
+ }
Removed / Before 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 });
Added / After Commit
+ export async function shareProduct(slug: string, title: string, basePath: string = "/shop"): Promise<void> {
+   const url = `${window.location.origin}${basePath}/${slug}`;
+ 
+ if (navigator.share) {
+ await navigator.share({ title, url });
Removed / Before Commit
- 
- export type WishlistItem = {
- id: number;
-   product_id: number;
- slug: string;
- title: string;
- thumbnail: string | null;
- selling_price: number;
- mrp: number;
- category?: string | null;
- };
- 
- const API = process.env.NEXT_PUBLIC_API_URL;
- }
- 
- export async function apiToggleWishlist(item: {
-   product_id: number;
- attribute_name?: string | null;
Added / After Commit
+ 
+ export type WishlistItem = {
+ id: number;
+   product_id: number | null;
+   combo_product_id?: number | null;
+ slug: string;
+ title: string;
+ thumbnail: string | null;
+ selling_price: number;
+ mrp: number;
+ category?: string | null;
+   is_combo?: boolean;
+ };
+ 
+ const API = process.env.NEXT_PUBLIC_API_URL;
+ }
+ 
+ export async function apiToggleWishlist(item: {