Skip to content

Fixed end to end custormer journey#22

Merged
AutomatosAI merged 4 commits intomainfrom
codex/redesign-core-product-pages-for-saas
Jan 17, 2026
Merged

Fixed end to end custormer journey#22
AutomatosAI merged 4 commits intomainfrom
codex/redesign-core-product-pages-for-saas

Conversation

@AutomatosAI
Copy link
Copy Markdown
Owner

@AutomatosAI AutomatosAI commented Jan 17, 2026

Summary by CodeRabbit

  • New Features

    • Added KYC verification status checking for account authentication.
    • Introduced dynamic multi-currency support across products and cart.
    • Implemented shipping address auto-population in checkout.
    • Added product image galleries with discount badges and expiry date display.
    • Enhanced order submission with verification requirements.
  • Bug Fixes

    • Fixed authentication checks to use current authentication state.
  • Improvements

    • Improved order summary and history displays with proper currency formatting.
    • Enhanced cart management with per-item currency tracking.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 17, 2026

📝 Walkthrough

Walkthrough

This PR introduces a comprehensive multi-tenant KYC (Know Your Customer) system integrated with Clerk authentication and Dr. Green API. It adds server actions for user verification, consultation submission with tenant-aware data access, order validation, enhanced product details with currency and discount support, and refactored cart/checkout flows with preloaded shipping addresses.

Changes

Cohort / File(s) Summary
KYC & Authentication Server Actions
nextjs_space/app/actions/kyc-check.ts, nextjs_space/app/actions/get-user-shipping.ts
New server actions for KYC status checking (multi-step verification flow with Clerk, DB, Dr. Green API) and shipping address retrieval with tenant-scoped queries and fallback logic.
Consultation & Order API Routes
nextjs_space/app/api/consultation/submit/route.ts, nextjs_space/app/api/store/[slug]/orders/submit/route.ts, nextjs_space/app/api/shop/register/route.ts
Consultation submission with Clerk user creation, local DB mirroring, and Dr. Green integration with clientId persistence. Order submission now enforces KYC verification (local or external). Registration extended with phone parsing, shipping object, and medical record restructuring.
Cart State Management
nextjs_space/lib/cart-store.ts, nextjs_space/lib/drgreen-cart.ts, nextjs_space/components/cart-dropdown.tsx
Cart store enhanced with optional currency field; drgreen-cart operations refactored to use drgreen_carts table with UUID generation and updatedAt tracking; dropdown displays per-item currency.
Order Management
nextjs_space/lib/drgreen-orders.ts
Order submission now includes local KYC verification override, per-item IDs, and conditional external Dr. Green API calls; maintains drgreen_carts cleanup with new table naming.
Dr. Green API Integration
nextjs_space/lib/doctor-green-api.ts, nextjs_space/lib/tenant-config.ts, nextjs_space/lib/drgreen-api-client.ts
DoctorGreenConfig extended with optional apiUrl for per-tenant API endpoint overrides; DoctorGreenProduct interface enhanced with prices array, expiryDate, discount, and strainImages; new fetchClient() function added; tenant-config retrieves and returns apiUrl with fallback to platform config.
Checkout & Cart Pages
nextjs_space/app/store/[slug]/checkout/page.tsx, nextjs_space/app/store/[slug]/cart/page.tsx, nextjs_space/app/store/[slug]/layout.tsx
Checkout refactored to use cart store (useCartStore) with address preloading via getUserShippingAddress; both pages now display dynamic currency per item instead of hardcoded €; layout wrapped in CartProvider.
Product & Dashboard Pages
nextjs_space/app/store/[slug]/products/[id]/page.tsx, nextjs_space/app/store/[slug]/products/page.tsx, nextjs_space/app/store/[slug]/dashboard/page.tsx
Product detail page adds KYC gating, discount badges, multiple images (strainImages), expiry date display, and currency per item; product list adds dynamic currency; dashboard fetches and displays dynamic KYC status with verified/pending states.
Order Detail Page
nextjs_space/app/store/[slug]/orders/[orderId]/page.tsx
Dynamic currency display for all pricing (subtotal, shipping, total) with fallback to default currency.
Tenant & Consultation Pages
nextjs_space/app/store/[slug]/consultation/page.tsx
Tenant validation switched from server-side API fetch to direct DB helper (getTenantBySlug).
Configuration & Middleware
nextjs_space/next.config.js, nextjs_space/middleware.ts, nextjs_space/tsconfig.json
Image domains and remotePatterns extended for S3 and Clerk resources; public routes now include /api/consultation(.*); tsconfig exclude expanded to cover entire scripts directory and prisma files.
Authentication Template
nextjs_space/templates/healingbuds/components/Navigation.tsx
Condition switched from status === 'authenticated' to isSignedIn for Clerk integration.
Debug & Utility Scripts
nextjs_space/prisma/check-tenant.ts, nextjs_space/scripts/debug-*.ts (7 files), nextjs_space/scripts/test-drgreen-*.ts (2 files), nextjs_space/scripts/find-product.ts, nextjs_space/scripts/populate-cart.ts, nextjs_space/scripts/fix-kyc-email.ts, nextjs_space/scripts/revert-tenant-country.ts
New debugging/diagnostic scripts for KYC inspection, Dr. Green API testing, cart inspection, tenant lookup, and data corrections; no public API changes.
Ignore & Metadata
nextjs_space/.gitignore
Added patterns for debug output files (debug\.txt, debug-\.txt, \\/debug-output.txt, \\/debug-deep-output.txt).

Sequence Diagrams

