Showing AI reviews for avriti-backend
Project AI Score ?
59%
Quality Avg ?
64%
Security Avg ?
45%
Reviews ?
57

Review Result ?

jattin01/avriti-backend · fa7bdd83
The commit introduces product import improvements with caching of existing slugs and SKUs for performance and uniqueness checks, and implements validation and logic for categories, product types, variants, and attributes. It adds detailed SKU uniqueness checking for tiers in the AdminServiceDetailController and appropriately handles image uploads and slug generation. The code includes structured validation with meaningful error messages and no obvious security flaws, but some improvements could enhance robustness and coverage.
Quality ?
85%
Security ?
70%
Business Value ?
80%
Maintainability ?
83%
Issues & Suggested Fixes ?
1AI detects the issue
2Shows file, line, or evidence
3Suggests the fix to apply
Use Of Product::pluck('Slug') >Flip() To C...
Where app/Http/Controllers/AdminProductController.php:170
Issue / Evidence Use of Product::pluck('slug')->flip() to check existing slugs is creative but flip() returns a collection; consider using a Laravel collection method like keyBy() or a native PHP array for clarity and performance.
Suggested Fix Review and simplify this section.
Missing Validation
Where app/Http/Controllers/AdminProductController.php:171-179
Issue / Evidence The extraction of SKUs from attributes uses a nested foreach and type casts; validate that attributes are well-formed arrays to avoid potential exceptions.
Suggested Fix Review and simplify this section.
The Sku Uniqueness Check Does Not Normaliz...
Where app/Http/Controllers/AdminProductController.php:317-324
Issue / Evidence The SKU uniqueness check does not normalize or trim SKUs before strtolower; explicitly trim SKUs to avoid false negatives due to whitespace.
Suggested Fix Review and simplify this section.
Missing Validation
Where app/Http/Controllers/AdminProductController.php:341-345
Issue / Evidence When a duplicate SKU is found, the process continues but logging more diagnostic info or returning structured validation errors to the user might help debugging and UX.
Suggested Fix Review and simplify this section.
Image Upload Max Size Is 2Mb (2048Kb); Con...
Where app/Http/Controllers/AdminServiceDetailController.php:98
Issue / Evidence Image upload max size is 2MB (2048KB); consider documenting or enforcing upload restrictions elsewhere to avoid silent failures.
Suggested Fix Review and simplify this section.
Missing Validation
Where app/Http/Controllers/AdminServiceDetailController.php:100-116
Issue / Evidence The tier SKU uniqueness validation is good, but adding normalization such as trimming whitespace and case-insensitivity is beneficial.
Suggested Fix Review and simplify this section.
In Uniqueslug(), The Where Clause Has A Co...
Where app/Http/Controllers/AdminServiceDetailController.php:145-151
Issue / Evidence In uniqueSlug(), the where clause has a condition with ->when($ignoreId, fn ($q) => $q->where('id', '!=', $ignoreId)) but the expression looks incorrect. Typically, the condition should be negated when ignoreId is provided to exclude that id. Verify this logic to prevent slug collisions on update.
Suggested Fix Review and simplify this section.
Issue
Where commit message
Issue / Evidence issue
Suggested Fix change to a more descriptive summary including key features or goals of this merge to improve business value and quality perception.
Code Change Preview · .gitignore ?
Removed / Before Commit
- /Product Excel test only.xlsx
- /Avriti_Ratna_Updated.xlsx
- /Service Detail test.xlsx
- \ No newline at end of file
Added / After Commit
+ /Product Excel test only.xlsx
+ /Avriti_Ratna_Updated.xlsx
+ /Service Detail test.xlsx
+ /BACKEND_AI_PROMPT.md
+ /Avriti_Services_sample_1.xlsx
+ /Product_Sample_1.xlsx
+ /Avriti_Services_sample.xlsx
+ \ No newline at end of file
Removed / Before Commit
- /** Download the sample Excel template for product import. */
- public function sampleExcel(): \Symfony\Component\HttpFoundation\BinaryFileResponse
- {
-         $path = storage_path('app/samples/product-sample.xlsx');
- abort_unless(file_exists($path), 404);
- 
-         return response()->download($path, 'Product Sample.xlsx');
- }
- 
- public function import(Request $request): \Illuminate\Http\JsonResponse
- $types      = ProductType::pluck('id', 'name');
- $variants   = Variant::pluck('id', 'name');
- 
- foreach ($rows as $row) {
- $rowNum = (int) $row['r'];
- if ($rowNum === 1) continue; // skip header
- continue;
- }
Added / After Commit
+ /** Download the sample Excel template for product import. */
+ public function sampleExcel(): \Symfony\Component\HttpFoundation\BinaryFileResponse
+ {
+         $path = storage_path('app/samples/Product_Sample_1.xlsx');
+ abort_unless(file_exists($path), 404);
+ 
+         // no-cache headers so a browser/proxy never serves an old cached copy.
+         return response()->download($path, 'Product_Sample_1.xlsx', [
+             'Cache-Control' => 'no-store, no-cache, must-revalidate, max-age=0',
+             'Pragma'        => 'no-cache',
+             'Expires'       => '0',
+         ]);
+ }
+ 
+ public function import(Request $request): \Illuminate\Http\JsonResponse
+ $types      = ProductType::pluck('id', 'name');
+ $variants   = Variant::pluck('id', 'name');
+ 
Removed / Before Commit
- 
- public function store(Request $request): RedirectResponse
- {
-         $request->validate([
-             'name'              => ['required', 'string', 'max:255'],
-             'short_description' => ['nullable', 'string'],
-             'consultation_fee'  => ['nullable', 'string', 'max:100'],
-             'icon'              => ['nullable', 'string'],
-             'image'             => ['nullable', 'image', 'max:2048'],
-             'is_active'         => ['nullable', 'in:0,1'],
-         ]);
- 
-         $slug = Str::slug($request->name);
-         $originalSlug = $slug;
-         $count = 1;
-         while (ServiceDetail::where('slug', $slug)->exists()) {
-             $slug = $originalSlug . '-' . $count++;
-         }
Added / After Commit
+ 
+ public function store(Request $request): RedirectResponse
+ {
+         $this->validateServiceDetail($request);
+ 
+         $slug = $this->uniqueSlug($request->input('slug') ?: $request->name);
+ 
+ $imagePath = null;
+ if ($request->hasFile('image')) {
+             $imagePath = $this->storeImage($request->file('image'));
+ }
+ 
+ ServiceDetail::create([
+ 'name'              => $request->name,
+ 'slug'              => $slug,
+ 'short_description' => $request->short_description,
+             'call_type'         => $request->call_type,
+             'tier_fields'       => $this->parseTierFields($request),
Removed / Before Commit
- }
- return [
- ...$item,
-                     'title'         => $sd?->name ?? ($item['title'] ?? 'Service'),
- 'thumbnail'     => $image,
- 'product_image' => $image,
- 'is_service'    => true,
Added / After Commit
+ }
+ return [
+ ...$item,
+                     // Prefer the checkout title — it carries the tier name
+                     // (e.g. "Avriti Vastu — Up to 4 BHK"); fall back to the
+                     // plain service name only if that's missing.
+                     'title'         => ! empty($item['title']) ? $item['title'] : ($sd?->name ?? 'Service'),
+ 'thumbnail'     => $image,
+ 'product_image' => $image,
+ 'is_service'    => true,
Removed / Before Commit
- use App\Models\Coupon;
- use App\Models\Order;
- use App\Models\Product;
- use App\Services\CouponService;
- use App\Services\ShippingService;
- use App\Services\TaxService;
- $appliedCouponCode = $couponResult['coupon']->code;
- }
- 
- $taxResult      = TaxService::calculate($subtotal - $discount);
-         $shippingResult = ShippingService::calculate($subtotal - $discount, $shippingAddress['pincode'] ?? null);
-         $shippingCharge = $shippingResult['shipping_charge'];
- 
- $order = Order::create([
- 'order_number'          => $orderNumber,
- }
- 
- // 3. Order items create
Added / After Commit
+ use App\Models\Coupon;
+ use App\Models\Order;
+ use App\Models\Product;
+ use App\Models\ServiceDetail;
+ use App\Services\CouponService;
+ use App\Services\ShippingService;
+ use App\Services\TaxService;
+ $appliedCouponCode = $couponResult['coupon']->code;
+ }
+ 
+         $requestItems   = $request->input('items', []);
+         $allService     = count($requestItems) > 0 && collect($requestItems)->every(fn($i) => !empty($i['service_detail_id']));
+ $taxResult      = TaxService::calculate($subtotal - $discount);
+         $shippingCharge = $allService ? 0 : ShippingService::calculate($subtotal - $discount, $shippingAddress['pincode'] ?? null)['shipping_charge'];
+ 
+ $order = Order::create([
+ 'order_number'          => $orderNumber,
+ }
Removed / Before Commit
- {
- $details = ServiceDetail::where('is_active', true)
- ->orderBy('id')
-             ->get(['id', 'name', 'slug', 'short_description', 'icon', 'image', 'consultation_fee']);
- 
- $data = $details->map(function ($sd) {
- return [
- 'id'                => $sd->id,
- 'name'              => $sd->name,
- 'slug'              => $sd->slug,
- 'short_description' => $sd->short_description ?? '',
- 'icon'              => !empty($sd->icon) ? asset('services-svg/' . $sd->icon) : null,
- 'image'             => !empty($sd->image) ? asset($sd->image) : null,
-                 'consultation_fee'  => $sd->consultation_fee ?? '',
- ];
- })->values()->toArray();
- 
- 'name'              => $sd->name,
Added / After Commit
+ {
+ $details = ServiceDetail::where('is_active', true)
+ ->orderBy('id')
+             ->get(['id', 'name', 'slug', 'short_description', 'icon', 'image', 'call_type', 'tier_fields']);
+ 
+ $data = $details->map(function ($sd) {
+             $tiers = $this->formatTiers($sd->tier_fields);
+ return [
+ 'id'                => $sd->id,
+ 'name'              => $sd->name,
+ 'slug'              => $sd->slug,
+ 'short_description' => $sd->short_description ?? '',
+ 'icon'              => !empty($sd->icon) ? asset('services-svg/' . $sd->icon) : null,
+ 'image'             => !empty($sd->image) ? asset($sd->image) : null,
+                 'call_type'         => $sd->call_type ?? '',
+                 'tiers'             => $tiers,
+                 // backward compat: first tier's fee
+                 'consultation_fee'  => $tiers[0]['consultation_fee'] ?? '',
Removed / Before Commit
- 'name',
- 'slug',
- 'short_description',
-         'consultation_fee',
- 'icon',
- 'image',
- 'other_details',
- {
- return [
- 'other_details' => 'array',
- 'is_active'     => 'boolean',
- ];
- }
Added / After Commit
+ 'name',
+ 'slug',
+ 'short_description',
+         'call_type',
+         'tier_fields',
+ 'icon',
+ 'image',
+ 'other_details',
+ {
+ return [
+ 'other_details' => 'array',
+             'tier_fields'   => 'array',
+ 'is_active'     => 'boolean',
+ ];
+ }
Removed / Before Commit

                                                
Added / After Commit
+ <?php
+ 
+ use Illuminate\Database\Migrations\Migration;
+ use Illuminate\Database\Schema\Blueprint;
+ use Illuminate\Support\Facades\Schema;
+ 
+ return new class extends Migration
+ {
+     public function up(): void
+     {
+         Schema::table('service_details', function (Blueprint $table) {
+             $table->string('call_type')->nullable()->after('image');
+             $table->json('tier_fields')->nullable()->after('call_type');
+         });
+ 
+         Schema::table('service_details', function (Blueprint $table) {
+             $table->dropColumn('consultation_fee');
+         });
Removed / Before Commit
- </tr>
- </thead>
- <tbody class="divide-y divide-gray-100 text-sm [&>tr:nth-child(even)]:bg-gray-100">
-         @forelse($banners as $banner)
- <tr class="hover:bg-blue-100 transition-colors">
- <td class="px-6 py-5 font-semibold text-gray-500">{{ $loop->iteration }}</td>
- <td class="px-6 py-5">
- </div>
- </td>
- </tr>
-         @empty
-         <tr>
-           <td colspan="5" class="px-6 py-12 text-center text-sm text-gray-400">No special offers added yet.</td>
-         </tr>
-         @endforelse
- </tbody>
- </table>
- </div>
Added / After Commit
+ </tr>
+ </thead>
+ <tbody class="divide-y divide-gray-100 text-sm [&>tr:nth-child(even)]:bg-gray-100">
+         @foreach($banners as $banner)
+ <tr class="hover:bg-blue-100 transition-colors">
+ <td class="px-6 py-5 font-semibold text-gray-500">{{ $loop->iteration }}</td>
+ <td class="px-6 py-5">
+ </div>
+ </td>
+ </tr>
+         @endforeach
+ </tbody>
+ </table>
+ </div>
Removed / Before Commit
- {{-- Items --}}
- @php
- $hasOnlyComboItems = $order->items->isNotEmpty() && $order->items->every(fn ($i) => $i->combo_product_id);
-       $itemsColumnCount = $hasOnlyComboItems ? 5 : 6;
- @endphp
- <div class="rounded-2xl border border-gray-200 overflow-hidden">
- <h3 class="px-4 py-3 text-sm font-semibold text-[#0b3dba] border-b border-gray-200">Items</h3>
- <thead class="bg-gray-50 text-xs uppercase tracking-[0.1em] text-gray-500">
- <tr>
- <th class="px-4 py-3">Product</th>
-               @unless ($hasOnlyComboItems)
- <th class="px-4 py-3">Variant</th>
-               @endunless
- <th class="px-4 py-3">SKU</th>
- <th class="px-4 py-3">Price</th>
- <th class="px-4 py-3">Qty</th>
- <span class="ml-2 inline-flex rounded-full bg-[#eef4ff] px-2 py-0.5 text-[10px] font-semibold text-[#0b3dba]">Combo</span>
- @endif
Added / After Commit
+ {{-- Items --}}
+ @php
+ $hasOnlyComboItems = $order->items->isNotEmpty() && $order->items->every(fn ($i) => $i->combo_product_id);
+       // Show the Variant column only if at least one item actually has a variant.
+       // Service-only orders (and combos) have none → hide the whole column.
+       $hasAnyVariant = $order->items->contains(
+           fn ($i) => ! $i->combo_product_id && (filled($i->attribute_value) || filled($i->variant_name))
+       );
+       $showVariantColumn = ! $hasOnlyComboItems && $hasAnyVariant;
+       $itemsColumnCount = $showVariantColumn ? 6 : 5;
+ @endphp
+ <div class="rounded-2xl border border-gray-200 overflow-hidden">
+ <h3 class="px-4 py-3 text-sm font-semibold text-[#0b3dba] border-b border-gray-200">Items</h3>
+ <thead class="bg-gray-50 text-xs uppercase tracking-[0.1em] text-gray-500">
+ <tr>
+ <th class="px-4 py-3">Product</th>
+               @if ($showVariantColumn)
+ <th class="px-4 py-3">Variant</th>
Removed / Before Commit
- form.append('_token', '{{ csrf_token() }}');
- if (dryRun) form.append('dry_run', '1');
- 
-       fetch('{{ route("products.import") }}', { method: 'POST', body: form })
-         .then(res => res.json())
-         .then(data => {
- renderResult(data);
- 
- if (dryRun) {
- doImport.classList.add('flex');
- doImport.disabled = false;
- doImport.innerHTML = '<i class="fa-solid fa-upload"></i> Import Now';
- }
- } else if (data.success && data.imported > 0) {
- setTimeout(() => { window.location.reload(); }, 2000);
- }
- })
- .catch(() => {
Added / After Commit
+ form.append('_token', '{{ csrf_token() }}');
+ if (dryRun) form.append('dry_run', '1');
+ 
+       fetch('{{ route("products.import") }}', {
+         method: 'POST',
+         body: form,
+         headers: { 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest' },
+       })
+         .then(async res => {
+           // Read as text first — the server may return an HTML error page
+           // (413/419/500/504) instead of JSON, which would crash res.json().
+           const raw = await res.text();
+           let data;
+           try {
+             data = JSON.parse(raw);
+           } catch (e) {
+             const hint = res.status === 413 ? 'File too large — increase upload limit (post_max_size / client_max_body_size).'
+               : res.status === 419 ? 'Session expired — refresh the page and try again.'
Removed / Before Commit
- </div>
- @endif
- 
- @if ($errors->any())
- <div class="mb-6 rounded-2xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">
-     <ul class="list-disc pl-4 space-y-1">
-       @foreach ($errors->all() as $error)<li>{{ $error }}</li>@endforeach
-     </ul>
- </div>
- @endif
- 
- <form method="POST" action="{{ $formAction }}" enctype="multipart/form-data">
- @csrf
- @if($isEdit) @method('PUT') @endif
- 
- <label class="mb-1.5 block text-sm font-medium text-gray-700">Name <span class="text-red-500">*</span></label>
- <input type="text" name="name" id="field-name"
- value="{{ old('name', $serviceDetail->name ?? '') }}"
Added / After Commit
+ </div>
+ @endif
+ 
+ {{-- SKU-uniqueness (cross-field) errors still surface here; per-field errors show inline --}}
+ @if ($errors->has('tiers'))
+ <div class="mb-6 rounded-2xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">
+     {{ $errors->first('tiers') }}
+ </div>
+ @endif
+ 
+ <form id="service-detail-form" method="POST" action="{{ $formAction }}" enctype="multipart/form-data">
+ @csrf
+ @if($isEdit) @method('PUT') @endif
+ 
+ <label class="mb-1.5 block text-sm font-medium text-gray-700">Name <span class="text-red-500">*</span></label>
+ <input type="text" name="name" id="field-name"
+ value="{{ old('name', $serviceDetail->name ?? '') }}"
+           data-req="Name is required"
Removed / Before Commit
- <th class="px-6 py-4">No</th>
- <th class="px-6 py-4">Name</th>
- <th class="px-6 py-4">Slug</th>
-           <th class="px-6 py-4">Fee</th>
- <th class="px-6 py-4">Status</th>
- <th class="px-6 py-4">Action</th>
- </tr>
- </div>
- </td>
- <td class="px-6 py-5 text-gray-500">{{ $sd->slug }}</td>
-             <td class="px-6 py-5 text-gray-700 font-medium">{{ $sd->consultation_fee ?: '—' }}</td>
- <td class="px-6 py-5">
- @if($sd->is_active)
- <span class="inline-flex rounded-full bg-emerald-100 px-3 py-1 text-xs font-semibold text-emerald-700">Active</span>
- form.append('_token', '{{ csrf_token() }}');
- if (dryRun) form.append('dry_run', '1');
- 
-       fetch('{{ route("service-details.import") }}', { method: 'POST', body: form })
Added / After Commit
+ <th class="px-6 py-4">No</th>
+ <th class="px-6 py-4">Name</th>
+ <th class="px-6 py-4">Slug</th>
+           <th class="px-6 py-4">Call Type</th>
+ <th class="px-6 py-4">Status</th>
+ <th class="px-6 py-4">Action</th>
+ </tr>
+ </div>
+ </td>
+ <td class="px-6 py-5 text-gray-500">{{ $sd->slug }}</td>
+             <td class="px-6 py-5">
+               @if($sd->call_type)
+                 <span class="inline-flex items-center gap-1.5 rounded-full bg-[#eef4ff] px-3 py-1 text-xs font-semibold text-[#1a41b1]">
+                   <i class="fa-solid {{ $sd->call_type === 'Zoom' ? 'fa-video' : 'fa-phone' }} text-[10px]"></i>
+                   {{ $sd->call_type }}
+                 </span>
+               @else
+                 <span class="text-gray-400">—</span>
Removed / Before Commit
- $billing = $orderData['billing_address'] ?? [];
- $shipping_addr = $orderData['shipping_address'] ?? [];
- $itemsCount = count($items);
- $subtotal = (float) ($orderData['subtotal'] ?? 0);
- $discount = (float) ($orderData['discount'] ?? 0);
- $tax = (float) ($orderData['tax'] ?? 0);
- <td style="font-size:13px; color:#555; padding:4px 0;">Tax</td>
- <td align="right" style="font-size:13px; color:#1a1a1a; padding:4px 0;">₹{{ number_format($tax, 2) }}</td>
- </tr>
- <tr>
- <td style="font-size:13px; color:#555; padding:4px 0;">Shipping</td>
- <td align="right" style="font-size:13px; padding:4px 0; @if($shippingCost == 0) color:#22c55e; font-weight:600; @else color:#1a1a1a; @endif">
- @if($shippingCost == 0) Free @else ₹{{ number_format($shippingCost, 2) }} @endif
- </td>
- </tr>
- </table>
- <hr style="border:none; border-top:1px dashed #d0d9ff; margin:12px 0;">
- <table width="100%" cellpadding="0" cellspacing="0" border="0"
Added / After Commit
+ $billing = $orderData['billing_address'] ?? [];
+ $shipping_addr = $orderData['shipping_address'] ?? [];
+ $itemsCount = count($items);
+   $isAllService = $itemsCount > 0 && collect($items)->every(fn ($i) => ! empty($i['is_service']));
+ $subtotal = (float) ($orderData['subtotal'] ?? 0);
+ $discount = (float) ($orderData['discount'] ?? 0);
+ $tax = (float) ($orderData['tax'] ?? 0);
+ <td style="font-size:13px; color:#555; padding:4px 0;">Tax</td>
+ <td align="right" style="font-size:13px; color:#1a1a1a; padding:4px 0;">₹{{ number_format($tax, 2) }}</td>
+ </tr>
+                 @if(! $isAllService)
+ <tr>
+ <td style="font-size:13px; color:#555; padding:4px 0;">Shipping</td>
+ <td align="right" style="font-size:13px; padding:4px 0; @if($shippingCost == 0) color:#22c55e; font-weight:600; @else color:#1a1a1a; @endif">
+ @if($shippingCost == 0) Free @else ₹{{ number_format($shippingCost, 2) }} @endif
+ </td>
+ </tr>
+                 @endif