Main#229
Conversation
Feat/presentation ready b2c
fix: resolve blank verification page by using CurrentMerchant decorator
fix: resolve whatsapp interactive crashes and improve OCR image search
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThe PR rebrands the platform from a hardware and construction-focused marketplace to a general e-commerce platform. Changes update product category references, user-facing messages, email content, invoice headers, test data, and UI placeholder text across backend services and frontend components. A new category synchronization script is introduced. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ❌ 3❌ Failed checks (2 warnings, 1 inconclusive)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
apps/web/src/app/(dashboard)/merchant/orders/[id]/page.tsx (1)
392-392:⚠️ Potential issue | 🟡 MinorTypo in placeholder text: "Broadcast Broadcast..."
The placeholder contains a duplicate word.
Proposed fix
- placeholder="Broadcast Broadcast..." + placeholder="Broadcast update..."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/app/`(dashboard)/merchant/orders/[id]/page.tsx at line 392, The placeholder string for the input/textarea currently reads "Broadcast Broadcast..." (duplicate word); update the placeholder prop in the JSX in the page component to remove the duplicated word — e.g., change placeholder="Broadcast Broadcast..." to a single, correct phrase such as "Broadcast..." or "Broadcast message..." in the component where the placeholder prop is set.apps/backend/src/modules/whatsapp/whatsapp.constants.ts (1)
68-76:⚠️ Potential issue | 🟠 MajorRemove orphaned wholesale button handler code.
The
handleBuyWholesalemethod (line 1542+) and its button routing (lines 458-461) are unreachable. No code path createsbuy_wholesale_prefixed button IDs—the buyer service creates buttons with thebuy_${productId}_${quantity}pattern instead. This dead code should be removed, along with the handler block at lines 458-461 and the associatedhandleWholesaleCheckoutStepandhandleWholesaleCheckoutStepmethods they reference.Note: Supplier-facing wholesale functionality (view_wholesale_order_ buttons) remains active and should be retained.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/backend/src/modules/whatsapp/whatsapp.constants.ts` around lines 68 - 76, Remove the dead buyer-side wholesale button handling: delete the unreachable handler block that routes button IDs starting with "buy_wholesale_" (the code that calls handleBuyWholesale), and remove the handleBuyWholesale and handleWholesaleCheckoutStep methods and any internal references to them; keep all supplier-facing wholesale code (e.g., handlers for "view_wholesale_order_" buttons) intact and ensure no remaining references or imports point to the deleted methods so the code compiles.apps/web/src/app/page.tsx (1)
98-119:⚠️ Potential issue | 🟡 MinorDuplicate category name in CATEGORIES array.
The category "Home & Kitchen" appears twice in the array (lines 100 and 115). This may be intentional for visual layout, but if distinct categories are expected, consider using different category names.
🔧 Suggested fix if unique categories are intended
{ - name: "Home & Kitchen", + name: "Office & Stationery", count: "200+", img: "/images/slideshow/slide-4.jpg", },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/app/page.tsx` around lines 98 - 119, The CATEGORIES constant in page.tsx contains a duplicated category name ("Home & Kitchen" appears twice); update the CATEGORIES array (the CATEGORIES variable) to ensure unique category entries by either removing the duplicate object or renaming its name field to the intended distinct category (e.g., "Furniture" or "Garden"), and keep the other properties (count, img) consistent with that change so the UI shows distinct categories.
🧹 Nitpick comments (10)
apps/web/src/app/(dashboard)/buyer/orders/[id]/page.tsx (1)
165-165: Consider using Next.jsScriptcomponent for external scripts.Loading external scripts via a raw
<script>tag in JSX bypasses Next.js optimizations. Thenext/scriptcomponent provides better control over loading strategy and performance.Proposed refactor
+import Script from "next/script"; // Then in the component return: - <script src="https://js.paystack.co/v1/inline.js" async></script> + <Script src="https://js.paystack.co/v1/inline.js" strategy="lazyOnload" />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/app/`(dashboard)/buyer/orders/[id]/page.tsx at line 165, Replace the raw <script> tag that loads Paystack with Next.js's Script component: import Script from 'next/script' in the component (page.tsx) and use <Script src="https://js.paystack.co/v1/inline.js" strategy="afterInteractive" /> (or appropriate loading strategy) instead of the inline tag so Next.js can optimize loading and lifecycle; remove the original raw <script> tag. Ensure the replacement is colocated where the original tag lived so any code that expects the Paystack global runs after the script loads.apps/backend/src/modules/order/order.service.ts (1)
142-151: Consider using actual product weight data for more accurate delivery fee estimates.The comment update removes the construction-specific reference, which aligns with the rebranding effort. However, the underlying logic still assumes all "bag" units weigh 50kg, which is typical for construction materials like cement but not accurate for general e-commerce products. Bags can contain items of vastly different weights (coffee at 1kg, rice at 5-25kg, pet food at 2-20kg, etc.).
For a general e-commerce platform, consider adding a
weightorweightKgfield to the product schema and using actual weight data instead of hardcoded assumptions based on unit type. This would provide more accurate delivery fee estimates and improve user experience.♻️ Proposed refactor to use product weight data
First, ensure the product schema includes weight information. Then update the weight estimation logic:
- // Estimate weight roughly, e.g., 50kg per unit if bulky, or fallback to 1kg - const estimatedWeightKg = - product.unit.toLowerCase() === "bag" ? 50 * quantity : 1 * quantity; + // Use actual product weight if available, otherwise fallback to conservative estimate + const unitWeightKg = product.weightKg ?? 1; // Add weightKg field to Product schema + const estimatedWeightKg = unitWeightKg * quantity;This approach provides accurate delivery costs while maintaining a safe fallback for products without weight data.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/backend/src/modules/order/order.service.ts` around lines 142 - 151, The current logic in order.service.ts estimates weight using product.unit and a hardcoded 50kg for "bag" which is inaccurate; instead add or use a product.weightKg (or weight) field on the Product model and change the weight calculation in the block that computes estimatedWeightKg to prefer product.weightKg * quantity, falling back to sensible defaults (e.g., unit-based heuristics or 1 kg per unit) only when product.weightKg is missing; update code references where estimatedWeightKg is passed to this.logisticsService.getQuote and ensure calculatedDeliveryFeeKobo is still set from quote.costKobo after the new calculation.apps/backend/test/helpers/test-setup.ts (1)
69-70: Use a unique merchant slug in fixtures to reduce flaky setup failures.A fixed slug can collide under reruns/parallel workers when test cleanup is incomplete. Consider making it unique like the email.
♻️ Suggested change
async createMockMerchant() { - const email = `test.merchant.${Date.now()}@swifta.store`; + const unique = Date.now(); + const email = `test.merchant.${unique}@swifta.store`; const user = await this.prisma.user.create({ data: { @@ merchantProfile: { create: { businessName: "Zaza Fashion Hub", - slug: "zaza-fashion-hub", + slug: `zaza-fashion-hub-${unique}`, businessAddress: "123 E2E Street, Lagos",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/backend/test/helpers/test-setup.ts` around lines 69 - 70, The fixture uses a fixed slug ("slug" property alongside "businessName") which can collide across test runs; update the fixture (where "businessName" and "slug" are set, e.g., the merchant fixture in test-setup.ts) to generate a unique slug per run (append a short random suffix, timestamp, or uuid) following the same approach used for the unique email so tests don't conflict under reruns/parallel workers.apps/backend/test/golden-path.e2e-spec.ts (1)
51-54: Keep fixture semantics coherent (iPhonewithbagsunit is confusing).Not functionally blocking, but aligning unit/category with the new product name will make this E2E path easier to reason about.
🧹 Possible cleanup
name: "iPhone 15 Pro Max", categoryTag: category.name, pricePerUnitKobo: 500000, - unit: "bags", + unit: "units", isActive: true,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/backend/test/golden-path.e2e-spec.ts` around lines 51 - 54, The product fixture uses name: "iPhone 15 Pro Max" but unit: "bags" which is confusing; update the fixture in apps/backend/test/golden-path.e2e-spec.ts so the unit field matches a phone (e.g., unit: "each" or "pcs") and ensure categoryTag/category.name reflects an electronics/mobile category instead of a mismatched one; adjust the name, unit, or categoryTag values in the test object (fields: name, unit, categoryTag, pricePerUnitKobo) so semantics are coherent.apps/backend/sync-categories.ts (3)
57-61: Unnecessaryasyncon catch handler.The catch handler doesn't
awaitanything, soasyncis superfluous and can be removed for clarity.♻️ Proposed fix
main() - .catch(async (e) => { + .catch((e) => { console.error("❌ SYNC FAILED:", e); process.exit(1); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/backend/sync-categories.ts` around lines 57 - 61, The catch handler passed to main() is declared async but doesn't await anything—remove the unnecessary async keyword to clarify intent; locate the main().catch(...) call and change the handler from "async (e) => { console.error(...); process.exit(1); }" to a plain "(e) => { console.error(...); process.exit(1); }" so no async marker remains on the catch callback.
32-48: Consider wrapping operations in a transaction for atomicity.If the script fails mid-execution (e.g., after upserting some categories but before deactivating legacy ones), the database could be left in an inconsistent state. While the script is idempotent and can be re-run, using
$transactionwould guarantee all-or-nothing semantics.♻️ Proposed transactional approach
- for (let i = 0; i < categoriesToSeed.length; i++) { - const cat = categoriesToSeed[i]; - await prisma.category.upsert({ - where: { slug: cat.slug }, - update: { name: cat.name, icon: cat.icon, sortOrder: i, isActive: true }, - create: { name: cat.name, slug: cat.slug, icon: cat.icon, sortOrder: i, isActive: true }, - }); - } - - // Now, let's DEACTIVATE any legacy/test categories (like Phones & Gadgets or Baby & Kids) that are not in our official top 10 list - // This ensures only our clean 10 categories show up in the dropdown - const validSlugs = categoriesToSeed.map(c => c.slug); - - await prisma.category.updateMany({ - where: { slug: { notIn: validSlugs } }, - data: { isActive: false } - }); + await prisma.$transaction(async (tx) => { + for (let i = 0; i < categoriesToSeed.length; i++) { + const cat = categoriesToSeed[i]; + await tx.category.upsert({ + where: { slug: cat.slug }, + update: { name: cat.name, icon: cat.icon, sortOrder: i, isActive: true }, + create: { name: cat.name, slug: cat.slug, icon: cat.icon, sortOrder: i, isActive: true }, + }); + } + + const validSlugs = categoriesToSeed.map(c => c.slug); + await tx.category.updateMany({ + where: { slug: { notIn: validSlugs } }, + data: { isActive: false }, + }); + });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/backend/sync-categories.ts` around lines 32 - 48, Wrap the series of upserts and the final updateMany in a single Prisma transaction to make the whole sync atomic: perform all prisma.category.upsert calls and the prisma.category.updateMany inside prisma.$transaction so either all category upserts and deactivations succeed or none are applied; locate the loop that calls prisma.category.upsert and the subsequent prisma.category.updateMany and move them into a prisma.$transaction block (or pass an array of operations to prisma.$transaction) to achieve all-or-nothing behavior.
10-16: Consider validatingDATABASE_URLbefore use.If
DATABASE_URLis undefined or malformed, the Pool initialization will fail with an unclear error. A defensive check provides clearer diagnostics.Additionally, the current Pool-based adapter approach is supported but Prisma 5's preferred pattern is to pass
connectionStringdirectly to PrismaPg, which simplifies configuration:+ if (!process.env.DATABASE_URL) { + throw new Error("DATABASE_URL environment variable is not set"); + } + - const pool = new Pool({ - connectionString: process.env.DATABASE_URL, - max: 2, - idleTimeoutMillis: 10000, - }); - const adapter = new PrismaPg(pool); + const adapter = new PrismaPg({ + connectionString: process.env.DATABASE_URL, + });If custom pool settings (max connections, idle timeout) are required, the Pool approach can be retained with the environment validation added.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/backend/sync-categories.ts` around lines 10 - 16, Validate process.env.DATABASE_URL before constructing the PG Pool: if DATABASE_URL is missing or empty throw or log a clear error and exit; then either pass the URL directly into PrismaPg (new PrismaPg({ connectionString: process.env.DATABASE_URL })) and construct PrismaClient with that adapter, or if you must keep the Pool-based approach (new Pool({ connectionString: process.env.DATABASE_URL, max, idleTimeoutMillis })), perform the same validation beforehand and pass the validated URL into Pool; update references to Pool, PrismaPg, PrismaClient, connectionString, max and idleTimeoutMillis accordingly.apps/backend/src/modules/email/email.service.ts (1)
99-99: Consider spelling consistency: "catalogues" vs "catalogs".The text uses British English spelling ("catalogues"). If the codebase consistently uses American English elsewhere, consider changing to "catalogs" for consistency.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/backend/src/modules/email/email.service.ts` at line 99, Change the British spelling "catalogues" to the American spelling "catalogs" in the email template string (the literal that currently reads `<p>Next step: Browse our merchant catalogues and start shopping for your favorite products.</p>`); locate the template in email.service.ts (the email body/HTML builder or send email method where that string is defined) and update the word to "catalogs" to match the project's American-English spelling.precommit.ps1 (1)
4-4: Make git flow rerunnable (branch exists / no-op commit cases).Line 4 fails on reruns if the branch already exists, and Line 21 fails when there are no staged changes. Guarding these cases will make this script more reliable.
Rerun-safe git flow
Write-Host "Checking out branch..." -git checkout -b feat/presentation-ready-b2c +git show-ref --verify --quiet refs/heads/feat/presentation-ready-b2c +if ($LASTEXITCODE -eq 0) { + git checkout feat/presentation-ready-b2c +} else { + git checkout -b feat/presentation-ready-b2c +} ... git add apps/backend apps/web precommit.ps1 -git commit -m "feat: complete b2c marketplace transition and generalization" -git push origin feat/presentation-ready-b2c +git diff --cached --quiet +if ($LASTEXITCODE -ne 0) { + git commit -m "feat: complete b2c marketplace transition and generalization" + git push origin feat/presentation-ready-b2c +}Also applies to: 21-22
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@precommit.ps1` at line 4, The script currently unconditionally runs git checkout -b feat/presentation-ready-b2c and attempts a commit even when no staged changes exist; make the git flow rerunnable by first checking whether the branch exists (e.g., using git rev-parse --verify or git branch --list) and if it exists, switch to it with git checkout feat/presentation-ready-b2c, otherwise create it with git checkout -b feat/presentation-ready-b2c; similarly, before running the commit commands near the current Line 21-22, check for staged changes (e.g., git diff --cached --quiet / git diff --staged --name-only) and skip the commit step when there are none so the script becomes a no-op instead of failing.apps/web/src/components/shared/product-card.tsx (1)
63-66: Dead code: conditional quantity calculation is now unreachable.Since
priceTypeis now hardcoded toPriceType.RETAIL, the ternary condition on lines 64-66 will always evaluate to the first branch. The wholesale quantity path (p.minOrderQuantity || 1) is unreachable.Consider simplifying:
♻️ Proposed simplification
- const priceType = PriceType.RETAIL; - const qty = priceType === PriceType.RETAIL - ? (p.minOrderQuantityConsumer || 1) - : (p.minOrderQuantity || 1); + const qty = p.minOrderQuantityConsumer || 1; - await addToCart(p.id, qty, priceType); + await addToCart(p.id, qty, PriceType.RETAIL);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/components/shared/product-card.tsx` around lines 63 - 66, The conditional computing qty is dead because priceType is hardcoded to PriceType.RETAIL; replace the ternary with a direct assignment using the retail path (set qty to p.minOrderQuantityConsumer || 1) and remove the unreachable wholesale branch, or if wholesale behavior is needed later, make priceType a variable/prop instead of hardcoding PriceType.RETAIL; update references to priceType, qty, p.minOrderQuantityConsumer and p.minOrderQuantity accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/backend/src/modules/reorder/reorder.constants.ts`:
- Line 9: REORDER_WINDOW_DAYS currently uses the key "office-stationery" which
doesn't match the lowercased categoryTag ("office & stationery"); update the key
in the REORDER_WINDOW_DAYS constant from "office-stationery" to "office &
stationery" so lookups by categoryTag.toLowerCase() correctly return 30 days
(refer to REORDER_WINDOW_DAYS and any code that does categoryTag.toLowerCase()
or uses the categoryTag value).
In `@apps/backend/src/prisma/seed.ts`:
- Around line 77-87: The seed removed "building-materials" and introduced
"office-stationery", but the shared categories constants (categories.ts) and
tests still reference the old category causing inconsistency; update the
categories list in packages/shared/src/constants/categories.ts to replace
"Building Materials"/"building-materials" with "Office &
Stationery"/"office-stationery" (matching the seed entries and slug), and adjust
the e2e test (golden-path.e2e-spec.ts) to either upsert "office-stationery"
instead of "building-materials" or fetch the canonical seed categories
dynamically before asserting/upserting so the test aligns with the seeded
canonical set; also verify reorder.constants.ts entries remain consistent with
the updated slug "office-stationery".
In `@apps/web/src/components/buyer/catalogue/category-card.tsx`:
- Line 16: In category-card.tsx update the icon mapping for hardware/tool to
match category-stories-bar.tsx by returning "grid_view" instead of
"shopping_bag" (the conditional in the function that checks
name.includes("hardware") || name.includes("tool")); ensure the same symbol is
used in both components so iconography is consistent across CategoryCard and the
logic in category-stories-bar.tsx.
In `@apps/web/src/components/merchant/products/product-creation-drawer.tsx`:
- Around line 305-306: The placeholder in product-creation-drawer.tsx uses
product wording while the adjacent label and helper still say “Material”; update
the surrounding copy to use consistent product terminology (e.g., change the
label from "Material" to "Product name" or "Product" and update the
helper/description text to match the example placeholder) so the label, helper
and the input placeholder align in the ProductCreationDrawer component and the
corresponding form field (the input with the provided placeholder and its
label/description).
In `@apps/web/src/components/shared/product-detail-view.tsx`:
- Around line 458-463: The "Wholesale Threshold" row is currently shown for all
owners via effectiveIsOwner even when no wholesale pricing exists; update the
render guard to require both effectiveIsOwner and a wholesale price/availability
on the product (e.g. check product?.wholesalePrice or
product?.wholesale?.price/existence) before rendering the "Wholesale Threshold"
block so the row only appears when wholesale pricing is available, leaving the
displayed value product?.minOrderQuantity unchanged.
In `@precommit.ps1`:
- Line 7: The script uses Set-Location with hardcoded relative paths which
breaks if invoked from a different working directory; update precommit.ps1 to
first change to the script's directory (use $PSScriptRoot or Split-Path -Parent
$MyInvocation.MyCommand.Path) and then call the existing Set-Location
"apps/backend" (and the other two occurrences referenced) so all three
Set-Location calls are anchored off the script location rather than the current
working directory; modify the logic around the Set-Location usages in
precommit.ps1 to compute and cd to the script folder before executing those
relative Set-Location calls.
- Line 3: Replace all uses of Write-Host in the script with Write-Information
(e.g., change Write-Host "Checking out branch..." to Write-Information "Checking
out branch..." -InformationAction Continue) so output uses the PowerShell
information stream; alternatively, set $InformationPreference = 'Continue' at
the top of the script and remove the per-call -InformationAction flags to keep
behavior consistent across the Write-Information calls that currently replace
Write-Host.
- Line 20: The script currently runs the unsafe command "git add ."; remove that
and instead stage only intended files by computing the set to commit and calling
"git add -- <paths>" (or, if you only want tracked changes, use "git add -u"),
ensure you skip/abort when the computed list is empty, and use explicit paths
passed in (e.g., from an env var or git diff output) so the script never blindly
stages all untracked files.
---
Outside diff comments:
In `@apps/backend/src/modules/whatsapp/whatsapp.constants.ts`:
- Around line 68-76: Remove the dead buyer-side wholesale button handling:
delete the unreachable handler block that routes button IDs starting with
"buy_wholesale_" (the code that calls handleBuyWholesale), and remove the
handleBuyWholesale and handleWholesaleCheckoutStep methods and any internal
references to them; keep all supplier-facing wholesale code (e.g., handlers for
"view_wholesale_order_" buttons) intact and ensure no remaining references or
imports point to the deleted methods so the code compiles.
In `@apps/web/src/app/`(dashboard)/merchant/orders/[id]/page.tsx:
- Line 392: The placeholder string for the input/textarea currently reads
"Broadcast Broadcast..." (duplicate word); update the placeholder prop in the
JSX in the page component to remove the duplicated word — e.g., change
placeholder="Broadcast Broadcast..." to a single, correct phrase such as
"Broadcast..." or "Broadcast message..." in the component where the placeholder
prop is set.
In `@apps/web/src/app/page.tsx`:
- Around line 98-119: The CATEGORIES constant in page.tsx contains a duplicated
category name ("Home & Kitchen" appears twice); update the CATEGORIES array (the
CATEGORIES variable) to ensure unique category entries by either removing the
duplicate object or renaming its name field to the intended distinct category
(e.g., "Furniture" or "Garden"), and keep the other properties (count, img)
consistent with that change so the UI shows distinct categories.
---
Nitpick comments:
In `@apps/backend/src/modules/email/email.service.ts`:
- Line 99: Change the British spelling "catalogues" to the American spelling
"catalogs" in the email template string (the literal that currently reads
`<p>Next step: Browse our merchant catalogues and start shopping for your
favorite products.</p>`); locate the template in email.service.ts (the email
body/HTML builder or send email method where that string is defined) and update
the word to "catalogs" to match the project's American-English spelling.
In `@apps/backend/src/modules/order/order.service.ts`:
- Around line 142-151: The current logic in order.service.ts estimates weight
using product.unit and a hardcoded 50kg for "bag" which is inaccurate; instead
add or use a product.weightKg (or weight) field on the Product model and change
the weight calculation in the block that computes estimatedWeightKg to prefer
product.weightKg * quantity, falling back to sensible defaults (e.g., unit-based
heuristics or 1 kg per unit) only when product.weightKg is missing; update code
references where estimatedWeightKg is passed to this.logisticsService.getQuote
and ensure calculatedDeliveryFeeKobo is still set from quote.costKobo after the
new calculation.
In `@apps/backend/sync-categories.ts`:
- Around line 57-61: The catch handler passed to main() is declared async but
doesn't await anything—remove the unnecessary async keyword to clarify intent;
locate the main().catch(...) call and change the handler from "async (e) => {
console.error(...); process.exit(1); }" to a plain "(e) => { console.error(...);
process.exit(1); }" so no async marker remains on the catch callback.
- Around line 32-48: Wrap the series of upserts and the final updateMany in a
single Prisma transaction to make the whole sync atomic: perform all
prisma.category.upsert calls and the prisma.category.updateMany inside
prisma.$transaction so either all category upserts and deactivations succeed or
none are applied; locate the loop that calls prisma.category.upsert and the
subsequent prisma.category.updateMany and move them into a prisma.$transaction
block (or pass an array of operations to prisma.$transaction) to achieve
all-or-nothing behavior.
- Around line 10-16: Validate process.env.DATABASE_URL before constructing the
PG Pool: if DATABASE_URL is missing or empty throw or log a clear error and
exit; then either pass the URL directly into PrismaPg (new PrismaPg({
connectionString: process.env.DATABASE_URL })) and construct PrismaClient with
that adapter, or if you must keep the Pool-based approach (new Pool({
connectionString: process.env.DATABASE_URL, max, idleTimeoutMillis })), perform
the same validation beforehand and pass the validated URL into Pool; update
references to Pool, PrismaPg, PrismaClient, connectionString, max and
idleTimeoutMillis accordingly.
In `@apps/backend/test/golden-path.e2e-spec.ts`:
- Around line 51-54: The product fixture uses name: "iPhone 15 Pro Max" but
unit: "bags" which is confusing; update the fixture in
apps/backend/test/golden-path.e2e-spec.ts so the unit field matches a phone
(e.g., unit: "each" or "pcs") and ensure categoryTag/category.name reflects an
electronics/mobile category instead of a mismatched one; adjust the name, unit,
or categoryTag values in the test object (fields: name, unit, categoryTag,
pricePerUnitKobo) so semantics are coherent.
In `@apps/backend/test/helpers/test-setup.ts`:
- Around line 69-70: The fixture uses a fixed slug ("slug" property alongside
"businessName") which can collide across test runs; update the fixture (where
"businessName" and "slug" are set, e.g., the merchant fixture in test-setup.ts)
to generate a unique slug per run (append a short random suffix, timestamp, or
uuid) following the same approach used for the unique email so tests don't
conflict under reruns/parallel workers.
In `@apps/web/src/app/`(dashboard)/buyer/orders/[id]/page.tsx:
- Line 165: Replace the raw <script> tag that loads Paystack with Next.js's
Script component: import Script from 'next/script' in the component (page.tsx)
and use <Script src="https://js.paystack.co/v1/inline.js"
strategy="afterInteractive" /> (or appropriate loading strategy) instead of the
inline tag so Next.js can optimize loading and lifecycle; remove the original
raw <script> tag. Ensure the replacement is colocated where the original tag
lived so any code that expects the Paystack global runs after the script loads.
In `@apps/web/src/components/shared/product-card.tsx`:
- Around line 63-66: The conditional computing qty is dead because priceType is
hardcoded to PriceType.RETAIL; replace the ternary with a direct assignment
using the retail path (set qty to p.minOrderQuantityConsumer || 1) and remove
the unreachable wholesale branch, or if wholesale behavior is needed later, make
priceType a variable/prop instead of hardcoding PriceType.RETAIL; update
references to priceType, qty, p.minOrderQuantityConsumer and p.minOrderQuantity
accordingly.
In `@precommit.ps1`:
- Line 4: The script currently unconditionally runs git checkout -b
feat/presentation-ready-b2c and attempts a commit even when no staged changes
exist; make the git flow rerunnable by first checking whether the branch exists
(e.g., using git rev-parse --verify or git branch --list) and if it exists,
switch to it with git checkout feat/presentation-ready-b2c, otherwise create it
with git checkout -b feat/presentation-ready-b2c; similarly, before running the
commit commands near the current Line 21-22, check for staged changes (e.g., git
diff --cached --quiet / git diff --staged --name-only) and skip the commit step
when there are none so the script becomes a no-op instead of failing.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 4af81415-8d53-4ed6-b9bd-e434241264d7
📒 Files selected for processing (33)
apps/backend/src/common/filters/global-exception.filter.tsapps/backend/src/modules/email/email.service.tsapps/backend/src/modules/order/invoice.service.tsapps/backend/src/modules/order/order.service.tsapps/backend/src/modules/reorder/reorder.constants.tsapps/backend/src/modules/verification/verification.controller.tsapps/backend/src/modules/verification/verification.service.tsapps/backend/src/modules/whatsapp/image-search.service.tsapps/backend/src/modules/whatsapp/whatsapp-buyer.service.tsapps/backend/src/modules/whatsapp/whatsapp-supplier.service.tsapps/backend/src/modules/whatsapp/whatsapp.constants.tsapps/backend/src/modules/whatsapp/whatsapp.service.tsapps/backend/src/prisma/seed.tsapps/backend/sync-categories.tsapps/backend/test/golden-path.e2e-spec.tsapps/backend/test/helpers/test-setup.tsapps/web/src/app/(auth)/admin/join/page.tsxapps/web/src/app/(auth)/admin/verify-token/page.tsxapps/web/src/app/(dashboard)/admin/analytics/page.tsxapps/web/src/app/(dashboard)/admin/categories/page.tsxapps/web/src/app/(dashboard)/buyer/orders/[id]/page.tsxapps/web/src/app/(dashboard)/merchant/orders/[id]/page.tsxapps/web/src/app/(dashboard)/merchant/settings/page.tsxapps/web/src/app/page.tsxapps/web/src/components/auth/register-flow/account-details-step.tsxapps/web/src/components/buyer/catalogue/category-card.tsxapps/web/src/components/buyer/catalogue/category-stories-bar.tsxapps/web/src/components/layout/merchant-sidebar.tsxapps/web/src/components/merchant/products/product-creation-drawer.tsxapps/web/src/components/merchant/products/product-modal.tsxapps/web/src/components/shared/product-card.tsxapps/web/src/components/shared/product-detail-view.tsxprecommit.ps1
💤 Files with no reviewable changes (1)
- apps/web/src/components/layout/merchant-sidebar.tsx
| ? `<p>Next step: Complete your business profile and start listing your products to receive orders from buyers.</p>` | ||
| : `<p>Next step: Browse our merchant catalogues and start shopping for your favorite products.</p>` |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for RFQ/quote-related code across the backend to assess if the workflow is still active
# Check for RFQ references in service and controller files
echo "=== RFQ references in backend services ==="
rg -n --type=ts -C2 'RFQ|rfq|quote' --glob 'apps/backend/src/**/*.service.ts' --glob 'apps/backend/src/**/*.controller.ts' | head -100
# Check for recent changes to RFQ-related files
echo -e "\n=== Recent git log for RFQ-related changes ==="
git log --oneline --all -20 --grep="RFQ\|quote" 2>/dev/null || echo "Git history check skipped"Repository: onerandomdevv/Swifta
Length of output: 10497
🏁 Script executed:
#!/bin/bash
# Read the email.service.ts file to examine RFQ methods
echo "=== Examining email.service.ts structure ==="
wc -l apps/backend/src/modules/email/email.service.ts
echo -e "\n=== Lines around 98-99 (welcome email) ==="
sed -n '90,110p' apps/backend/src/modules/email/email.service.ts
echo -e "\n=== Lines 120-190 (RFQ methods) ==="
sed -n '120,190p' apps/backend/src/modules/email/email.service.tsRepository: onerandomdevv/Swifta
Length of output: 4746
🏁 Script executed:
#!/bin/bash
# Check if RFQ email methods are called anywhere in the codebase
echo "=== Usage of RFQ email methods ==="
rg -n 'sendNewRFQNotification|sendQuoteSubmittedNotification|sendQuoteAcceptedNotification' --type=ts
echo -e "\n=== Check git log for these specific methods ==="
git log --oneline --all -p -- apps/backend/src/modules/email/email.service.ts | grep -A5 -B5 'sendNewRFQ\|sendQuoteSubmitted\|sendQuoteAccepted' | head -50Repository: onerandomdevv/Swifta
Length of output: 1938
Update welcome email to acknowledge both RFQ and direct ordering workflows.
The email.service.ts file confirms both workflows are active:
- RFQ methods exist and are actively called by notification.processor.ts (lines 102, 121, 146)
- Welcome email messaging only references direct ordering ("receive orders from buyers", "start shopping")
New users won't be informed about the RFQ workflow available to them. Update the welcome email to mention both options, or if RFQ is being fully deprecated, complete its removal and mark the methods accordingly.
| agriculture: 14, | ||
| "food & groceries": 7, | ||
| "building materials": 30, | ||
| "office-stationery": 30, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check how categoryTag is assigned to products and what format it uses
ast-grep --pattern 'categoryTag: $_'
# Also check the Product model/type definition
rg -n "categoryTag" --type ts -A2 -B2Repository: onerandomdevv/Swifta
Length of output: 23848
🏁 Script executed:
# Find seed data or category creation files
fd -e ts -e js | xargs grep -l "office-stationery\|Office.*Stationery" | head -20Repository: onerandomdevv/Swifta
Length of output: 181
🏁 Script executed:
# Also search for category definitions or seed data
rg "office-stationery|Office.*Stationery" --type ts -B2 -A2 -iRepository: onerandomdevv/Swifta
Length of output: 1343
Fix key mismatch in REORDER_WINDOW_DAYS: category name lowercases to "office & stationery", not "office-stationery".
The categoryTag field stores the category display name ("Office & Stationery"), which when lowercased becomes "office & stationery". The current key "office-stationery" won't match, causing the lookup to fail and fall back to the default 21-day window instead of 30 days.
Change the key to "office & stationery" to match all other category names in the constants object.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/backend/src/modules/reorder/reorder.constants.ts` at line 9,
REORDER_WINDOW_DAYS currently uses the key "office-stationery" which doesn't
match the lowercased categoryTag ("office & stationery"); update the key in the
REORDER_WINDOW_DAYS constant from "office-stationery" to "office & stationery"
so lookups by categoryTag.toLowerCase() correctly return 30 days (refer to
REORDER_WINDOW_DAYS and any code that does categoryTag.toLowerCase() or uses the
categoryTag value).
| name: "Office & Stationery", | ||
| slug: "office-stationery", | ||
| icon: "print", | ||
| }, | ||
| { | ||
| name: "Food & Groceries", | ||
| slug: "food-groceries", | ||
| icon: "local_grocery_store", | ||
| }, | ||
| { name: "Books & Media", slug: "books-media", icon: "menu_book" }, | ||
| { name: "Other", slug: "other", icon: "category" }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if there are any references to the old "building-materials" or "Building Materials" category
rg -i "building.?materials" --type ts --type tsx -g '!*.spec.ts' -g '!*.test.ts'Repository: onerandomdevv/Swifta
Length of output: 91
🏁 Script executed:
#!/bin/bash
# Check if there are any references to the old "building-materials" or "Building Materials" category
rg -i "building.?materials" --type ts -g '!*.spec.ts' -g '!*.test.ts'Repository: onerandomdevv/Swifta
Length of output: 334
🏁 Script executed:
#!/bin/bash
# Check the reorder.constants.ts file to verify the slug usage
fd -e ts -e js reorder.constantsRepository: onerandomdevv/Swifta
Length of output: 117
🏁 Script executed:
#!/bin/bash
# View the seed.ts file around lines 77-87 to see the full context and category changes
cat -n apps/backend/src/prisma/seed.ts | sed -n '60,100p'Repository: onerandomdevv/Swifta
Length of output: 1760
🏁 Script executed:
#!/bin/bash
# View reorder.constants.ts to check if it keys on "office-stationery"
cat -n apps/backend/src/modules/reorder/reorder.constants.tsRepository: onerandomdevv/Swifta
Length of output: 615
🏁 Script executed:
#!/bin/bash
# View the categories.ts constants file to see if it still defines "Building Materials"
cat -n packages/shared/src/constants/categories.tsRepository: onerandomdevv/Swifta
Length of output: 646
🏁 Script executed:
#!/bin/bash
# Search for any products in seed.ts that might be using "Building Materials" or "building-materials"
rg -i "building.?materials|office.?stationery" apps/backend/src/prisma/seed.tsRepository: onerandomdevv/Swifta
Length of output: 96
🏁 Script executed:
#!/bin/bash
# Search the entire seed.ts file for any product seeding that mentions categories or Building Materials
wc -l apps/backend/src/prisma/seed.tsRepository: onerandomdevv/Swifta
Length of output: 99
🏁 Script executed:
#!/bin/bash
# Look for product seeding section in seed.ts to see if any products are tagged with categories
rg -A 5 -B 5 "Building Materials|building-materials|office-stationery|Office & Stationery" apps/backend/src/prisma/seed.ts | head -50Repository: onerandomdevv/Swifta
Length of output: 517
🏁 Script executed:
#!/bin/bash
# Check if there are any products being created in the seed that reference categoryTag
rg "categoryTag" apps/backend/src/prisma/seed.ts -A 2 -B 2Repository: onerandomdevv/Swifta
Length of output: 46
🏁 Script executed:
#!/bin/bash
# Check git history to see if "Building Materials" was in the old seed.ts
git log --oneline -p apps/backend/src/prisma/seed.ts | grep -A 3 -B 3 "Building Materials" | head -30Repository: onerandomdevv/Swifta
Length of output: 46
🏁 Script executed:
#!/bin/bash
# Search for any other references to "building-materials" slug in the codebase (excluding tests)
rg "building-materials" --type ts -g '!*.spec.ts' -g '!*.test.ts'Repository: onerandomdevv/Swifta
Length of output: 266
🏁 Script executed:
#!/an/bin/bash
# View more of the seed.ts to see if there's any category deletion logic
sed -n '85,147p' apps/backend/src/prisma/seed.tsRepository: onerandomdevv/Swifta
Length of output: 2238
🏁 Script executed:
#!/an/bin/bash
# Check if there are any deleteMany or delete operations on categories
rg "deleteMany|delete" apps/backend/src/prisma/seed.ts -A 2 -B 2Repository: onerandomdevv/Swifta
Length of output: 1000
🏁 Script executed:
#!/bin/bash
# Check the entire categories array in seed.ts to see if Building Materials was there before
sed -n '67,90p' apps/backend/src/prisma/seed.tsRepository: onerandomdevv/Swifta
Length of output: 1000
🏁 Script executed:
#!/bin/bash
# Search for git changes to see the diff of what was replaced
git diff HEAD apps/backend/src/prisma/seed.ts | head -80Repository: onerandomdevv/Swifta
Length of output: 46
🏁 Script executed:
#!/bin/bash
# Check git log to see recent changes to seed.ts
git log --oneline apps/backend/src/prisma/seed.ts | head -10Repository: onerandomdevv/Swifta
Length of output: 142
🏁 Script executed:
#!/bin/bash
# Check the actual git status of the file
git status apps/backend/src/prisma/seed.tsRepository: onerandomdevv/Swifta
Length of output: 129
🏁 Script executed:
#!/bin/bash
# If there's a change, look at the actual diff with more context
git show HEAD:apps/backend/src/prisma/seed.ts 2>/dev/null | sed -n '67,90p' || echo "No previous version found"Repository: onerandomdevv/Swifta
Length of output: 1000
🏁 Script executed:
#!/bin/bash
# Check the parent commit of the current HEAD to see the previous state
git show HEAD~1:apps/backend/src/prisma/seed.ts 2>/dev/null | sed -n '67,90p' || echo "Cannot access parent commit"Repository: onerandomdevv/Swifta
Length of output: 46
🏁 Script executed:
#!/bin/bash
# Let's also check if "Building Materials" ever appeared in the recent git history
git log -p --all -- apps/backend/src/prisma/seed.ts | grep -A 10 -B 10 "Building Materials" | head -40Repository: onerandomdevv/Swifta
Length of output: 46
🏁 Script executed:
#!/bin/bash
# Let's check all commits that touched seed.ts to see when categories were added
git log --all --oneline -- apps/backend/src/prisma/seed.tsRepository: onerandomdevv/Swifta
Length of output: 2413
🏁 Script executed:
#!/bin/bash
# Check if there are any other seed files or if this file was recently created
fd -e ts -path "*/prisma/*" -name "*seed*"Repository: onerandomdevv/Swifta
Length of output: 232
🏁 Script executed:
#!/bin/bash
# Let's look at the complete diff for the recent changes to understand the PR
git show 31e778f:apps/backend/src/prisma/seed.ts 2>/dev/null | sed -n '67,90p' || echo "Could not access commit"Repository: onerandomdevv/Swifta
Length of output: 1000
🏁 Script executed:
#!/bin/bash
# Check the commit that expanded to building materials
git show 9b7a506:apps/backend/src/prisma/seed.ts 2>/dev/null | sed -n '67,90p' || echo "Could not access that commit"Repository: onerandomdevv/Swifta
Length of output: 743
🏁 Script executed:
#!/bin/bash
# Check a more recent commit to see if Building Materials was ever in seed.ts
git show 1f22843:apps/backend/src/prisma/seed.ts 2>/dev/null | grep -A 30 "categoriesToSeed" | head -40Repository: onerandomdevv/Swifta
Length of output: 1427
🏁 Script executed:
#!/bin/bash
# Check if there are any products created outside of the seed that might be affected
rg "product.*create|prisma.product.create" apps/backend/src --type ts -g '!*.spec.ts' -g '!*.test.ts' | head -20Repository: onerandomdevv/Swifta
Length of output: 380
🏁 Script executed:
#!/bin/bash
# Check the test file to see if it's using seeded data or creating its own
cat -n apps/backend/test/golden-path.e2e-spec.ts | sed -n '1,50p'Repository: onerandomdevv/Swifta
Length of output: 1739
Clarify category definition inconsistency and test compatibility.
The category change from "Building Materials" to "Office & Stationery" introduces a mismatch in the codebase: the shared constants file (packages/shared/src/constants/categories.ts) still defines "Building Materials" while the seed no longer creates it. Additionally, the test file (golden-path.e2e-spec.ts) explicitly upserts the "building-materials" category, which will still work but is now orphaned from the seed's canonical list.
The reorder.constants.ts correctly uses "office-stationery" (30-day window), but has no entry for "building-materials". If products ever get tagged with the old category, they'll fall back to the default 21-day window.
Update the shared constants to reflect the new category set, and align the test to use one of the seed categories or dynamically fetch available categories.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/backend/src/prisma/seed.ts` around lines 77 - 87, The seed removed
"building-materials" and introduced "office-stationery", but the shared
categories constants (categories.ts) and tests still reference the old category
causing inconsistency; update the categories list in
packages/shared/src/constants/categories.ts to replace "Building
Materials"/"building-materials" with "Office & Stationery"/"office-stationery"
(matching the seed entries and slug), and adjust the e2e test
(golden-path.e2e-spec.ts) to either upsert "office-stationery" instead of
"building-materials" or fetch the canonical seed categories dynamically before
asserting/upserting so the test aligns with the seeded canonical set; also
verify reorder.constants.ts entries remain consistent with the updated slug
"office-stationery".
| if (name.includes("electronic")) return "devices"; | ||
| if (name.includes("fashion") || name.includes("cloth")) return "apparel"; | ||
| if (name.includes("hardware") || name.includes("tool")) return "construction"; | ||
| if (name.includes("hardware") || name.includes("tool")) return "shopping_bag"; |
There was a problem hiding this comment.
Inconsistent icon mapping with category-stories-bar.tsx.
This component maps hardware/tool categories to shopping_bag, but category-stories-bar.tsx (line 35) maps the same categories to grid_view. Consider using the same icon for consistency across components.
🔧 Suggested fix for consistency
- if (name.includes("hardware") || name.includes("tool")) return "shopping_bag";
+ if (name.includes("hardware") || name.includes("tool")) return "grid_view";📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (name.includes("hardware") || name.includes("tool")) return "shopping_bag"; | |
| if (name.includes("hardware") || name.includes("tool")) return "grid_view"; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/components/buyer/catalogue/category-card.tsx` at line 16, In
category-card.tsx update the icon mapping for hardware/tool to match
category-stories-bar.tsx by returning "grid_view" instead of "shopping_bag" (the
conditional in the function that checks name.includes("hardware") ||
name.includes("tool")); ensure the same symbol is used in both components so
iconography is consistent across CategoryCard and the logic in
category-stories-bar.tsx.
| placeholder="e.g. Apple iPhone 15 Pro (128GB)" | ||
| className="w-full p-6 text-xl font-black border-2 border-border-light bg-background-secondary/50 rounded-[1.5rem] focus:border-primary focus:bg-surface transition-all outline-none placeholder:text-foreground-muted text-foreground uppercase tracking-tighter" |
There was a problem hiding this comment.
Use consistent product terminology in the surrounding field copy.
The placeholder is now product-based, but adjacent copy still says “Material” (label and description helper), which creates mixed wording in the same flow.
Suggested copy alignment
- Material Denomination
+ Product Name
...
- placeholder="Provide detailed material transparency - origin, grade, quality certificates..."
+ placeholder="Provide detailed product specifications — origin, quality, certifications..."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/components/merchant/products/product-creation-drawer.tsx` around
lines 305 - 306, The placeholder in product-creation-drawer.tsx uses product
wording while the adjacent label and helper still say “Material”; update the
surrounding copy to use consistent product terminology (e.g., change the label
from "Material" to "Product name" or "Product" and update the helper/description
text to match the example placeholder) so the label, helper and the input
placeholder align in the ProductCreationDrawer component and the corresponding
form field (the input with the provided placeholder and its label/description).
| {effectiveIsOwner && ( | ||
| <div className="flex justify-between py-3 border-b border-slate-50 dark:border-white/5"> | ||
| <span className="text-[10px] font-bold text-slate-400 uppercase tracking-widest">Wholesale Threshold</span> | ||
| <span className="font-bold text-slate-900 dark:text-white text-xs">{product?.minOrderQuantity} Units</span> | ||
| </div> | ||
| )} |
There was a problem hiding this comment.
Guard the “Wholesale Threshold” row with wholesale availability too.
This row now renders for all owners, even when no wholesale price exists, which makes the specs tab inconsistent with the pricing section.
Suggested fix
- {effectiveIsOwner && (
+ {effectiveIsOwner && wholesale > 0 && (
<div className="flex justify-between py-3 border-b border-slate-50 dark:border-white/5">
<span className="text-[10px] font-bold text-slate-400 uppercase tracking-widest">Wholesale Threshold</span>
<span className="font-bold text-slate-900 dark:text-white text-xs">{product?.minOrderQuantity} Units</span>
</div>
)}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/components/shared/product-detail-view.tsx` around lines 458 -
463, The "Wholesale Threshold" row is currently shown for all owners via
effectiveIsOwner even when no wholesale pricing exists; update the render guard
to require both effectiveIsOwner and a wholesale price/availability on the
product (e.g. check product?.wholesalePrice or
product?.wholesale?.price/existence) before rendering the "Wholesale Threshold"
block so the row only appears when wholesale pricing is available, leaving the
displayed value product?.minOrderQuantity unchanged.
| @@ -0,0 +1,24 @@ | |||
| $ErrorActionPreference = "Stop" | |||
|
|
|||
| Write-Host "Checking out branch..." | |||
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "precommit.ps1" -type fRepository: onerandomdevv/Swifta
Length of output: 79
🏁 Script executed:
cat -n ./precommit.ps1Repository: onerandomdevv/Swifta
Length of output: 833
🌐 Web query:
PowerShell Write-Host vs Write-Information best practices
💡 Result:
PowerShell best practices recommend avoiding Write-Host in reusable scripts and functions due to its historical limitations in automation, though it has improved since PowerShell 5.0. Instead, prefer structured output streams like Write-Information for informational messages that can be controlled, redirected, or suppressed. Key differences and guidelines: - Write-Host: Primarily for host-display-only output, such as colored prompts or interactive user messages. Since PowerShell 5.0, it wraps Write-Information (with tag 'PSHOST'), allowing capture/suppression via -InformationAction (except Ignore always suppresses), but $InformationPreference does not affect it by default—it always displays. Supports colors (-ForegroundColor, -BackgroundColor). Not suitable for data output or automation as it bypasses the success pipeline. - Write-Information: For structured informational messages to the information stream (stream 6). Default $InformationPreference is 'SilentlyContinue' (suppressed), controllable via -InformationAction or preference variable. Supports -Tags for filtering. Ideal for logging or progress info that might be redirected (e.g., to files, transcripts). Use with [CmdletBinding] for common parameters. When to use each: - Use Write-Host sparingly: Only for pure UI elements in interactive tools (e.g., Show- or Format- verbs, prompts). Jeffrey Snover called it "almost always wrong" for automation. - Use Write-Information (or Write-Verbose for details): For user feedback in scripts/functions that may run non-interactively, allowing users to control visibility (-InformationAction Continue/SilentlyContinue). - For actual data: Write-Output (success stream). - Avoid mixing in functions; output one type of object. Examples: # Interactive prompt (Write-Host OK) Write-Host "Enter name: " -ForegroundColor Yellow $name = Read-Host # Script info (prefer Write-Information) Write-Information "Processing server $server" -InformationAction Continue # Or set $InformationPreference = 'Continue' # Suppress: script.ps1 -InformationAction SilentlyContinue This aligns with PowerShell's stream model (about_Output_Streams) for automation.
Citations:
- 1: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/write-information?view=powershell-7.4
- 2: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/write-information?view=powershell-7.5
- 3: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/write-host?view=powershell-7.4
- 4: https://www.jsnover.com/blog/2013/12/07/write-host-considered-harmful/
- 5: https://poshcode.gitbook.io/powershell-practice-and-style/best-practices/output-and-formatting
- 6: https://stackoverflow.com/questions/38523369/write-host-vs-write-information-in-powershell-5
Replace Write-Host with Write-Information for stream-aware logging.
PSScriptAnalyzer correctly flags Write-Host as bypassing output streams, making it less suitable for automation and redirection. Use Write-Information with -InformationAction Continue to maintain visibility while enabling proper stream control.
Applies to lines 3, 6, 12, 18, and 24.
Suggested replacement pattern
-Write-Host "Checking out branch..."
+Write-Information "Checking out branch..." -InformationAction Continue
-Write-Host "Running Backend Checks..."
+Write-Information "Running Backend Checks..." -InformationAction Continue
-Write-Host "Running Frontend Checks..."
+Write-Information "Running Frontend Checks..." -InformationAction Continue
-Write-Host "Committing and Pushing..."
+Write-Information "Committing and Pushing..." -InformationAction Continue
-Write-Host "All done! Ready for presentation."
+Write-Information "All done! Ready for presentation." -InformationAction ContinueAlternatively, set $InformationPreference = 'Continue' at the script start and omit -InformationAction from each call.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| Write-Host "Checking out branch..." | |
| Write-Information "Checking out branch..." -InformationAction Continue |
🧰 Tools
🪛 PSScriptAnalyzer (1.24.0)
[warning] 3-3: File 'precommit.ps1' uses Write-Host. Avoid using Write-Host because it might not work in all hosts, does not work when there is no host, and (prior to PS 5.0) cannot be suppressed, captured, or redirected. Instead, use Write-Output, Write-Verbose, or Write-Information.
(PSAvoidUsingWriteHost)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@precommit.ps1` at line 3, Replace all uses of Write-Host in the script with
Write-Information (e.g., change Write-Host "Checking out branch..." to
Write-Information "Checking out branch..." -InformationAction Continue) so
output uses the PowerShell information stream; alternatively, set
$InformationPreference = 'Continue' at the top of the script and remove the
per-call -InformationAction flags to keep behavior consistent across the
Write-Information calls that currently replace Write-Host.
| git checkout -b feat/presentation-ready-b2c | ||
|
|
||
| Write-Host "Running Backend Checks..." | ||
| Set-Location -Path "apps/backend" |
There was a problem hiding this comment.
Anchor directory changes to script location to avoid path breakage.
Line 7, Line 13, and Line 19 assume the script always starts from repo root. If invoked from another working directory, commands can run in the wrong place or fail.
Suggested hardening diff
$ErrorActionPreference = "Stop"
+$repoRoot = $PSScriptRoot
+Set-Location -Path $repoRoot
Write-Host "Checking out branch..."
git checkout -b feat/presentation-ready-b2c
Write-Host "Running Backend Checks..."
-Set-Location -Path "apps/backend"
+Push-Location -Path (Join-Path $repoRoot "apps/backend")
pnpm run lint
npx tsc --noEmit
pnpm run build
Write-Host "Running Frontend Checks..."
-Set-Location -Path "../../apps/web"
+Pop-Location
+Push-Location -Path (Join-Path $repoRoot "apps/web")
pnpm run lint
npx tsc --noEmit
pnpm run build
Write-Host "Committing and Pushing..."
-Set-Location -Path "../.."
+Pop-Location
+Set-Location -Path $repoRoot
git add .
git commit -m "feat: complete b2c marketplace transition and generalization"
git push origin feat/presentation-ready-b2cAlso applies to: 13-13, 19-19
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@precommit.ps1` at line 7, The script uses Set-Location with hardcoded
relative paths which breaks if invoked from a different working directory;
update precommit.ps1 to first change to the script's directory (use
$PSScriptRoot or Split-Path -Parent $MyInvocation.MyCommand.Path) and then call
the existing Set-Location "apps/backend" (and the other two occurrences
referenced) so all three Set-Location calls are anchored off the script location
rather than the current working directory; modify the logic around the
Set-Location usages in precommit.ps1 to compute and cd to the script folder
before executing those relative Set-Location calls.
|
|
||
| Write-Host "Committing and Pushing..." | ||
| Set-Location -Path "../.." | ||
| git add . |
There was a problem hiding this comment.
Avoid git add . in automation scripts with push steps.
Line 20 stages everything in the repository, which can accidentally commit unrelated local files (including sensitive artifacts if ignore rules are incomplete). This is a high-risk default for scripted commits.
Safer staging pattern
-git add .
+git add apps/backend apps/web precommit.ps1📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| git add . | |
| git add apps/backend apps/web precommit.ps1 |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@precommit.ps1` at line 20, The script currently runs the unsafe command "git
add ."; remove that and instead stage only intended files by computing the set
to commit and calling "git add -- <paths>" (or, if you only want tracked
changes, use "git add -u"), ensure you skip/abort when the computed list is
empty, and use explicit paths passed in (e.g., from an env var or git diff
output) so the script never blindly stages all untracked files.
Summary by CodeRabbit
Release Notes
New Features
UI/UX Changes
Bug Fixes