sequenceDiagram
    participant User as Client / Browser
    participant App as Next.js App
    participant Clerk as Clerk Auth
    participant DB as Database
    participant DrGreen as Dr. Green API

    User->>App: Submits consultation form
    App->>Clerk: Create/verify user
    Clerk-->>App: Clerk user ID
    App->>DB: Create local user (Clerk ID)
    DB-->>App: Local user created
    App->>DB: Create consultation_questionnaires entry
    DB-->>App: Questionnaire stored
    App->>DrGreen: Submit questionnaire payload (signed)
    DrGreen-->>App: clientId + kycLink
    App->>DB: Update questionnaire with clientId
    App->>DB: Update user with drGreenClientId & tenantId
    DB-->>App: Record updated
    App-->>User: Success (or queued for retry)
Loading
sequenceDiagram
    participant User as Client / Browser
    participant App as Next.js App
    participant DB as Database
    participant DrGreen as Dr. Green API

    User->>App: Request order submission
    App->>DB: Query consultation_questionnaires (local KYC check)
    alt Local verification found
        DB-->>App: Verified questionnaire
        App->>App: Use local verification
    else No local verification
        App->>DB: Check user.drGreenClientId
        DB-->>App: drGreenClientId
        App->>DrGreen: Fetch client status
        DrGreen-->>App: Client status (ACTIVE & verified)
    end
    App->>DB: Create order + order_items
    DB-->>App: Order persisted
    alt Has drGreenCartId & no local override
        App->>DrGreen: Delete external cart
    end
    App-->>User: Order confirmation
Loading
sequenceDiagram
    participant User as Client / Browser
    participant App as Next.js App
    participant DB as Database
    participant Clerk as Clerk Auth
    participant DrGreen as Dr. Green API

    User->>App: Load dashboard/products
    App->>Clerk: Get current user
    Clerk-->>App: Clerk user or null
    App->>DB: Lookup user by Clerk ID (fallback: email)
    DB-->>App: DB user (or null)
    alt User found
        App->>DB: Query consultation_questionnaires (verified)
        DB-->>App: Questionnaire or null
        alt Questionnaire exists
            App->>App: Return ACTIVE + kycVerified=true
        else No verified questionnaire
            App->>DB: Check drGreenClientId
            alt drGreenClientId exists
                App->>DrGreen: Fetch client details
                DrGreen-->>App: Client status & verified flag
                App->>App: Return status based on client
            else No drGreenClientId
                App->>App: Return PENDING or NO_ID
            end
        end
    else User not found
        App->>App: Return GUEST
    end
    App-->>User: KYC status + UI state
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

codex

Poem

🐰 Hops through the tenant-aware garden,
Where KYC blooms and Dr. Green's beckoning,
Carts now carry currency, orders know their way,
Clerk guards the door, Prisma keeps the day,
Consultation questionnaires spring to life—
No more euro walls, just seamless strife!

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.66% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Fixed end to end custormer journey' is vague and does not clearly convey the scope of changes. While it hints at workflow improvements, it lacks specificity about what was fixed and which components were involved. Revise the title to be more specific about the primary changes, such as 'Add KYC verification flow and shipping address management' or 'Implement end-to-end order verification and checkout improvements'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 18

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
nextjs_space/app/api/consultation/submit/route.ts (1)

317-336: Remove or gate sensitive payload logging.

This logs PII and medical data (HIPAA/PII risk) on every submission. Please remove or strictly gate behind a debug flag with redaction.

🧹 Proposed fix (dev-only, redacted)
-      console.log("\n=== DR GREEN API DEBUG ===");
-      console.log("API URL:", `${drGreenApiUrl}/dapp/clients`);
-      console.log("Has API Key:", !!apiKey);
-      console.log("Has Secret Key:", !!secretKey);
-      console.log("\n=== PHONE FIELDS DEBUG ===");
-      console.log("Raw phoneCode from form:", JSON.stringify(body.phoneCode));
-      console.log(
-        "Raw phoneNumber from form:",
-        JSON.stringify(body.phoneNumber),
-      );
-      console.log(
-        "Cleaned phoneCode:",
-        JSON.stringify(body.phoneCode.replace(/[^\+\d]/g, "")),
-      );
-      console.log(
-        "Cleaned contactNumber:",
-        JSON.stringify(body.phoneNumber.replace(/\D/g, "")),
-      );
-      console.log("\n=== PAYLOAD ===");
-      console.log(JSON.stringify(drGreenPayload, null, 2));
-      console.log("\n===================\n");
+      if (process.env.NODE_ENV === "development") {
+        console.log("\n=== DR GREEN API DEBUG ===");
+        console.log("API URL:", `${drGreenApiUrl}/dapp/clients`);
+        console.log("Has API Key:", !!apiKey);
+        console.log("Has Secret Key:", !!secretKey);
+        console.log("\n=== PHONE FIELDS DEBUG (redacted) ===");
+        console.log("Has phoneCode:", !!body.phoneCode);
+        console.log("Has phoneNumber:", !!body.phoneNumber);
+        console.log("\n===================\n");
+      }
nextjs_space/app/store/[slug]/cart/page.tsx (1)

197-215: Guard against mixed-currency carts in totals.

Lines 197 and 215 derive subtotal/total currency from items[0]. Because the cart store isn’t tenant-scoped, a user can end up with items from multiple stores/currencies, which makes totals and labels incorrect. Consider enforcing single-currency carts (e.g., clear cart on store change) or grouping totals per currency.

nextjs_space/app/store/[slug]/checkout/page.tsx (1)

242-274: Handle mixed-currency carts and align fallback currency.

Lines 242–274 assume a single currency (using items[0]?.currency) and default to '$'. If the cart contains items from another store/currency, totals and labels become incorrect; also, the fallback differs from the cart page (“€”).

Consider enforcing a single currency per cart or grouping totals by currency, and use a consistent default (preferably derived from tenant config).

nextjs_space/lib/doctor-green-api.ts (1)

252-257: Image base should respect tenant apiUrl

Now that per‑tenant API URLs exist, image normalization should use config.apiUrl (when present) rather than the global API_URL, or images will break for tenants using custom endpoints.

🛠️ Suggested fix to respect tenant apiUrl for image base
-  const apiBase = API_URL.replace(/\/api\/v1\/?$/, '');
+  const apiBase = (config?.apiUrl || API_URL).replace(/\/api\/v1\/?$/, '');
-  const apiBase = API_URL.replace(/\/api\/v1\/?$/, '');
+  const apiBase = (config?.apiUrl || API_URL).replace(/\/api\/v1\/?$/, '');

Also applies to: 329-332

nextjs_space/app/store/[slug]/products/[id]/page.tsx (1)

239-246: Guard Next/Image against empty src values

Next.js Image component throws an error when src="" is passed. Currently, if product.strainImages exists but contains items without strainImageUrl, empty strings will be passed to the Image component at lines 300 and 366. Filter strainImages to only valid images and ensure fallback URLs exist before rendering.

🖼️ Safer gallery handling
  const imageUrl = product.image_url || product.imageUrl;
+  const galleryImages =
+    (product.strainImages ?? []).filter((img) => img.strainImageUrl);
-              {((product.strainImages && product.strainImages.length > 0) || imageUrl) ? (
+              {(galleryImages.length > 0 || imageUrl) ? (
                 <div className="absolute inset-0 p-8 flex items-center justify-center">
                   <div className="relative w-full h-full transition-transform duration-500 hover:scale-110">
                     <Image
                       src={
-                        (product.strainImages && product.strainImages.length > 0)
-                          ? (product.strainImages[selectedImage]?.strainImageUrl || "")
-                          : (imageUrl || "")
+                        galleryImages.length > 0
+                          ? (galleryImages[selectedImage]?.strainImageUrl || imageUrl || "")
+                          : (imageUrl || "")
                       }
                       alt={product.name}
                       fill
                       className="object-contain drop-shadow-2xl"
                       sizes="(max-width: 768px) 100vw, 50vw"
                       priority
                     />
                   </div>
                 </div>
               ) : (
-            {product.strainImages && product.strainImages.length > 1 && (
+            {galleryImages.length > 1 && (
               <div className="flex gap-4 overflow-x-auto pb-2">
-                {product.strainImages.map((img, idx) => (
+                {galleryImages.map((img, idx) => (
                   <button
                     key={idx}
                     onClick={() => setSelectedImage(idx)}
                     className={`relative w-24 h-24 rounded-lg overflow-hidden border-2 transition-all flex-shrink-0 ${selectedImage === idx ? "border-primary" : "border-transparent opacity-70 hover:opacity-100"
                       }`}
                     style={{
                       borderColor: selectedImage === idx ? "var(--tenant-color-primary, `#059669`)" : "transparent"
                     }}
                   >
                     <Image
-                      src={img.strainImageUrl || ""}
+                      src={img.strainImageUrl!}
                       alt={img.altText || `View ${idx + 1}`}
                       fill
                       className="object-cover"
                     />
                   </button>
                 ))}
               </div>
             )}
🤖 Fix all issues with AI agents
In `@nextjs_space/app/actions/get-user-shipping.ts`:
- Around line 46-53: The questionnaire lookup in get-user-shipping.ts uses
prisma.consultation_questionnaires.findFirst filtering only by email which can
cross tenants; update the query in get-user-shipping (the
prisma.consultation_questionnaires.findFirst call) to also filter by tenantId
(use dbUser.tenantId or request tenant context like in kyc-check.ts) and add a
guard to return/throw if tenantId or email is missing before querying; mirror
the tenant-scoping pattern used in kyc-check.ts to ensure only the current
tenant's questionnaire is returned.

In `@nextjs_space/app/actions/kyc-check.ts`:
- Around line 36-52: The questionnaire lookup can match across tenants because
tenantId is undefined when dbUser is null; change the logic in
nextjs_space/app/actions/kyc-check.ts so before calling
prisma.consultation_questionnaires.findFirst you compute a concrete tenantId
(e.g., const tenantId = dbUser?.tenantId ?? <authenticated user's tenant id>)
and verify an email exists (guard clerkUser.email), then pass where: { tenantId,
email: { equals: clerkUser.email, mode: 'insensitive' } } to
prisma.consultation_questionnaires.findFirst; ensure you bail/return
appropriately if tenantId or email is missing to avoid cross-tenant leaks.

In `@nextjs_space/app/api/store/`[slug]/orders/submit/route.ts:
- Around line 63-72: The local KYC override query
(prisma.consultation_questionnaires.findFirst) only filters by email and
isKycVerified, allowing a verified questionnaire from another tenant to be used;
update the query for localQuestionnaire to include tenantId: tenant.id in the
where/AND filters so the lookup is scoped to the current tenant (use the
existing localQuestionnaire variable and prisma.consultation_questionnaires call
and add tenant.id to the AND conditions).

In `@nextjs_space/app/store/`[slug]/orders/[orderId]/page.tsx:
- Around line 130-145: The UI currently hardcodes "ZAR" for line-item, subtotal,
shipping and total labels; replace those hardcoded strings with a dynamic
currency value (use order.currency if present, otherwise derive from an item
like items[0]?.currency or item.currency inside the loop) so the displayed
currency matches the order data; update the spans rendering item price, subtotal
(order.subtotal), shippingCost (order.shippingCost) and total (order.total) to
prepend the resolved currency variable instead of "ZAR" (see variables order,
item and the cart-dropdown.tsx pattern items[0]?.currency for reference).

In `@nextjs_space/app/store/`[slug]/products/page.tsx:
- Line 343: The product listing renders product.currency directly which can be
undefined for legacy products; update the render logic around product.currency
(the expression that currently outputs "{product.currency}
{product.price.toFixed(2)}") to use a fallback such as tenantCurrency or a
hardcoded "€" when product.currency is falsy (e.g., use product.currency ||
tenantCurrency || '€'), ensuring the rest of the display
(product.price.toFixed(2)) remains unchanged.

In `@nextjs_space/debug-deep-output.txt`:
- Around line 7-28: The debug dump file nextjs_space/debug-deep-output.txt
contains PHI/PII (emails, DOBs, addresses, phone numbers, tenant IDs, password
hashes, medicalConditions) and must be removed and replaced with anonymized
fixtures: delete the sensitive file from the commit, replace it with a synthetic
fixture containing non-identifiable example rows for the
consultation_questionnaires schema, redact all fields (email, dateOfBirth,
phoneCode/phoneNumber, address*, tenantId, password, medicalConditions,
drGreenClientId, submissionError) in the fixture, and then purge the sensitive
file from the repository history (use a history-rewrite tool) so no sensitive
data remains in commits.
- Around line 1-6: The file nextjs_space/debug-deep-output.txt contains
committed PII (emails like gerard161+buds@gmail.com, password/resetToken fields,
addresses, phone numbers) — remove the sensitive data by deleting the file from
the repo history or replacing it with a redacted placeholder that omits/masks
all PII (mask email, remove password/resetToken, phone/address lines) and keep
only non-sensitive debug metadata; add a rule to .gitignore to prevent future
commits of debug-deep-output.txt (or a broader pattern like nextjs_space/*.txt
or debug*.txt); if the file was already committed, run git rm --cached
nextjs_space/debug-deep-output.txt and commit the removal (or perform a history
rewrite if required) and, if any secrets/credentials were exposed, rotate them
immediately.

In `@nextjs_space/debug-output.txt`:
- Around line 1-9: The committed debug output contains PII (email, user ID, SQL
traces) in nextjs_space/debug-output.txt; remove or redact it, add
nextjs_space/debug-output.txt (or its folder) to .gitignore, replace the file
with a sanitized placeholder if you must keep a sample, and purge the sensitive
data from history (use a git history-rewrite tool to remove the file from prior
commits and force-push). Also search the repo for other debug logs containing
emails/IDs and remove or redact them before pushing.

In `@nextjs_space/lib/drgreen-orders.ts`:
- Around line 77-101: The guard that throws when user.drGreenClientId is missing
prevents the local override path from running; update the logic around
localQuestionnaire and drGreenClientId so the missing-clientId check only
applies to the API call path: relocate or change the existing throw to live
inside the else branch that calls callDrGreenAPI (or add a conditional that only
throws if !localQuestionnaire), ensuring localQuestionnaire still constructs the
mock orderData (id/invoiceNumber/status/total) without requiring
drGreenClientId.
- Around line 66-75: The local KYC lookup using
prisma.consultation_questionnaires.findFirst (assigned to localQuestionnaire)
currently filters only by email and can leak across tenants; update the where
clause to also filter by tenantId (or the user's tenant linkage, e.g.,
user.tenantId) so the query becomes constrained by both email (case-insensitive)
and tenantId, ensuring the local verification is scoped to the same tenant as
the current user.

In `@nextjs_space/scripts/debug-kyc-auth.ts`:
- Line 9: Remove the hardcoded constant KEY and replace it with reading a secure
environment variable (e.g., process.env.KYC_ENCRYPTION_KEY) wherever KEY is used
(search for the constant KEY and any decrypt/encrypt calls in this file and
related helpers); update initialization in debug-kyc-auth.ts so it throws a
clear error if the env var is missing, and ensure all functions referencing KEY
(decryptTenantCredentials, decrypt, etc.) use the env var value instead; after
code change, remove the committed literal from history and coordinate a
production key rotation and update of the KYC_ENCRYPTION_KEY environment
variable in deployment configuration.

In `@nextjs_space/scripts/debug-kyc-deep.ts`:
- Around line 5-8: The script currently hard-codes sensitive PII (e.g., the
constants email and clerkId and other similar values later in the file) and logs
full records; change these to accept inputs via environment variables or CLI
arguments (process.env.DEBUG_KYC_EMAIL / DEBUG_KYC_CLERK_ID or argv), validate
they exist before use, and remove hard-coded defaults; when logging, never print
full records—log only minimal, non-sensitive fields or a redacted form (e.g.,
show first/last 3 chars or hashes) and ensure any debug output explicitly
redacts or truncates clerkId/email; finally update README or script help to
document the new env/CLI flags and ensure no PII remains in committed scripts.
- Around line 49-55: The catch block currently calls process.exit(1) which can
terminate the process before the finally block runs and skip cleanup; change the
catch to set process.exitCode = 1 instead of calling process.exit(1) so that the
finally block (where prisma.$disconnect() is awaited) always executes; locate
the main() invocation and its .catch handler and replace the hard exit with
setting exitCode while preserving the existing console.error(e) call.

In `@nextjs_space/scripts/debug-kyc-status.ts`:
- Around line 5-7: Replace the hard-coded email in debug-kyc-status.ts (the
email constant used for logging) with a parameterized value read from
process.env (e.g., process.env.DEBUG_EMAIL) or from command-line args
(process.argv) and if neither is provided log a clear error and exit with
non-zero status (use console.error and process.exit(1)) so the script fails fast
when the email is missing.

In `@nextjs_space/scripts/populate-cart.ts`:
- Around line 4-13: In main(), remove the hard-coded PII in the email variable
and instead read the user identifier from a secure source (e.g.,
process.env.POPULATE_CART_EMAIL or a CLI arg parsed in main), validate it exists
and if missing fail fast with a clear error message before calling
prisma.users.findUnique; update references to the existing email constant so the
script is reusable and safe (refer to the email variable and main() function).

In `@nextjs_space/scripts/revert-tenant-country.ts`:
- Around line 17-19: The current promise chain uses main().catch(console.error)
which only logs errors and leaves the process exit code zero; update the catch
to capture the thrown error from main, log it (using console.error or a logger)
and then call process.exit(1) to set a non‑zero exit code on failure (ensure
prisma.$disconnect() still runs in finally or await disconnect before exiting);
locate this logic around the main() invocation and prisma.$disconnect() call and
modify the catch handler to log the error and invoke process.exit(1).

In `@nextjs_space/scripts/test-drgreen-auth.ts`:
- Around line 6-46: The file contains hard-coded secrets (KEY, encryptedApiKey,
encryptedSecretKey) used by getKeyId(), decryptText(), and generateSignature();
remove these literals and load them from environment variables instead (e.g.,
process.env.DRGREEN_KEY, process.env.DRGREEN_ENCRYPTED_API_KEY,
process.env.DRGREEN_ENCRYPTED_SECRET_KEY), update getKeyId() to derive the key
from the env var, change any callers of decryptText() and generateSignature() to
pass or read the env values, add clear validation and error messages when the
env vars are missing, delete the hard-coded strings from the source, ensure
.env.example documents the new variables, and rotate/revoke the embedded
credentials in secrets management/out-of-band after deployment.

In `@nextjs_space/scripts/test-drgreen-products.ts`:
- Around line 5-28: The code currently hardcodes KEY, encryptedApiKey, and
encryptedSecretKey and uses getKeyId()/decryptText() to derive and decrypt them;
rotate the exposed API credentials immediately, remove KEY and the encrypted
payload constants from the source, and load the decryption key and ciphertexts
from secure environment variables or a secrets manager (e.g.,
process.env.DECRYPTION_KEY, process.env.ENCRYPTED_API_KEY,
process.env.ENCRYPTED_SECRET_KEY) instead; update getKeyId() and decryptText()
to read those env vars, add runtime guards that throw a clear error if required
env vars are missing, and scrub the old values from git history (and add a note
to rotate any leaked credentials).
🧹 Nitpick comments (14)
nextjs_space/scripts/revert-tenant-country.ts (2)

2-3: Align module import style with TS conventions (or confirm CJS is intended).

This is a .ts script but uses require. If the repo is ESM/TS-first, consider import { PrismaClient } from "@prisma/client"; for consistency and type tooling.


5-12: Add a safety guard + parameterize the tenant ID to avoid accidental prod edits.

This script performs a direct mutation with a hard-coded tenant ID. Consider requiring an explicit CLI arg and a confirmation env var to avoid unintended execution against the wrong DB.

nextjs_space/templates/healingbuds/components/Navigation.tsx (1)

252-309: Guard auth UI on isLoaded to avoid brief unauthenticated render.
isSignedIn is false until Clerk finishes loading, so both desktop and mobile menus can briefly show the signed‑out UI before flipping. Consider gating on isLoaded to avoid flicker.

♻️ Suggested adjustment
-                {isSignedIn ? (
+                {isLoaded ? (
+                  isSignedIn ? (
                     <DropdownMenu>
                       ...
                     </DropdownMenu>
-                ) : (
+                  ) : (
                     <Link
                       href={`/store/${tenant.subdomain}/login`}
                       ...
                     >
                       <User className="w-6 h-6" />
                     </Link>
-                )}
+                  )
+                ) : null}
-                {isSignedIn ? (
+                {isLoaded ? (
+                  isSignedIn ? (
                   <>
                     ...
                   </>
-                ) : (
+                  ) : (
                   <Link
                     href={`/store/${tenant.subdomain}/login`}
                     ...
                   >
                     Patient Login
                   </Link>
-                )}
+                  )
+                ) : null}

Also applies to: 399-432

nextjs_space/scripts/test-drgreen-products.ts (2)

37-51: Replace the placeholder signature with a real one (or omit it).

Line 49–50 uses a dummy signature and the decrypted secretKey is otherwise unused, so this script may not validate the real auth flow. Please confirm the Dr. Green API signature requirements and generate the signature from secretKey if required.


54-56: Guard against missing fetch in non‑Node‑18 runtimes.

Line 55 will throw on Node versions without global fetch. Add a guard or explicitly provide a fetch implementation.

✅ Minimal guard
 async function main() {
+    if (typeof fetch !== "function") {
+        throw new Error("Global fetch is unavailable. Run with Node 18+ or provide a fetch polyfill.");
+    }
     const apiKey = decryptText(encryptedApiKey);
nextjs_space/lib/cart-store.ts (1)

75-80: Consider: getTotalPrice() doesn't handle mixed currencies.

If items with different currencies can coexist in the cart, summing their prices produces a meaningless total. The UI currently uses the first item's currency for display, which masks this issue.

If multi-currency carts are allowed, consider either:

  1. Validating that all items share the same currency when adding items
  2. Converting to a common currency before summing
  3. Returning totals grouped by currency

If single-currency carts are enforced elsewhere, this is fine as-is.

nextjs_space/prisma/check-tenant.ts (1)

1-27: Debug script looks fine for its purpose.

This utility script properly handles Prisma lifecycle with $disconnect in the finally block. The hardcoded subdomain and standalone PrismaClient are acceptable for a diagnostic tool.

Consider adding a comment at the top indicating this is a debug/diagnostic script, or moving it to a clearly named debug directory to prevent accidental use in production workflows.

nextjs_space/scripts/debug-cart.ts (1)

4-31: Debug script looks functional, but consider adding a development-only guard.

This script logs potentially sensitive cart data (line 25: full JSON). Since debug scripts can accidentally be run against production databases, consider adding an environment check at the start:

if (process.env.NODE_ENV === 'production') {
  console.error('This script should not be run in production');
  process.exit(1);
}
nextjs_space/scripts/debug-kyc-auth.ts (1)

2-6: Inconsistent module system and PrismaClient instantiation.

This script uses CommonJS require() while other debug scripts (e.g., debug-cart.ts) use ES imports. It also creates a new PrismaClient instead of using the shared instance.

For consistency with other scripts in this directory:

♻️ Suggested refactor
-const cryptoModule = require("crypto");
-const { PrismaClient } = require("@prisma/client");
-const https = require("https"); // or fetch if node 18+
-
-const prisma = new PrismaClient();
+import crypto from "crypto";
+import { prisma } from "../lib/db";
nextjs_space/scripts/fix-kyc-email.ts (1)

10-15: Consider handling the case where the record doesn't exist.

If the questionnaire with targetId doesn't exist, Prisma will throw a RecordNotFoundError. For a one-time fix script, you may want to handle this gracefully:

♻️ Optional improvement
-    const updated = await prisma.consultation_questionnaires.update({
-        where: { id: targetId },
-        data: { email: newEmail }
-    });
-
-    console.log("Updated record:", updated.email);
+    const existing = await prisma.consultation_questionnaires.findUnique({
+        where: { id: targetId }
+    });
+    
+    if (!existing) {
+        console.error(`Questionnaire ${targetId} not found`);
+        process.exit(1);
+    }
+    
+    const updated = await prisma.consultation_questionnaires.update({
+        where: { id: targetId },
+        data: { email: newEmail }
+    });
+
+    console.log("Updated record:", updated.email);
nextjs_space/next.config.js (1)

19-19: LGTM! Image domain configuration is correct.

The additions for prod-profiles-backend.s3.amazonaws.com and img.clerk.com are properly configured in both domains and remotePatterns. These are legitimate external image sources for S3-hosted assets and Clerk user avatars.

Note: images.domains is deprecated in Next.js 14+ in favor of remotePatterns alone. Consider removing the domains array in a future cleanup to use only remotePatterns.

Also applies to: 39-50

nextjs_space/components/cart-dropdown.tsx (1)

118-122: Guard total currency when cart may contain mixed currencies.

The total uses items[0]?.currency, which can be misleading if mixed currencies are allowed. Consider enforcing a single cart currency or presenting a mixed-currency total explicitly.

nextjs_space/app/store/[slug]/dashboard/page.tsx (1)

64-92: Consider a loading state while KYC status is unresolved.

kycStatus starts as null, so the UI immediately shows “Pending Verification” (Lines 64–92 and 246–248) before the async check finishes. A brief “Checking verification…” state would avoid a misleading flash.

Also applies to: 246-248

nextjs_space/lib/doctor-green-api.ts (1)

283-294: Gate debug logging behind an env flag

The per‑product console.log will be noisy in production; consider guarding it with an env flag or your logger’s debug level.

♻️ Example guard
-    console.log(`[DrGreen Debug] ID: ${product.id} | Default: ${defaultCurrency} | Selected: ${currency} ${price}`);
+    if (process.env.NODE_ENV === "development") {
+      console.log(
+        `[DrGreen Debug] ID: ${product.id} | Default: ${defaultCurrency} | Selected: ${currency} ${price}`,
+      );
+    }

Comment thread nextjs_space/app/actions/get-user-shipping.ts
Comment thread nextjs_space/app/actions/kyc-check.ts
Comment thread nextjs_space/app/api/store/[slug]/orders/submit/route.ts
Comment thread nextjs_space/app/store/[slug]/orders/[orderId]/page.tsx Outdated
Comment thread nextjs_space/app/store/[slug]/products/page.tsx Outdated
Comment thread nextjs_space/scripts/debug-kyc-status.ts Outdated
Comment thread nextjs_space/scripts/populate-cart.ts
Comment thread nextjs_space/scripts/revert-tenant-country.ts
Comment thread nextjs_space/scripts/test-drgreen-auth.ts Outdated
Comment thread nextjs_space/scripts/test-drgreen-products.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
nextjs_space/lib/drgreen-orders.ts (1)

140-152: Fix include in getOrder to use correct relation name order_items.

Line 206-208 includes items: true, but the Prisma schema defines the relation as order_items order_items[]. This causes the order items to be omitted from the result, resulting in order.items being undefined.

        include: {
-           items: true,
+           order_items: true,
        },

(The creation code at lines 140-152 correctly uses order_items and matches the schema.)

🤖 Fix all issues with AI agents
In `@nextjs_space/app/api/shop/register/route.ts`:
- Around line 85-95: In the registration route where you build the payload's
medicalRecord object, the spread ...medicalRecord is placed after your explicit
mappings (dob, gender, medicalConditions, medicinesTreatments) which allows
incoming fields to overwrite them; fix this by moving the spread to the
beginning of the object literal so your explicit mappings override any values
from the incoming medicalRecord (i.e., construct medicalRecord as {
...medicalRecord, dob: personal.dateOfBirth, gender: "Not Specified",
medicalConditions: medicalRecord.conditions || [], medicinesTreatments:
medicalRecord.currentMedications || [] } and keep the existing as any cast).
- Around line 62-66: The phone parsing in route.ts (variables phone, phoneCode,
contactNumber) incorrectly assumes a fixed two‑digit country code by using
phone.slice(0, 3); replace this with a robust approach: either make ZA-only
intent explicit via a clear comment and enforce/validate ZA formats, or
integrate a library like libphonenumber-js (e.g., use parsePhoneNumber with
default region 'ZA') to derive countryCallingCode and nationalNumber, then set
phoneCode to `+${countryCallingCode}` and contactNumber to the parsed national
number; update error handling/validation for empty or invalid numbers
accordingly.
- Around line 74-82: Replace hardcoded "ZA" values with the tenant's country
code and avoid overriding parsed phone info: use the tenant object returned by
getCurrentTenant() (tenant.countryCode) as the fallback for both
phoneCountryCode and shipping.countryCode instead of the string "ZA", and ensure
phoneCountryCode does not blindly override phoneCode—either set phoneCountryCode
from the same phone parsing logic that produced phoneCode or default to
tenant.countryCode when parsing yields no explicit country code (refer to
phoneCode, phoneCountryCode, getCurrentTenant(), and shipping.countryCode to
locate the changes).

In `@nextjs_space/app/store/`[slug]/the-wire/[postSlug]/page.tsx:
- Around line 5-6: A duplicate import of ArrowLeft, Calendar, and User from
"lucide-react" was added (two identical import statements); remove the redundant
import line so those symbols are imported only once (keep a single import
statement that imports ArrowLeft, Calendar, User from "lucide-react").
- Around line 53-58: Reintroduce HTML sanitization for post.content before it's
assigned to cleanContent and rendered with dangerouslySetInnerHTML: restore the
DOMPurify import/usage and set cleanContent = DOMPurify.sanitize(post.content ||
"", { SAFE_FOR_TEMPLATES: true }) (or equivalent) so the value used in the
component's dangerouslySetInnerHTML is sanitized; ensure DOMPurify is imported
at the top of page.tsx and remove the commented-out sanitization code to avoid
returning unsanitized post.content.

In `@nextjs_space/scripts/debug-kyc-auth.ts`:
- Around line 55-68: Replace the hardcoded tenant subdomain and user email used
in prisma calls with configurable inputs: stop using the literal 'healingbuds'
in prisma.tenants.findUnique and the literal 'gerard161+buds@gmail.com' in
prisma.users.findFirst; instead read the tenant identifier and user email from
environment variables or CLI arguments (e.g., process.env.TENANT_SUBDOMAIN /
process.env.KYC_USER_EMAIL or parsed argv) before calling
decryptText(tenant.drGreenApiKey)/decryptText(tenant.drGreenSecretKey) and the
user lookup, and validate these inputs (throw a clear error if missing) so the
script is portable and avoids embedding PII.

In `@nextjs_space/scripts/populate-cart.ts`:
- Around line 16-18: The script currently logs "User or tenant not found" and
returns, which lets automation treat this as success; in populate-cart.ts update
the failure path that checks user and user.tenantId to fail fast by logging the
error via console.error (include context like user/tenant) and calling
process.exit(1) instead of returning so the process exits with a non‑zero
status; modify the block that references user and user.tenantId accordingly.
♻️ Duplicate comments (2)
nextjs_space/lib/drgreen-orders.ts (1)

48-96: Local override still blocked by early drGreenClientId guard.
Line 48-50 throws before the local questionnaire path (Line 66+) can run, so manually verified users without a client ID still cannot order. Move the guard into the API branch and add a user-null check.

🐛 Proposed fix
-    if (!user?.drGreenClientId) {
-        throw new Error("User must complete consultation before placing orders");
-    }
+    if (!user) {
+        throw new Error("User not found");
+    }
nextjs_space/app/actions/kyc-check.ts (1)

36-49: Consider falling back to auth tenantId when dbUser is missing.
Line 38 only uses dbUser?.tenantId, so users not in DB but with clerkUser.tenantId skip the pending questionnaire check and return NO_ID. Using the auth tenantId (and returning NO_TENANT when absent) keeps behavior consistent.

♻️ Suggested adjustment
-            const tenantId = dbUser?.tenantId;
-            if (!tenantId || !clerkUser.email) {
-                return { isLoggedIn: true, kycVerified: false, status: "NO_ID" };
-            }
+            const tenantId = dbUser?.tenantId ?? clerkUser.tenantId;
+            if (!tenantId) {
+                return { isLoggedIn: true, kycVerified: false, status: "NO_TENANT" };
+            }
+            if (!clerkUser.email) {
+                return { isLoggedIn: true, kycVerified: false, status: "NO_ID" };
+            }
🧹 Nitpick comments (4)
nextjs_space/app/api/shop/register/route.ts (2)

27-33: Consider validating nested required fields.

The validation only checks for top-level objects but personal.firstName, personal.lastName, and personal.email are used directly in the createClient call. Missing nested fields would cause unclear errors downstream.

♻️ Suggested improvement
     // Validate required fields
-    if (!personal || !address || !medicalRecord) {
+    if (!personal || !address || !medicalRecord) {
+      return NextResponse.json(
+        { error: "Missing required fields" },
+        { status: 400 },
+      );
+    }
+
+    if (!personal.firstName || !personal.lastName || !personal.email) {
       return NextResponse.json(
-        { error: "Missing required fields" },
+        { error: "Missing required personal information" },
         { status: 400 },
       );
     }

100-106: Persist drGreenClientId to the user record.

The Dr. Green clientId is returned in the response but not stored in the database. The schema already has a drGreenClientId field (line 149 of schema.prisma); add it to the user update to avoid losing this identifier for future API calls or lookups.

♻️ Suggested improvement
     await prisma.users.update({
       where: { id: dbUser.id },
       data: {
         name: `${personal.firstName} ${personal.lastName}`,
+        drGreenClientId: result.clientId,
       },
     });
nextjs_space/scripts/test-drgreen-products.ts (1)

46-55: Avoid hardcoded dummy signature to keep test results meaningful.
Line 54 forces auth failure; consider generating a real signature (or omitting the header) when validating the integration path.

nextjs_space/scripts/test-drgreen-auth.ts (1)

83-93: Align signature inclusion with production logic to avoid false negatives.
Line 92 always signs even for empty GET query strings; callDrGreenAPI only signs when a payload exists (or non‑GET). Mirroring that behavior will make this test closer to prod.

♻️ Suggested adjustment
-    // Always sign?
-    headers['x-auth-signature'] = generateSignature(signaturePayload, secretKey);
+    // Mirror prod: sign only if there is a query string payload
+    if (signaturePayload) {
+        headers['x-auth-signature'] = generateSignature(signaturePayload, secretKey);
+    }

Comment thread nextjs_space/app/api/shop/register/route.ts Outdated
Comment thread nextjs_space/app/api/shop/register/route.ts Outdated
Comment thread nextjs_space/app/api/shop/register/route.ts
Comment thread nextjs_space/app/store/[slug]/the-wire/[postSlug]/page.tsx Outdated
Comment thread nextjs_space/app/store/[slug]/the-wire/[postSlug]/page.tsx
Comment thread nextjs_space/scripts/debug-kyc-auth.ts Outdated
Comment thread nextjs_space/scripts/populate-cart.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@nextjs_space/app/api/shop/register/route.ts`:
- Around line 62-90: The current phone parsing silently defaults phoneCode to
"+27" and leaves contactNumber unnormalized, which fails for non-ZA tenants and
formatted inputs (e.g., "+1 (555) 123-4567"); update the logic around phone,
phoneCode, and contactNumber used when calling createClient to: strip all
non-digit/leading-plus characters from the input, validate/parse the result as
E.164 if it has a leading "+", extract the international calling code and
national number robustly (improving the current regex), and if there is no
leading "+" derive the calling code from tenant?.countryCode via a
tenant-to-dial-code mapping (or explicitly reject invalid/ambiguous values);
ensure you no longer default to "+27" silently and return/throw a validation
error for unknown/unparsable numbers so createClient always receives normalized
digits in phoneCode and contactNumber.

In `@nextjs_space/scripts/debug-kyc-auth.ts`:
- Around line 78-81: The script currently logs missing user or
user.drGreenClientId then returns which yields exit code 0; change the control
flow in the if-block that checks "if (!user || !user.drGreenClientId)" to call
process.exit(1) after logging the error (preserving the existing console.error
message and user payload) so the script exits with a failure status for CI/CD;
locate this check in debug-kyc-auth.ts and replace the lone "return" with
process.exit(1).
🧹 Nitpick comments (5)
nextjs_space/scripts/debug-kyc-auth.ts (5)

2-6: Unused import and inconsistent module style.

  • https module is imported but never used (the script uses fetch instead on line 115).
  • Using CommonJS require() in a TypeScript file is inconsistent with the type annotations present. Consider ES module imports for consistency.
♻️ Suggested cleanup
-const cryptoModule = require("crypto");
-const { PrismaClient } = require("@prisma/client");
-const https = require("https"); // or fetch if node 18+
+import * as cryptoModule from "crypto";
+import { PrismaClient } from "@prisma/client";

21-24: Consider stronger typing for better debugging.

The any type on line 21 defeats TypeScript's benefits. Also, silently returning the original text when the format doesn't match (line 24) could mask decryption issues during debugging.

♻️ Suggested improvement
-function decryptText(text: any) {
-    if (!text) return "";
-    const parts = text.split(":");
-    if (parts.length !== 3) return text;
+function decryptText(text: string | null | undefined): string {
+    if (!text) return "";
+    const parts = text.split(":");
+    if (parts.length !== 3) {
+        console.warn(`decryptText: unexpected format, returning as-is: ${text.substring(0, 20)}...`);
+        return text;
+    }

39-44: Silent error swallowing hides debugging information.

The empty catch block silently ignores base64 decoding failures. In a debug script, this could mask the root cause of signature failures. Consider logging the error.

♻️ Suggested fix
         try {
             privateKeyPEM = Buffer.from(secretKey, 'base64').toString('utf-8');
         } catch (error) {
-            // ignore
+            console.warn("Failed to decode secretKey as base64, using as-is:", error);
         }

78-84: Consider limiting PII in debug output.

Logging the entire user object (line 79) may expose PII if script output is captured in CI logs or shared for debugging. Consider logging only necessary identifiers.

         if (!user || !user.drGreenClientId) {
-            console.error("User or DrGreenClientId not found:", user);
+            console.error("User not found or missing drGreenClientId for email:", userEmail);
             return;
         }

117-124: Truncating error/response data may hide debugging details.

For a debugging script, truncating error messages (line 119) and response data (line 123) could hide the information needed to diagnose issues. Consider logging full content or using a larger limit.

Comment thread nextjs_space/app/api/shop/register/route.ts
Comment thread nextjs_space/scripts/debug-kyc-auth.ts
@AutomatosAI AutomatosAI merged commit 79d2278 into main Jan 17, 2026
1 check passed
@coderabbitai coderabbitai bot mentioned this pull request Feb 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants