Skip to content

feat: add next-intl RTL Arabic support (#104) - #123

Closed
Ayinkx wants to merge 1 commit into
Deen-Bridge:mainfrom
Ayinkx:codex/i18n-rtl-104
Closed

feat: add next-intl RTL Arabic support (#104)#123
Ayinkx wants to merge 1 commit into
Deen-Bridge:mainfrom
Ayinkx:codex/i18n-rtl-104

Conversation

@Ayinkx

@Ayinkx Ayinkx commented Jul 22, 2026

Copy link
Copy Markdown

Closes #104

Added locale routing, Arabic RTL document direction/font, locale switchers, message catalogs, and RTL logical-property fixes.
Localized key landing/dashboard shell surfaces and preserved LTR Stellar values.

Summary by CodeRabbit

  • New Features

    • Added Arabic and English language support with a locale switcher and right-to-left layout support.
    • Added landing-page statistics, partner highlights, and localized content across navigation, hero, about, CTA, and footer sections.
    • Added blog, dashboard, courses, library, messaging, wallet, donations, AI chat, search, support, profile, and settings experiences.
    • Added course and book purchasing, reviews, reading, bookmarking, and access workflows.
    • Added improved loading, not-found, and error states.
  • Style

    • Improved layout alignment and visual support for Arabic text and bidirectional content.

@vercel

vercel Bot commented Jul 22, 2026

Copy link
Copy Markdown

@Ayinkx1 is attempting to deploy a commit to the Deen Bridge Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 275d3c8b-0ec4-45e6-b62e-b34ca20c48d1

📥 Commits

Reviewing files that changed from the base of the PR and between c76e032 and 694596d.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (62)
  • app/(pages)/(landingPage)/WhyDeenBridge.jsx
  • app/[locale]/(pages)/(auth)/login/page.jsx
  • app/[locale]/(pages)/(auth)/signup/page.jsx
  • app/[locale]/(pages)/(landingPage)/About.jsx
  • app/[locale]/(pages)/(landingPage)/CTA.jsx
  • app/[locale]/(pages)/(landingPage)/Footer.jsx
  • app/[locale]/(pages)/(landingPage)/Hero.jsx
  • app/[locale]/(pages)/(landingPage)/HowItWorks.jsx
  • app/[locale]/(pages)/(landingPage)/Partners.jsx
  • app/[locale]/(pages)/(landingPage)/Stats.jsx
  • app/[locale]/(pages)/(landingPage)/Testimonials.jsx
  • app/[locale]/(pages)/(landingPage)/WhyDeenBridge.jsx
  • app/[locale]/(pages)/blog/layout.jsx
  • app/[locale]/(pages)/blog/page.jsx
  • app/[locale]/account/layout.jsx
  • app/[locale]/account/notifications/page.jsx
  • app/[locale]/account/profile/[profileid]/page.jsx
  • app/[locale]/account/security/page.jsx
  • app/[locale]/account/settings/page.jsx
  • app/[locale]/account/support/page.jsx
  • app/[locale]/account/wallet/page.jsx
  • app/[locale]/dashboard/ai/layout.js
  • app/[locale]/dashboard/ai/page.jsx
  • app/[locale]/dashboard/courses/[courseId]/CourseDetailPageClient.jsx
  • app/[locale]/dashboard/courses/[courseId]/page.jsx
  • app/[locale]/dashboard/courses/page.jsx
  • app/[locale]/dashboard/layout.jsx
  • app/[locale]/dashboard/library/[bookid]/BookDetailPageClient.jsx
  • app/[locale]/dashboard/library/[bookid]/page.jsx
  • app/[locale]/dashboard/library/page.jsx
  • app/[locale]/dashboard/library/read/[bookid]/BookReaderClient.jsx
  • app/[locale]/dashboard/library/read/[bookid]/page.jsx
  • app/[locale]/dashboard/messages/[room]/page.jsx
  • app/[locale]/dashboard/messages/layout.js
  • app/[locale]/dashboard/messages/page.jsx
  • app/[locale]/dashboard/page.jsx
  • app/[locale]/dashboard/reels/page.jsx
  • app/[locale]/dashboard/sadaqah/page.jsx
  • app/[locale]/dashboard/search/[searchparam]/page.jsx
  • app/[locale]/dashboard/spaces/[spacesid]/page.jsx
  • app/[locale]/dashboard/spaces/page.jsx
  • app/[locale]/layout.js
  • app/[locale]/loading.jsx
  • app/[locale]/not-found.js
  • app/[locale]/page.jsx
  • app/[locale]/profile-setup/page.jsx
  • components/i18n/LocaleSwitcher.jsx
  • components/molecules/dashboard/nav-header.jsx
  • components/molecules/dashboard/nav-main.jsx
  • components/molecules/dashboard/nav-routers.jsx
  • components/molecules/ladingpage/Navbar.jsx
  • components/organisms/dashboard/sidebar-left.jsx
  • components/stellar/WalletConnectButton.jsx
  • i18n/navigation.js
  • i18n/request.js
  • i18n/routing.js
  • messages/ar.json
  • messages/en.json
  • middleware.js
  • next.config.mjs
  • package.json
  • styles/globals.css
💤 Files with no reviewable changes (1)
  • app/(pages)/(landingPage)/WhyDeenBridge.jsx

Walkthrough

The PR adds next-intl English/Arabic routing, RTL support, localized landing and dashboard navigation, new public pages, protected account layouts, and dashboard features for chat, learning content, spaces, search, wallet management, and donations.

Changes

Internationalization and RTL support

Layer / File(s) Summary
Locale routing and message loading
i18n/*, middleware.js, app/[locale]/layout.js, messages/*, next.config.mjs, package.json, styles/globals.css
Adds English/Arabic routing, dynamic message catalogs, locale-aware document direction, Arabic font support, middleware, and next-intl configuration.
Locale switching and shell navigation
components/i18n/*, components/molecules/dashboard/*, components/molecules/ladingpage/*, components/organisms/dashboard/*, components/stellar/*
Localizes navigation labels, adds route-preserving locale switches, converts selected spacing utilities to logical properties, and keeps wallet values LTR.
Localized landing sections
app/[locale]/(pages)/(landingPage)/*, app/[locale]/page.jsx
Translates landing copy and feature cards while updating directional decorative positioning and composing the localized landing route.

Public and landing page experiences

Layer / File(s) Summary
Landing statistics and partner sections
app/[locale]/(pages)/(landingPage)/Stats.jsx, Partners.jsx, app/[locale]/page.jsx
Adds animated statistic cards, partner marquee cards, and renders them in the landing page composition.
Authentication and profile setup pages
app/[locale]/(pages)/(auth)/*, app/[locale]/profile-setup/page.jsx
Adds responsive login, signup, and two-step profile setup pages with shared forms, navigation links, and imagery.
Blog layout and post cards
app/[locale]/(pages)/blog/*
Adds blog metadata, navbar layout, static post data, formatted dates, and linked post cards.
Locale loading and not-found handling
app/[locale]/loading.jsx, app/[locale]/not-found.js
Adds locale-scoped loading and not-found route components.

Account and dashboard experiences

Layer / File(s) Summary
Protected account shell and account pages
app/[locale]/account/*
Adds protected account navigation plus profile, notifications, charts, settings, support, and Stellar wallet pages.
Dashboard shells and communication flows
app/[locale]/dashboard/layout.jsx, page.jsx, ai/*, messages/*, reels/page.jsx
Adds dashboard composition, AI chat history and API messaging, real-time room messaging, responsive message navigation, and dynamically loaded reels.
Courses, library, reading, spaces, and search
app/[locale]/dashboard/courses/*, library/*, spaces/*, search/*
Adds browsing, creation, purchase, review, PDF reading, space discovery/details, and typed search-result experiences.
Sadaqah donation flow
app/[locale]/dashboard/sadaqah/page.jsx
Adds wallet-gated donation steps, fund statistics, transaction execution states, success handling, and recent donation history.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Visitor
  participant LocaleSwitcher
  participant Middleware
  participant LocaleLayout
  participant TranslationCatalog
  Visitor->>LocaleSwitcher: choose English or Arabic
  LocaleSwitcher->>Middleware: navigate to current route with locale
  Middleware->>LocaleLayout: resolve locale
  LocaleLayout->>TranslationCatalog: load locale messages
  TranslationCatalog-->>LocaleLayout: return translated messages
  LocaleLayout-->>Visitor: render localized RTL or LTR page
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning Core i18n/RTL plumbing is present, but locale-aware formatting is still hardcoded to en-US in the new blog page, so the issue isn't fully met. Replace hardcoded en-US formatting with active-locale formatting (e.g. useFormatter) in all touched date/number displays, then reverify Arabic/English output.
Out of Scope Changes check ⚠️ Warning The PR also adds unrelated feature pages and flows (blog, account, wallet, courses, messages, spaces, etc.) beyond the i18n/RTL scope. Move unrelated feature work into separate PRs and keep this one focused on locale routing, translations, RTL fixes, and locale-aware formatting.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding next-intl-based RTL Arabic support.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Checkov (3.3.8)
messages/ar.json

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'

messages/en.json

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'

package.json

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 16

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (24)
app/[locale]/not-found.js-1-3 (1)

1-3: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve the active locale in 404 fallback navigation.

NotFoundComp currently falls back to /dashboard/classes or /login without the [locale] prefix. From an Arabic route, this can send users to the default locale instead of preserving /ar. Update the shared component to derive or receive the active locale and prefix both fallback paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/not-found.js around lines 1 - 3, Update NotFoundComp to derive
or receive the active locale and include it in both fallback navigation paths,
`/dashboard/classes` and `/login`, so 404 navigation preserves locale prefixes
such as `/ar`; keep the existing fallback destinations otherwise unchanged.
app/[locale]/account/security/page.jsx-151-151 (1)

151-151: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Typo in the CSS value: extra ) in fill.

fill="var(--color-accent))" has an unbalanced parenthesis, so this resolves to an invalid CSS value and the label likely won't take the intended color. Compare with the sibling <Label> just above (line 145) which correctly uses var(--color-accent).

🐛 Suggested fix
-                    fill="var(--color-accent))"
+                    fill="var(--color-accent)"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/account/security/page.jsx at line 151, Correct the fill value
in the affected Label element to use a balanced var(--color-accent) expression,
matching the sibling Label above.
app/[locale]/account/support/page.jsx-83-85 (1)

83-85: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Looks like a leftover debug placeholder — the "hi" <div> should go.

This empty grid column just renders the text "hi", which is almost certainly scaffolding that slipped in. Removing it avoids shipping stray text to users:

🧹 Suggested cleanup
-      <div>
-        hi
-</div>
-
     </div>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/account/support/page.jsx around lines 83 - 85, Remove the
placeholder “hi” div from the support page’s rendered markup, leaving the
surrounding layout structure unchanged.
app/[locale]/account/profile/[profileid]/page.jsx-24-24 (1)

24-24: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Please drop the user-object console.log — it leaks PII.

console.log("Fetched user:", res?.user) prints the full user record (likely name, email, etc.) into the browser console on every profile load. Beyond being noisy, logging user identifiers is a privacy/compliance concern (GDPR/CCPA), so it's best removed before release.

🔒 Suggested change
-      setUser(res?.user || null);
-      console.log("Fetched user:", res?.user);
-      setLoading(false);
+      setUser(res?.user || null);
+      setLoading(false);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/account/profile/[profileid]/page.jsx at line 24, Remove the
user-object console.log from the profile-loading flow so profile loads no longer
emit personal user data; leave the surrounding fetch and response handling
unchanged.
app/[locale]/account/notifications/page.jsx-601-608 (1)

601-608: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Pagination controls aren't keyboard- or screen-reader-accessible.

The two arrows are bare lucide-react icons with only a hover: style — there's no <button>, no onClick, and no accessible label, so keyboard users can't focus or activate them and assistive tech won't announce them. The footer also says "Showing 1-10 of 32" while the table renders more than 10 rows, so the text is out of sync with the data too. Wrapping the icons in buttons fixes both focusability and labeling:

♿ Suggested fix
-          <div className="flex text-xs text-muted-foreground gap-4">
-              <ArrowLeftToLine className="hover:text-accent"  size={17}/>
-              <ArrowRightToLine className="hover:text-accent"  size={17}/>
-            </div>
+          <div className="flex text-xs text-muted-foreground gap-4">
+              <Button variant="ghost" size="icon" aria-label="Previous page" disabled={/* isFirstPage */ false}>
+                <ArrowLeftToLine size={17} />
+              </Button>
+              <Button variant="ghost" size="icon" aria-label="Next page" disabled={/* isLastPage */ false}>
+                <ArrowRightToLine size={17} />
+              </Button>
+            </div>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/account/notifications/page.jsx around lines 601 - 608, Update
the pagination controls in the CardFooter to use accessible buttons around the
ArrowLeftToLine and ArrowRightToLine icons, with keyboard activation and
descriptive aria-labels (and click behavior if pagination is supported). Replace
the hard-coded “Showing 1-10 of 32” text with values derived from the rendered
notification data so the displayed range and total remain accurate.
app/[locale]/account/profile/[profileid]/page.jsx-18-52 (1)

18-52: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Distinguish request failures from missing profiles here. getUserById(profileid) returns null on caught API/request errors, so this component falls through to NotFoundComp and never shows NetworkErrorComp/Retry for transient failures. Treat null as the error path (or let getUserById throw) so users can retry instead of seeing a permanent 404-style state.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/account/profile/[profileid]/page.jsx around lines 18 - 52,
Update fetchUser to distinguish request failure from a genuinely missing
profile: when getUserById(profileid) returns null, set the error state and avoid
treating it as a missing user, so the existing NetworkErrorComp retry path
renders. Preserve NotFoundComp for successful responses where the user is
actually absent.
components/molecules/ladingpage/Navbar.jsx-34-34 (1)

34-34: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Localize the mobile logo alternative text too.

The desktop image now uses t("logo"), but the mobile image still has alt="Logo" at Line 60. Arabic screen readers therefore receive English text, leaving the landing navbar partially untranslated. Reuse the same message key for both images.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/molecules/ladingpage/Navbar.jsx` at line 34, Update the mobile
logo image’s alt attribute in the Navbar component to use the existing t("logo")
translation key, matching the desktop logo image and removing the hardcoded
English "Logo" text.
components/organisms/dashboard/sidebar-left.jsx-23-23 (1)

23-23: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep the sidebar border override physical.

components/ui/sidebar.jsx adds a physical border-r for the left sidebar, so border-e-0 only clears the inline-end side in RTL and leaves the divider visible. Use border-r-0 here, or move the border logic into the shared sidebar component so both stay aligned.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/organisms/dashboard/sidebar-left.jsx` at line 23, Update the
Sidebar invocation in the dashboard sidebar component to use the physical
right-border override that clears the shared component’s border-r styling,
replacing the current border-e-0 class with border-r-0 while preserving the
existing props.
components/stellar/WalletConnectButton.jsx-101-107 (1)

101-107: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Format Stellar balances without binary floating point.

The dir="ltr" wrappers are fine, but parseFloat(...).toFixed(...) can round USDC/XLM balances incorrectly. Use the raw decimal string or a decimal-safe formatter so the displayed amount stays exact.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/stellar/WalletConnectButton.jsx` around lines 101 - 107, Update
the balance rendering near the USDC and XLM display spans to avoid parseFloat
and binary floating-point rounding. Use the existing raw decimal values or a
decimal-safe formatter while preserving the two-decimal display format and
dir="ltr" wrappers.

Source: Path instructions

app/[locale]/dashboard/ai/page.jsx-117-121 (1)

117-121: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Minor: guard against an undefined data.response. If the upstream ever returns a body without response, the assistant bubble renders empty (undefined). A small fallback keeps the UI sensible.

🛡️ Suggested guard
-      const aiResponse = data.response;
+      const aiResponse = data.response ?? "Sorry, I couldn't generate a response.";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/dashboard/ai/page.jsx around lines 117 - 121, Update the
response handling in the AI message flow to guard against an undefined
data.response before appending the assistant message. Use a sensible fallback
content value so the assistant bubble never receives undefined, while preserving
the existing response content when it is present.
app/[locale]/dashboard/messages/page.jsx-7-14 (1)

7-14: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Minor (a11y): alt="shadow" is misleading. This is the "choose a chat" empty-state illustration, so alt="shadow" gives screen-reader users nothing useful. If it's purely decorative, use an empty alt="" so it's skipped; otherwise describe its purpose.

♿ Suggested change
-          alt="shadow"
+          alt="Select a conversation to start chatting"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/dashboard/messages/page.jsx around lines 7 - 14, Update the
Image element rendering choose-chat.svg to replace the misleading alt text with
an empty alt value if the illustration is decorative, ensuring screen readers
skip it; otherwise provide a concise description of the choose-chat empty state.
app/[locale]/dashboard/messages/[room]/page.jsx-220-222 (1)

220-222: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Minor: the list key always falls back to the array index. listenToMessages returns docs shaped as { id: doc.id, ... } (note id, not _id), so msg._id is always undefined and key={msg._id || i} degrades to the index. Using the real doc id keeps keys stable as messages stream in.

🔧 Suggested change
-                key={msg._id || i}
+                key={msg.id || msg._id || i}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/dashboard/messages/[room]/page.jsx around lines 220 - 222,
Update the message list key in the render loop to use msg.id, matching the
document shape returned by listenToMessages, instead of msg._id. Preserve the
index only as a fallback when no document id is available.
app/[locale]/dashboard/ai/page.jsx-127-140 (1)

127-140: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Minor: the error-message extraction assumes an Axios error, but this path uses fetch. Because a failed request throws new Error(\HTTP error! status: ...`), error.responseis alwaysundefined, so the error.response?.data?.message/.detailsbranches never fire and users only ever see the rawerror.message. The route handler (app/api/ai/chat/route.js) actually returns a JSON body with message/details`, so reading the response body would surface a much more helpful toast.

🛠️ Suggested direction
-      if (!response.ok) {
-        throw new Error(`HTTP error! status: ${response.status}`);
-      }
-
-      const data = await response.json();
+      const data = await response.json().catch(() => ({}));
+      if (!response.ok) {
+        throw new Error(data.details || data.message || `HTTP error! status: ${response.status}`);
+      }

Then in catch, error.message already carries the server-provided detail.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/dashboard/ai/page.jsx around lines 127 - 140, Update the catch
block in the AI response flow to stop relying on Axios-only error.response
fields, since the request uses fetch. Use the existing error.message, which
already contains the server-provided detail from the route handler, when
constructing errorMessage, while retaining the generic fallback.
app/[locale]/dashboard/messages/[room]/page.jsx-130-133 (1)

130-133: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Minor: prefer a toast over alert() for send failures. The rest of the app uses sonner (toast.error), and a native alert() is jarring, blocks the thread, and looks out of place. Consistency here also helps screen-reader users get a uniform experience.

💡 Suggested change
-      console.log("Failed to send message:", error);
-      alert("Failed to send message. Please try again.");
+      console.error("Failed to send message:", error);
+      toast.error("Failed to send message. Please try again.");

(add import { toast } from "sonner";)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/dashboard/messages/[room]/page.jsx around lines 130 - 133,
Replace the native alert in the message-send error handler with the app’s sonner
notification pattern: import toast from sonner and call toast.error with the
existing failure message, while preserving the console logging and catch flow.
app/[locale]/(pages)/(auth)/login/page.jsx-28-28 (1)

28-28: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make the side-panel image alternative text intentional.

alt="Image" is not meaningful accessible text. These panels appear decorative, so use alt=""; otherwise provide a description of the image’s purpose.

  • app/[locale]/(pages)/(auth)/login/page.jsx#L28-L28: replace the generic alt text.
  • app/[locale]/(pages)/(auth)/signup/page.jsx#L26-L26: replace the generic alt text.
  • app/[locale]/profile-setup/page.jsx#L42-L42: replace the generic alt text.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/(pages)/(auth)/login/page.jsx at line 28, Replace the generic
alt text with an empty alt value for the decorative side-panel images in
app/[locale]/(pages)/(auth)/login/page.jsx lines 28-28,
app/[locale]/(pages)/(auth)/signup/page.jsx lines 26-26, and
app/[locale]/profile-setup/page.jsx lines 42-42. No descriptions are needed
because these images are decorative.
app/[locale]/dashboard/library/[bookid]/BookDetailPageClient.jsx-128-136 (1)

128-136: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Add rel="noopener noreferrer" to the target="_blank" download links. Opening an untrusted book.fileUrl in a new tab without rel exposes the opener to reverse tabnabbing (the opened page can manipulate window.opener). Modern browsers default to noopener, but setting it explicitly is the robust, lint-clean fix.

  • app/[locale]/dashboard/library/[bookid]/BookDetailPageClient.jsx#L128-L136: add rel="noopener noreferrer" to the Download Button (it spreads ...props to the underlying link).
  • app/[locale]/dashboard/library/read/[bookid]/BookReaderClient.jsx#L294-L306: add rel="noopener noreferrer" to the Download Button.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/dashboard/library/[bookid]/BookDetailPageClient.jsx around
lines 128 - 136, Add rel="noopener noreferrer" to the target="_blank" Download
Button in BookDetailPageClient.jsx (lines 128-136) and the corresponding
Download Button in BookReaderClient.jsx (lines 294-306), preserving the existing
book.fileUrl/download behavior.

Source: Linters/SAST tools

app/[locale]/dashboard/search/[searchparam]/page.jsx-71-73 (1)

71-73: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use locale-aware date formatting with an explicit locale/timezone.

new Date(date).toLocaleString() relies on the ambient locale/timezone. This PR's goal is active-locale-aware formatting, and React Doctor also flags it as a hydration-mismatch risk on SSR paths. Passing an explicit locale (e.g., from next-intl) and timeZone makes server/client output deterministic and honors the selected locale.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/dashboard/search/[searchparam]/page.jsx around lines 71 - 73,
The formatDate function currently depends on the environment’s locale and
timezone. Update formatDate to accept and use the active locale plus an explicit
deterministic timeZone when calling toLocaleString, sourcing the locale from the
existing next-intl context or page locale, while preserving the null result for
missing dates.

Source: Linters/SAST tools

app/[locale]/dashboard/courses/[courseId]/CourseDetailPageClient.jsx-169-171 (1)

169-171: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Placeholder copy mismatch: this is a course, not a book.

The review textarea on the course page still says "What did you think about the book?", which will confuse learners leaving a course review. A quick copy fix keeps the UX consistent.

✏️ Suggested copy fix
-                      placeholder="What did you think about the book?"
+                      placeholder="What did you think about the course?"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/dashboard/courses/[courseId]/CourseDetailPageClient.jsx around
lines 169 - 171, Update the review Textarea placeholder in
CourseDetailPageClient to refer to the course instead of the book, preserving
the existing styling and review flow.
app/[locale]/layout.js-52-67 (1)

52-67: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Generate locale-aware metadata for Arabic routes. app/[locale]/layout.js still exports a static English metadata object, so /ar will keep English title, description, Open Graph, and Twitter text. Switch this to generateMetadata({ params }) (or otherwise branch on locale) so Arabic pages get localized metadata too.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/layout.js around lines 52 - 67, Replace the static metadata
export in RootLayout with locale-aware generateMetadata({ params }). Resolve the
locale from params, select the localized title, description, Open Graph, and
Twitter values for Arabic versus English, and preserve the existing metadata
structure for other locales.
app/[locale]/(pages)/(landingPage)/Partners.jsx-26-27 (1)

26-27: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Mirror the marquee edge fades in RTL.

Lines 26-27 remain physically anchored and retain LTR gradient directions, so the fade effect does not mirror on /ar. Use logical start/end placement and direction-aware gradients.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/(pages)/(landingPage)/Partners.jsx around lines 26 - 27, Update
the marquee edge-fade elements in the Partners component to use logical
start/end positioning instead of left/right anchors, and make each gradient
direction respond to the document direction so the fades mirror correctly in RTL
while preserving the existing sizing and styling.

Source: Path instructions

app/[locale]/(pages)/(landingPage)/CTA.jsx-15-18 (1)

15-18: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Avoid nesting the CTA button inside a link.

Line 15 wraps Button’s native <button> in Link, creating invalid nested interactive elements. Render one styled Link, or update the shared button API to render a single link element for navigation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/(pages)/(landingPage)/CTA.jsx around lines 15 - 18, Update the
CTA navigation around the Link and Button elements to render a single
interactive element instead of nesting Button’s native button inside Link.
Preserve the existing signup destination, translated label, styling, and hover
behavior by either styling Link directly or using the shared Button API’s
link-rendering support.
app/[locale]/(pages)/blog/page.jsx-124-146 (1)

124-146: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use logical positioning and a direction-aware arrow.

left-4 anchors the date badge physically on the left, while &rarr; always points right. In Arabic, use start-4 and a mirrored/direction-aware icon or RTL transform.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/(pages)/blog/page.jsx around lines 124 - 146, Update the blog
card date badge positioning to use logical `start-4` instead of `left-4`, and
replace the hardcoded right arrow in the “Read more” link with a direction-aware
or RTL-mirrored icon so it points correctly for Arabic and other RTL locales.
app/[locale]/(pages)/blog/layout.jsx-33-33 (1)

33-33: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use openGraph.siteName here. site_name is not a supported Metadata API key, so Next won’t emit the intended og:site_name tag.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/(pages)/blog/layout.jsx at line 33, Update the metadata
configuration in the blog layout to use the supported openGraph.siteName key
instead of site_name, preserving the existing “Deen Bridge Blog” value so
Next.js emits the intended Open Graph site name.

Source: MCP tools

app/[locale]/(pages)/blog/page.jsx-125-142 (1)

125-142: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use the page locale when formatting blog dates. toLocaleDateString("en-US") hardcodes English, and without timeZone: "UTC" an ISO date can land on a different day across environments. Format once with the active locale and reuse that value instead of rendering the raw post.date.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/(pages)/blog/page.jsx around lines 125 - 142, Update the blog
date rendering in the page component to format each post date once using the
active page locale and timeZone "UTC", then reuse that formatted value in both
the displayed date locations instead of rendering raw post.date.

Source: Linters/SAST tools

🧹 Nitpick comments (13)
app/[locale]/account/security/page.jsx (1)

298-298: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Small grammar nit in the user-facing copy.

"You're average more steps a day this year than last year." reads awkwardly — likely intended as "You're averaging more steps a day this year than last year."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/account/security/page.jsx at line 298, Correct the user-facing
sentence in the security page copy from “You're average more steps...” to
“You're averaging more steps a day this year than last year.”
app/[locale]/account/layout.jsx (1)

12-13: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Tiny cleanup: path is computed but never used (and .split("") looks unintended).

usePathname().split("") splits the pathname into an array of individual characters, and the resulting path is never referenced anywhere in the component. It's harmless at runtime, but it's a little confusing for the next reader and can trip up lint rules. Safe to drop entirely:

♻️ Suggested cleanup
-export default function Layout({ children }) {
-    const path = usePathname().split("");
-    return (
+export default function Layout({ children }) {
+    return (

You can then also drop the usePathname import if nothing else uses it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/account/layout.jsx around lines 12 - 13, Remove the unused path
declaration from Layout, including the unintended usePathname().split("")
computation, and remove the usePathname import if it is no longer referenced
elsewhere in the component.
app/[locale]/account/settings/page.jsx (1)

475-505: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Heads-up for when this gets wired to a real API: confirmPassword is collected but never validated.

Right now handleAccountSave is a mock (setTimeout), so nothing breaks — but the "Confirm New Password" field is never compared against newPassword, so once you connect this to a real endpoint a mismatch would sail straight through. Worth adding a guard now while the form is fresh in mind:

♻️ Suggested guard
   const handleAccountSave = async (e) => {
     e.preventDefault();
+    if (account.newPassword && account.newPassword !== account.confirmPassword) {
+      showMessage("error", "New password and confirmation do not match.");
+      return;
+    }
     setIsLoading(true);

(For context: the setstate-same-var and missing-key warnings from static analysis on this file are false positives — the setTimeout calls are fine and CheckCircle is a conditional child of the already-keyed Button.)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/account/settings/page.jsx around lines 475 - 505, Add a
validation guard in handleAccountSave that compares account.confirmPassword with
account.newPassword before the mock save proceeds, and return early with the
form’s existing error feedback when they differ. Keep the current setTimeout
success flow unchanged for matching passwords.

Source: Linters/SAST tools

app/[locale]/account/support/page.jsx (1)

20-23: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

handleSubmit is a no-op, so the ticket form doesn't do anything yet.

handleSubmit only calls e.preventDefault()subject, description, image, and loading are all captured in state but never sent anywhere, and loading/setLoading (and modalHandler) are declared but unused. That means clicking "Create a ticket" silently does nothing. If this is intentionally staged for a follow-up, a // TODO comment would signal that clearly.

I'm happy to draft the submission logic (e.g., building the FormData with the image and posting to your tickets endpoint) — want me to open an issue to track it?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/account/support/page.jsx around lines 20 - 23, Implement the
ticket submission flow in handleSubmit instead of leaving it as a no-op: build a
FormData payload from subject, description, and image, manage loading with
setLoading, and submit it to the tickets endpoint using the existing request
conventions. Handle success and failure through modalHandler, reset or preserve
form state appropriately, and ensure loading is cleared on every path.
app/[locale]/account/notifications/page.jsx (1)

63-597: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

This looks like unadapted boilerplate: 16 hand-copied rows of product/sales data on a "Notifications" page.

Two things worth addressing here:

  1. Content mismatch — the columns are Name / Price / Total Sales / Created at with rows like "Laser Lemonade Machine" and "$499.99". That's e-commerce product data, not notifications, so this reads as a template that wasn't tailored to the feature yet.
  2. Duplication — the same <TableRow> markup is copy-pasted ~16 times. Driving it from a data array keeps it DRY and makes it trivial to swap in real notification data later.
♻️ Sketch of a data-driven approach
const notifications = [/* { id, title, image, createdAt, ... } */];

<TableBody>
  {notifications.map((n) => (
    <TableRow key={n.id}>
      {/* one row template referencing n.* */}
    </TableRow>
  ))}
</TableBody>

Want me to open an issue to track wiring this up to a real notifications source?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/account/notifications/page.jsx around lines 63 - 597, Replace
the repeated hand-copied product rows in the TableBody with a notifications data
array and a single mapped TableRow template, keyed by each notification’s unique
id. Adapt the displayed columns, labels, image/metadata, and actions to
notification content rather than e-commerce fields such as price and total
sales, while preserving the existing table and dropdown structure.
components/molecules/dashboard/nav-main.jsx (1)

23-23: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use explicit translation keys in both dashboard navigation lists.

Both components derive next-intl message IDs from English display text, making catalog keys brittle when labels change or contain multiple words.

  • components/molecules/dashboard/nav-main.jsx#L23-L23: add a stable translationKey to each data.navMain item and call t(item.translationKey).
  • components/molecules/dashboard/nav-routers.jsx#L97-L97: add a stable translation key to each route record and translate that key instead of item.name.toLowerCase().
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/molecules/dashboard/nav-main.jsx` at line 23, Replace
label-derived translation IDs with explicit stable translation keys in both
navigation lists: update each data.navMain item and route record in
components/molecules/dashboard/nav-main.jsx (line 23) and
components/molecules/dashboard/nav-routers.jsx (line 97) to define a
translationKey, then have the corresponding renderers call
t(item.translationKey) instead of deriving keys from display text.
app/[locale]/dashboard/sadaqah/page.jsx (1)

508-540: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Keep monetary amounts LTR in the Arabic layout.

This page lives under app/[locale] and is reachable at /ar, where the document is RTL. A PR objective is to keep monetary values LTR within RTL layouts, but ${formatAmount(donation.amount)} USDC (and the balance/pool figures above) inherit RTL, which can flip the $/USDC ordering around the number. Wrapping the numeric+currency runs in dir="ltr" (or a <bdi>) keeps them rendering correctly under Arabic. Please confirm whether this page is in the current translation/RTL-audit scope; if it's intentionally deferred as an untranslated fallback area, this can wait.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/dashboard/sadaqah/page.jsx around lines 508 - 540, The monetary
amount and currency displays in the dashboard, including the recent donation
value rendered in the recent.map block and the balance/pool figures above, must
remain LTR under Arabic RTL layouts. Wrap each complete numeric-and-currency run
with dir="ltr" or an equivalent bdi element, and apply the same treatment to all
affected figures on this page; confirm this page is within the current RTL-audit
scope rather than deferring it as an untranslated fallback.
app/[locale]/dashboard/page.jsx (1)

1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Nit: stale // pages/dashboard.jsx comment. This is an App Router route (app/[locale]/dashboard/page.jsx), so the leading comment points at the wrong location/paradigm and can confuse contributors. Safe to remove.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/dashboard/page.jsx at line 1, Remove the stale leading
“pages/dashboard.jsx” comment from the dashboard route file; no other code
changes are needed.
app/[locale]/dashboard/messages/[room]/page.jsx (1)

277-293: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Recommended: debounce the typing signal. setTyping(room, user._id, true) fires on every keystroke, and each call is a Firestore setDoc write (lib/actions/messages/typing.js). On a longer message that's dozens of writes per message — real cost and quota churn. Debouncing the "true" write and flipping to false after a short idle timeout (in addition to onBlur) gives the same UX far more cheaply.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/dashboard/messages/[room]/page.jsx around lines 277 - 293, The
message Textarea’s onChange currently writes the typing state on every
keystroke. Update the typing flow around setTyping, setNewMessage, and the
Textarea handlers to debounce the true signal and schedule a short idle-timeout
false signal, while retaining the immediate onBlur reset; clear or replace
pending timers as needed so stale callbacks cannot overwrite newer typing
activity.
app/[locale]/dashboard/ai/layout.js (1)

4-9: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Nit: pathname is computed but never used. usePathname() here just adds an unused subscription/import — safe to drop until it's actually needed, which keeps the component lean and avoids confusing future readers.

♻️ Suggested cleanup
-import { usePathname } from "next/navigation";
   const [chatData, setChatData] = useState({ messages: [], chatId: null });
-  const pathname = usePathname();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/dashboard/ai/layout.js around lines 4 - 9, Remove the unused
usePathname import and delete the pathname declaration from Layout, while
leaving the currentChatId and chatData state setup unchanged.
app/[locale]/dashboard/courses/[courseId]/CourseDetailPageClient.jsx (1)

108-112: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Consider next/image for the preview thumbnail.

A raw <img> skips Next.js optimization (automatic sizing, lazy-loading, format negotiation) that the rest of this PR already relies on via next/image. Not blocking, but worth aligning for performance and to avoid the @next/next/no-img-element lint warning in CI.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/dashboard/courses/[courseId]/CourseDetailPageClient.jsx around
lines 108 - 112, Replace the raw img element in CourseDetailPageClient with
Next.js’s Image component, preserving the existing thumbnail source, alt text,
styling, and preview appearance while supplying the sizing or fill configuration
required by next/image.
app/[locale]/(pages)/(landingPage)/WhyDeenBridge.jsx (1)

1-5: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Remove the unnecessary client boundaries.

Neither component uses browser-only APIs, state, or event handlers, so keeping them as client components only widens the client bundle. useTranslations can stay here, and importing Button from a server component is fine.

  • app/[locale]/(pages)/(landingPage)/WhyDeenBridge.jsx#L1-L5: drop "use client".
  • app/[locale]/(pages)/(landingPage)/Partners.jsx#L1-L3: drop "use client"; it only maps static data.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/(pages)/(landingPage)/WhyDeenBridge.jsx around lines 1 - 5,
Remove the "use client" directive from WhyDeenBridge.jsx and Partners.jsx; both
components can remain server components while retaining useTranslations, Button
imports, and static data mapping unchanged.

Source: Path instructions

app/[locale]/(pages)/blog/page.jsx (1)

1-1: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Remove the unnecessary client boundary.

This page only renders static data and links; it has no state, effects, or browser-only APIs. Removing "use client" keeps the listing server-rendered and avoids shipping the entire post catalog and card tree as client code.

As per path instructions, app/** is a Next.js 15 App Router project; client components that could be server components should be flagged.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/(pages)/blog/page.jsx at line 1, Remove the unnecessary "use
client" directive from the blog page module so it remains a server component.
Preserve the existing static post catalog and link-rendering behavior without
adding client-only state, effects, or browser APIs.

Source: Path instructions


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 275d3c8b-0ec4-45e6-b62e-b34ca20c48d1

📥 Commits

Reviewing files that changed from the base of the PR and between c76e032 and 694596d.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (62)
  • app/(pages)/(landingPage)/WhyDeenBridge.jsx
  • app/[locale]/(pages)/(auth)/login/page.jsx
  • app/[locale]/(pages)/(auth)/signup/page.jsx
  • app/[locale]/(pages)/(landingPage)/About.jsx
  • app/[locale]/(pages)/(landingPage)/CTA.jsx
  • app/[locale]/(pages)/(landingPage)/Footer.jsx
  • app/[locale]/(pages)/(landingPage)/Hero.jsx
  • app/[locale]/(pages)/(landingPage)/HowItWorks.jsx
  • app/[locale]/(pages)/(landingPage)/Partners.jsx
  • app/[locale]/(pages)/(landingPage)/Stats.jsx
  • app/[locale]/(pages)/(landingPage)/Testimonials.jsx
  • app/[locale]/(pages)/(landingPage)/WhyDeenBridge.jsx
  • app/[locale]/(pages)/blog/layout.jsx
  • app/[locale]/(pages)/blog/page.jsx
  • app/[locale]/account/layout.jsx
  • app/[locale]/account/notifications/page.jsx
  • app/[locale]/account/profile/[profileid]/page.jsx
  • app/[locale]/account/security/page.jsx
  • app/[locale]/account/settings/page.jsx
  • app/[locale]/account/support/page.jsx
  • app/[locale]/account/wallet/page.jsx
  • app/[locale]/dashboard/ai/layout.js
  • app/[locale]/dashboard/ai/page.jsx
  • app/[locale]/dashboard/courses/[courseId]/CourseDetailPageClient.jsx
  • app/[locale]/dashboard/courses/[courseId]/page.jsx
  • app/[locale]/dashboard/courses/page.jsx
  • app/[locale]/dashboard/layout.jsx
  • app/[locale]/dashboard/library/[bookid]/BookDetailPageClient.jsx
  • app/[locale]/dashboard/library/[bookid]/page.jsx
  • app/[locale]/dashboard/library/page.jsx
  • app/[locale]/dashboard/library/read/[bookid]/BookReaderClient.jsx
  • app/[locale]/dashboard/library/read/[bookid]/page.jsx
  • app/[locale]/dashboard/messages/[room]/page.jsx
  • app/[locale]/dashboard/messages/layout.js
  • app/[locale]/dashboard/messages/page.jsx
  • app/[locale]/dashboard/page.jsx
  • app/[locale]/dashboard/reels/page.jsx
  • app/[locale]/dashboard/sadaqah/page.jsx
  • app/[locale]/dashboard/search/[searchparam]/page.jsx
  • app/[locale]/dashboard/spaces/[spacesid]/page.jsx
  • app/[locale]/dashboard/spaces/page.jsx
  • app/[locale]/layout.js
  • app/[locale]/loading.jsx
  • app/[locale]/not-found.js
  • app/[locale]/page.jsx
  • app/[locale]/profile-setup/page.jsx
  • components/i18n/LocaleSwitcher.jsx
  • components/molecules/dashboard/nav-header.jsx
  • components/molecules/dashboard/nav-main.jsx
  • components/molecules/dashboard/nav-routers.jsx
  • components/molecules/ladingpage/Navbar.jsx
  • components/organisms/dashboard/sidebar-left.jsx
  • components/stellar/WalletConnectButton.jsx
  • i18n/navigation.js
  • i18n/request.js
  • i18n/routing.js
  • messages/ar.json
  • messages/en.json
  • middleware.js
  • next.config.mjs
  • package.json
  • styles/globals.css
💤 Files with no reviewable changes (1)
  • app/(pages)/(landingPage)/WhyDeenBridge.jsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

🛑 Comments failed to post (16)
app/[locale]/(pages)/(auth)/login/page.jsx (1)

12-12: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== files ==\n'
git ls-files 'app/[locale]/(pages)/(auth)/login/page.jsx' 'app/[locale]/(pages)/(auth)/signup/page.jsx' 'app/[locale]/profile-setup/page.jsx' 'app/**/navigation*' 'i18n*' 'middleware*' | sed 's#^`#-` #'

printf '\n== login page ==\n'
sed -n '1,120p' 'app/[locale]/(pages)/(auth)/login/page.jsx'

printf '\n== signup page ==\n'
sed -n '1,120p' 'app/[locale]/(pages)/(auth)/signup/page.jsx'

printf '\n== profile-setup page ==\n'
sed -n '1,160p' 'app/[locale]/profile-setup/page.jsx'

printf '\n== navigation / middleware helpers ==\n'
for f in $(git ls-files | grep -E '(^|/)(navigation|i18n|middleware).*\.(js|jsx|ts|tsx)$' || true); do
  echo "--- $f"
  sed -n '1,220p' "$f"
done

Repository: Deen-Bridge/dnb-frontend

Length of output: 13148


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Narrow search for Link usage and locale-aware navigation helpers
rg -n --glob 'app/**/*.jsx' "from 'next/link'|from \"next/link\"|href=\"/\"|href='/'|`@/i18n/navigation`|next-intl|useLocale|locale" app

Repository: Deen-Bridge/dnb-frontend

Length of output: 1829


🌐 Web query:

next-intl navigation Link from "@/i18n/navigation" locale-aware href="/" current locale

💡 Result:

In next-intl, you can create a locale-aware Link component by using the navigation APIs created via the createNavigation (or previously createSharedPathnamesNavigation) factory function [1][2][3]. By importing Link from your configured navigation file (e.g., @/i18n/navigation), the component automatically handles the current locale and applies the correct path prefixes based on your routing configuration [1][3]. Usage: 1. Setup your navigation configuration (typically in src/i18n/navigation.ts): import {createNavigation} from 'next-intl/navigation'; import {routing} from './routing'; export const {Link, redirect, usePathname, useRouter, getPathname} = createNavigation(routing); 2. Use the Link component in your components: import {Link} from '@/i18n/navigation'; // When on /en, this renders a link to /en/about About // To switch the locale, you can pass the locale prop: // This will point to the root of the specified locale Switch to German Key features of this Link component: - Locale awareness: It automatically prepends the current locale to the href based on your localePrefix setting (e.g., 'always' or 'as-needed') [1][4]. - Type safety: If using localized pathnames, the href prop is strictly typed according to your routing configuration [1][5]. - Overriding: You can explicitly pass a locale prop to change the language of the target page, which also updates the hreflang attribute [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect any i18n navigation helper and middleware references if present
for f in $(git ls-files | grep -E '(^|/)(i18n|navigation|middleware).*\.(js|jsx|ts|tsx)$' || true); do
  echo "--- $f"
  sed -n '1,240p' "$f"
done

Repository: Deen-Bridge/dnb-frontend

Length of output: 8184


Use the locale-aware Link here. href="/" bypasses the app’s locale handling and sends users to the locale-neutral root, which can drop the active language on these locale-scoped pages.

  • app/[locale]/(pages)/(auth)/login/page.jsx#L12
  • app/[locale]/(pages)/(auth)/signup/page.jsx#L10
  • app/[locale]/profile-setup/page.jsx#L26
📍 Affects 3 files
  • app/[locale]/(pages)/(auth)/login/page.jsx#L12-L12 (this comment)
  • app/[locale]/(pages)/(auth)/signup/page.jsx#L10-L10
  • app/[locale]/profile-setup/page.jsx#L26-L26
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/(pages)/(auth)/login/page.jsx at line 12, Update the Link
components in app/[locale]/(pages)/(auth)/login/page.jsx:12-12,
app/[locale]/(pages)/(auth)/signup/page.jsx:10-10, and
app/[locale]/profile-setup/page.jsx:26-26 to use the project’s locale-aware Link
implementation while preserving the existing destination and styling. Ensure
navigation retains the active locale instead of using the locale-neutral root.

Source: Path instructions

app/[locale]/(pages)/(landingPage)/Partners.jsx (1)

16-22: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Localize the newly added landing sections.

These sections bypass the new message catalogs, so /ar still renders English content instead of satisfying the localized-landing objective.

  • app/[locale]/(pages)/(landingPage)/Partners.jsx#L16-L22: move the heading and description into the landing message namespace and render them with useTranslations.
  • app/[locale]/(pages)/(landingPage)/Stats.jsx#L8-L37: move stat labels and descriptions into translated message keys.
  • app/[locale]/(pages)/(landingPage)/Stats.jsx#L97-L105: translate the statistics section heading, eyebrow, and description.
📍 Affects 2 files
  • app/[locale]/(pages)/(landingPage)/Partners.jsx#L16-L22 (this comment)
  • app/[locale]/(pages)/(landingPage)/Stats.jsx#L8-L37
  • app/[locale]/(pages)/(landingPage)/Stats.jsx#L97-L105
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/(pages)/(landingPage)/Partners.jsx around lines 16 - 22,
Localize the new landing-page content through the landing message namespace: in
app/[locale]/(pages)/(landingPage)/Partners.jsx lines 16-22, use useTranslations
and replace the heading and description with message keys; in
app/[locale]/(pages)/(landingPage)/Stats.jsx lines 8-37, move stat labels and
descriptions to translated keys; and in
app/[locale]/(pages)/(landingPage)/Stats.jsx lines 97-105, translate the
statistics eyebrow, heading, and description.

Source: Path instructions

app/[locale]/(pages)/(landingPage)/Stats.jsx (1)

45-77: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Format statistics from structured values, not display strings.

AnimatedCounter always emits Western digits and ./$ ordering, so /ar does not get locale-aware formatting and monetary values can render with incorrect bidi ordering. Keep numeric values/unit metadata separately, format with Intl.NumberFormat for the active locale, and isolate monetary output with dir="ltr"/<bdi>.

🧰 Tools
🪛 ast-grep (0.44.1)

[warning] 59-59: Avoid using the initial state variable in setState
Context: setCount(numericValue)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(setstate-same-var)


[warning] 62-62: Avoid using the initial state variable in setState
Context: setCount(current)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(setstate-same-var)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/(pages)/(landingPage)/Stats.jsx around lines 45 - 77, Update
AnimatedCounter to receive and preserve numeric values and unit metadata
separately instead of parsing prefixes and suffixes from display strings. Format
the animated count with Intl.NumberFormat using the active locale, preserving
the intended decimal precision and localized digits. For monetary statistics,
isolate the formatted output with dir="ltr" and/or bdi so currency ordering and
bidi rendering remain correct.
app/[locale]/(pages)/blog/layout.jsx (1)

3-48: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## blog layout\n'
cat -n 'app/[locale]/(pages)/blog/layout.jsx' | sed -n '1,220p'

printf '\n## blog page\n'
cat -n 'app/[locale]/(pages)/blog/page.jsx' | sed -n '1,260p'

printf '\n## locale-related files\n'
git ls-files 'app/[locale]/**' | sed -n '1,200p'

printf '\n## search for i18n/locale helpers\n'
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' --glob '!coverage' '\b(locale|locales|i18n|translate|translations|t\()' app lib src . 2>/dev/null | sed -n '1,260p'

Repository: Deen-Bridge/dnb-frontend

Length of output: 27288


🏁 Script executed:

#!/bin/bash
set -euo pipefail
cat /tmp/sandbox_stdout.log | sed -n '1,260p'

Repository: Deen-Bridge/dnb-frontend

Length of output: 218


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## files under app/[locale]\n'
git ls-files 'app/[locale]/**' | sed -n '1,260p'

printf '\n## /blog link patterns\n'
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' --glob '!coverage' 'to=\{?`?/blog|href=\{?`?/blog|"/blog/|\'/blog/' app components lib src 2>/dev/null | sed -n '1,260p'

printf '\n## locale segment patterns\n'
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' --glob '!coverage' '\[\s*locale\s*\]|params\.locale|locale\/' app components lib src 2>/dev/null | sed -n '1,260p'

Repository: Deen-Bridge/dnb-frontend

Length of output: 276


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## home page\n'
cat -n 'app/[locale]/(pages)/home/page.jsx' | sed -n '1,220p'

printf '\n## courses page\n'
cat -n 'app/[locale]/(pages)/courses/page.jsx' | sed -n '1,180p'

printf '\n## blog slug page\n'
cat -n 'app/[locale]/(pages)/blog/[slug]/page.jsx' | sed -n '1,260p'

printf '\n## locale helpers / link wrappers\n'
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' --glob '!coverage' 'from "`@/components`|from ".*/Link|locale.*href|href.*locale|useLocale|NextIntl|next-intl|i18n' app components lib src 2>/dev/null | sed -n '1,260p'

Repository: Deen-Bridge/dnb-frontend

Length of output: 245


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## root routing files\n'
git ls-files | rg '^(middleware\.js|middleware\.ts|next\.config\.(js|mjs|ts)|app/.*/layout\.(js|jsx|ts|tsx)|app/.*/page\.(js|jsx|ts|tsx))$' | sed -n '1,240p'

printf '\n## search for locale rewrite / redirect / middleware logic\n'
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' --glob '!coverage' 'redirect\(|rewrite\(|NextResponse|matcher|locale|i18n|defaultLocale|locales' middleware.* next.config.* app lib src 2>/dev/null | sed -n '1,260p'

printf '\n## metadata in other pages\n'
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' --glob '!coverage' 'export const metadata|generateMetadata' app/[locale] 2>/dev/null | sed -n '1,260p'

Repository: Deen-Bridge/dnb-frontend

Length of output: 2454


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## root page\n'
cat -n 'app/page.jsx' | sed -n '1,220p'

printf '\n## home metadata\n'
cat -n 'app/[locale]/(pages)/home/page.jsx' | sed -n '1,60p'

printf '\n## about metadata\n'
cat -n 'app/[locale]/(pages)/about/page.jsx' | sed -n '1,80p'

printf '\n## contact metadata\n'
cat -n 'app/[locale]/(pages)/contact/page.jsx' | sed -n '1,80p'

Repository: Deen-Bridge/dnb-frontend

Length of output: 221


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## button component files\n'
git ls-files 'components/**' | rg 'button|link|nav|header|footer' | sed -n '1,260p'

printf '\n## button implementation\n'
fd -a 'button.*' components . | sed -n '1,80p'

printf '\n## read button source\n'
for f in $(fd -a 'button.*' components .); do
  echo "\n### $f"
  cat -n "$f" | sed -n '1,220p'
done

printf '\n## search for custom link helper\n'
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' --glob '!coverage' 'function Button|const Button|export .*Button|to=' components app lib src 2>/dev/null | sed -n '1,260p'

Repository: Deen-Bridge/dnb-frontend

Length of output: 35364


🏁 Script executed:

#!/bin/bash
set -euo pipefail
cat -n 'components/ui/button.jsx' | sed -n '1,220p'

Repository: Deen-Bridge/dnb-frontend

Length of output: 2472


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## i18n/routing.js\n'
cat -n 'i18n/routing.js' | sed -n '1,220p'

printf '\n## middleware.js\n'
cat -n 'middleware.js' | sed -n '1,220p'

printf '\n## i18n/navigation.js\n'
cat -n 'i18n/navigation.js' | sed -n '1,220p'

Repository: Deen-Bridge/dnb-frontend

Length of output: 937


Keep the blog surface locale-aware
app/[locale]/(pages)/blog/layout.jsx hardcodes https://deenbridge.com/blog, and app/[locale]/(pages)/blog/page.jsx / app/[locale]/(pages)/blog/[slug]/page.jsx use plain /blog paths for the CTA and back link. With localePrefix: "as-needed", Arabic visitors fall back to the default locale instead of staying on the localized route. The same /blog/... links also appear in app/[locale]/(pages)/home/page.jsx and app/[locale]/(pages)/courses/page.jsx; move the blog copy into locale messages as well.

📍 Affects 2 files
  • app/[locale]/(pages)/blog/layout.jsx#L3-L48 (this comment)
  • app/[locale]/(pages)/blog/page.jsx#L4-L95
  • app/[locale]/(pages)/blog/page.jsx#L97-L111
  • app/[locale]/(pages)/blog/page.jsx#L145-L147
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/(pages)/blog/layout.jsx around lines 3 - 48, Make the blog
metadata and navigation locale-aware: update
app/[locale]/(pages)/blog/layout.jsx lines 3-48 to generate the canonical URL
with the active locale, and update app/[locale]/(pages)/blog/page.jsx lines
4-95, 97-111, and 145-147 to use locale-preserving blog routes for the CTA and
back link. Move the affected blog copy into locale messages, and also update the
`/blog/...` links in app/[locale]/(pages)/home/page.jsx and
app/[locale]/(pages)/courses/page.jsx to preserve the current locale.

Source: MCP tools

app/[locale]/(pages)/blog/page.jsx (1)

2-2: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## app/[locale]/(pages)/blog/page.jsx\n'
cat -n app/[locale]/(pages)/blog/page.jsx

printf '\n## locale-aware navigation helpers and blog slug routes\n'
rg -n "locale-aware|navigation helper|Link from \"next/link\"|/blog/\$\{post\.slug\}|post\.slug|useRouter|Link" app -g '!**/*.map' || true

printf '\n## blog route files\n'
fd -a "page.jsx" app | sed 's#^\./##' | sort

Repository: Deen-Bridge/dnb-frontend

Length of output: 254


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## app/[locale]/(pages)/blog/page.jsx\n'
cat -n "app/[locale]/(pages)/blog/page.jsx"

printf '\n## locale-aware navigation helpers and blog slug routes\n'
rg -n "locale-aware|navigation helper|Link from \"next/link\"|/blog/\$\{post\.slug\}|post\.slug|useRouter|Link" app -g '!**/*.map' || true

printf '\n## blog route files\n'
fd -a "page.jsx" app | sed 's#^\./##' | sort

Repository: Deen-Bridge/dnb-frontend

Length of output: 13572


🏁 Script executed:

#!/bin/bash
set -euo pipefail
cat -n "app/[locale]/(pages)/blog/page.jsx"

Repository: Deen-Bridge/dnb-frontend

Length of output: 7783


Keep blog detail links locale-scoped. Both href={/blog/${post.slug}} and to={/blog/${post.slug}} drop the active locale, and there isn’t a matching app/[locale]/(pages)/blog/[slug]/page.jsx route for them to land on. Build these URLs with the current locale so the title and CTA reach the localized post page.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/(pages)/blog/page.jsx at line 2, Update the blog post title and
CTA links to include the active locale when constructing each post URL, using
the locale-aware route under the blog slug page. Apply the same localized URL
construction to both the href and to link props.
app/[locale]/account/wallet/page.jsx (1)

92-122: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

RTL: wrap the wallet address and balances in dir="ltr" to match the PR's own objective.

One of this PR's goals is to keep wallet addresses, transaction hashes, and monetary values rendering left-to-right inside RTL layouts — and the shared WalletConnectButton already does exactly this (<span dir="ltr">…</span> around the truncated address and balances). On this page the address (line 92) and both balances (lines 110, 121) are unwrapped, so under /ar they'll be reordered/mirrored and become hard to read or copy. Adding dir="ltr" keeps them correct:

🌐 Suggested fix
-                  <p className="font-mono text-sm">{truncateAddress(connectedWallet)}</p>
+                  <p dir="ltr" className="font-mono text-sm">{truncateAddress(connectedWallet)}</p>
-                  <p className="text-2xl font-bold text-primary">
-                    ${parseFloat(walletInfo?.usdcBalance || 0).toFixed(2)}
-                  </p>
+                  <p dir="ltr" className="text-2xl font-bold text-primary">
+                    ${parseFloat(walletInfo?.usdcBalance || 0).toFixed(2)}
+                  </p>
-                  <p className="text-2xl font-bold">
-                    {parseFloat(walletInfo?.xlmBalance || 0).toFixed(4)} XLM
-                  </p>
+                  <p dir="ltr" className="text-2xl font-bold">
+                    {parseFloat(walletInfo?.xlmBalance || 0).toFixed(4)} XLM
+                  </p>
📝 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.

                  <p dir="ltr" className="font-mono text-sm">{truncateAddress(connectedWallet)}</p>
                </div>
                <a
                  href={getExplorerUrl(connectedWallet)}
                  target="_blank"
                  rel="noopener noreferrer"
                  className="inline-flex items-center gap-1 text-sm text-primary hover:underline"
                >
                  View on Explorer
                  <ExternalLink className="h-3 w-3" />
                </a>
              </div>

              {/* Balances */}
              <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
                <div className="p-4 border rounded-lg">
                  <p className="text-sm text-muted-foreground">USDC Balance</p>
                  <p dir="ltr" className="text-2xl font-bold text-primary">
                    ${parseFloat(walletInfo?.usdcBalance || 0).toFixed(2)}
                  </p>
                  {!walletInfo?.hasTrustline && (
                    <p className="text-xs text-orange-500 mt-1">
                      No USDC trustline. Add USDC asset to your wallet.
                    </p>
                  )}
                </div>
                <div className="p-4 border rounded-lg">
                  <p className="text-sm text-muted-foreground">XLM Balance</p>
                  <p dir="ltr" className="text-2xl font-bold">
                    {parseFloat(walletInfo?.xlmBalance || 0).toFixed(4)} XLM
                  </p>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/account/wallet/page.jsx around lines 92 - 122, Wrap the wallet
address rendered by truncateAddress(connectedWallet) and both formatted balance
values in the wallet balance section with dir="ltr" containers. Update the
relevant markup around the address, USDC balance, and XLM balance while
preserving their existing formatting and surrounding layout.
app/[locale]/dashboard/courses/[courseId]/page.jsx (1)

4-6: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Await params in these async server pages (Next.js 15 made it a Promise). In Next.js 15, dynamic APIs including params are asynchronous; synchronous property access is deprecated (currently a warning, removed in the next major) and can already break under Turbopack. Since each of these is an async server component, awaiting is a one-line fix.

  • app/[locale]/dashboard/courses/[courseId]/page.jsx#L4-L6: change to const { courseId } = await params;.
  • app/[locale]/dashboard/library/[bookid]/page.jsx#L4-L6: change to const { bookid } = await params;.
  • app/[locale]/dashboard/spaces/[spacesid]/page.jsx#L10-L11: change to const { spacesid } = await params;.
  • app/[locale]/dashboard/library/read/[bookid]/page.jsx#L5-L6: change to const { bookid } = await params;.

As per path instructions: "Flag use of deprecated patterns (synchronous params/cookies, pages-router idioms)".

📍 Affects 4 files
  • app/[locale]/dashboard/courses/[courseId]/page.jsx#L4-L6 (this comment)
  • app/[locale]/dashboard/library/[bookid]/page.jsx#L4-L6
  • app/[locale]/dashboard/spaces/[spacesid]/page.jsx#L10-L11
  • app/[locale]/dashboard/library/read/[bookid]/page.jsx#L5-L6
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/dashboard/courses/[courseId]/page.jsx around lines 4 - 6, Await
the dynamic params object before destructuring it in the async page components:
update the Page components in app/[locale]/dashboard/courses/[courseId]/page.jsx
(lines 4-6), app/[locale]/dashboard/library/[bookid]/page.jsx (lines 4-6),
app/[locale]/dashboard/spaces/[spacesid]/page.jsx (lines 10-11), and
app/[locale]/dashboard/library/read/[bookid]/page.jsx (lines 5-6) to await
params while preserving each existing identifier and downstream logic.

Source: Path instructions

app/[locale]/dashboard/library/[bookid]/BookDetailPageClient.jsx (1)

158-158: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Guard against a missing book.author here to avoid a render crash.

Everywhere else you defensively use book?.author?._id (e.g., Line 45), but this comparison dereferences book.author._id directly. If a book ever comes back without a populated author, this line throws TypeError: Cannot read properties of undefined and takes down the whole page render. Optional chaining keeps it safe.

🛡️ Suggested guard
-          {user?._id !== book.author._id && (
+          {user?._id !== book.author?._id && (
📝 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.

          {user?._id !== book.author?._id && (
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/dashboard/library/[bookid]/BookDetailPageClient.jsx at line
158, Update the author comparison in BookDetailPageClient so it safely handles
books without an author by using the same optional-chaining pattern as the
existing book?.author?._id usage, while preserving the current conditional
rendering behavior.
app/[locale]/dashboard/library/read/[bookid]/BookReaderClient.jsx (1)

19-21: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm bundler config + how the worker is wired
fd -t f 'next.config' 
rg -nP "webpack|turbopack|\?url|pdf\.worker" next.config.* 2>/dev/null
rg -nP "GlobalWorkerOptions|pdf\.worker" -g '!**/node_modules/**'
# Check installed pdfjs-dist version and worker file presence
cat package.json | jq '.dependencies["pdfjs-dist"], .devDependencies["pdfjs-dist"]'
fd -t f 'pdf.worker' node_modules/pdfjs-dist 2>/dev/null | head

Repository: Deen-Bridge/dnb-frontend

Length of output: 272


🌐 Web query:

Next.js webpack import asset with ?url query support

💡 Result:

In Next.js, importing assets with a?url query parameter—a feature common in pure Vite or Webpack projects to force an asset to be treated as a URL—is not a built-in standard pattern, as Next.js manages static assets through its own specific mechanisms [1][2]. Instead of using webpack-specific query suffixes, you should use the following approaches to handle assets as URLs or imports: 1. Recommended Approach: Static Imports For local assets (images, fonts, etc.), import the file directly. Next.js handles these imports by providing an object that contains the URL, width, and height [3][1][2]. import logo from '../assets/logo.png'; // Access the URL directly via.src Logo 2. Standard Web API: new URL(..., import.meta.url) If you need to treat an asset as a URL (e.g., for Web Workers, WebAssembly, or passing a path to a browser API), the standard, supported way in modern Next.js (including Edge environments) is to use the URL constructor [4][5][6][7]: const assetUrl = new URL('../assets/file.wasm', import.meta.url); console.log(assetUrl.pathname); // Returns the processed public path 3. Webpack Configuration (Advanced) If you specifically require the webpack-style?url behavior for custom file types or legacy integration, you must manually extend the webpack configuration in next.config.js to define an asset module rule [8][9]. However, note that with Next.js 16 defaulting to Turbopack, custom webpack configurations may need to be translated to turbopack.rules or replaced by native support [10][11]. // Example for next.config.js (Webpack only) module.exports = { webpack: (config) => { config.module.rules.push({ test: /.custom-ext$/, type: 'asset/resource', }); return config; }, }; If you are using Turbopack, you should avoid custom webpack logic and use standard imports or the new URL constructor whenever possible to ensure full compatibility [7][10].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== next.config.mjs =="
cat -n next.config.mjs | sed -n '1,220p'

echo
echo "== BookReaderClient.jsx relevant lines =="
cat -n app/[locale]/dashboard/library/read/[bookid]/BookReaderClient.jsx | sed -n '1,80p'

echo
echo "== package.json pdfjs-dist / next version =="
jq '.dependencies.next, .dependencies["pdfjs-dist"], .devDependencies.next, .devDependencies["pdfjs-dist"]' package.json

echo
echo "== pdfjs-dist worker files present? =="
fd -t f 'pdf.worker' node_modules/pdfjs-dist 2>/dev/null | sed -n '1,20p'

Repository: Deen-Bridge/dnb-frontend

Length of output: 4453


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== search for ?url and resourceQuery rules =="
rg -n --hidden --glob '!node_modules/**' '\?url|resourceQuery|asset/resource|new URL\(' .

echo
echo "== next.config.mjs file contents around webpack config =="
sed -n '1,120p' next.config.mjs

Repository: Deen-Bridge/dnb-frontend

Length of output: 1382


Use a Next-compatible worker URL pdfjs-dist/build/pdf.worker.min.js?url relies on Vite-style query handling, but this Next.js setup doesn’t add any asset rule for it. Switch to new URL(..., import.meta.url) or an explicit worker/asset rule so GlobalWorkerOptions.workerSrc resolves to a real URL and PDF rendering stays reliable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/dashboard/library/read/[bookid]/BookReaderClient.jsx around
lines 19 - 21, Update the worker source setup near GlobalWorkerOptions.workerSrc
to use a Next-compatible URL resolution approach instead of importing
pdf.worker.min.js with the Vite-style ?url query. Ensure
GlobalWorkerOptions.workerSrc receives a real emitted worker URL and preserve
the existing PDF rendering behavior.

Source: Linters/SAST tools

app/[locale]/dashboard/messages/layout.js (2)

5-12: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect routing config + confirm the locale-aware navigation helpers are exported.
fd -t f 'routing.(js|ts)' i18n 2>/dev/null -x cat -n
fd -t f 'navigation.(js|ts)' i18n 2>/dev/null -x cat -n
# See how other files consume usePathname vs the i18n helper
rg -nP "usePathname" --type=js --type=jsx -g '!node_modules' -C1

Repository: Deen-Bridge/dnb-frontend

Length of output: 645


🌐 Web query:

next-intl usePathname next/navigation locale prefix included

💡 Result:

When using the next-intl navigation API, the usePathname hook returns the pathname without the locale prefix [1]. For example, if a user is visiting /en/about, usePathname() will return /about [1]. This behavior ensures that your components remain agnostic of the locale prefix, simplifying logic for active state detection or link generation [2][1]. ### Key Details * Implementation: You must use the usePathname exported from your configured next-intl navigation file (e.g., import {usePathname} from '@/i18n/navigation'), rather than the one from next/navigation [1][3]. * Locale Prefix Handling: The next-intl wrapper handles the stripping of the prefix automatically [3]. If you are using the pathnames feature, the returned value will correspond to the internal, non-localized pathname template [1]. * Reconstruction: If you need to construct a URL that includes the locale prefix, use the getPathname function, which is also provided by the same next-intl navigation configuration [1][3]. Example usage: tsx 'use client'; import {usePathname} from '`@/i18n/navigation`'; export default function MyComponent() { const pathname = usePathname(); // e.g., "/about" return <div>Current path: {pathname}</div>; }

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the target file with line numbers.
if [ -f "app/[locale]/dashboard/messages/layout.js" ]; then
  cat -n "app/[locale]/dashboard/messages/layout.js"
fi

# Inspect the i18n navigation setup and routing one more time.
echo "--- i18n/routing.js ---"
if [ -f "i18n/routing.js" ]; then
  cat -n "i18n/routing.js"
fi

echo "--- i18n/navigation.js ---"
if [ -f "i18n/navigation.js" ]; then
  cat -n "i18n/navigation.js"
fi

Repository: Deen-Bridge/dnb-frontend

Length of output: 3171


Use the locale-aware pathname hook here Import usePathname from @/i18n/navigation instead of next/navigation so the path comparison ignores the locale prefix. With the current import, /ar/dashboard/messages won’t match "/dashboard/messages", so the sidebar/chat toggle breaks on the Arabic route.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/dashboard/messages/layout.js around lines 5 - 12, Update the
Layout component’s usePathname import to use the locale-aware hook from
`@/i18n/navigation`, preserving the existing isInChat comparison so
locale-prefixed routes are normalized before checking the dashboard messages
path.

Source: Path instructions


9-31: 🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

hasConversations is set but never read — and the fetch behind it is expensive. Nothing in the returned JSX consumes hasConversations, so this whole effect is dead weight. Worse, fetchUserConversations eagerly reads every conversation plus each conversation's entire messages subcollection (see lib/actions/messages/fetchConversations.js) just to compute a boolean that's discarded — that's a lot of Firestore reads on every mount of the messages shell.

There's also a latent hang: isLoading is only cleared inside this effect, which early-returns when user?._id is falsy, so if the id never resolves the Loader shows forever.

If the flag really is unused, drop the state and the fetch (and gate the loader on auth resolution instead). If you do need "has any conversation", prefer a lightweight count/existence query rather than hydrating all messages.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/dashboard/messages/layout.js around lines 9 - 31, Remove the
unused hasConversations state and the checkConversations effect that calls
fetchUserConversations. Update the loader logic in the layout to depend on the
auth-resolution state rather than isLoading being cleared by a user-dependent
effect, ensuring it cannot remain visible when user._id is unavailable; if the
flag is required elsewhere, replace the fetch with a lightweight conversation
existence query.
app/[locale]/dashboard/search/[searchparam]/page.jsx (2)

56-68: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Client-component params is a Promise in Next.js 15 — unwrap it with use().

Since this file is "use client", Next.js 15 passes params as a Promise, so params?.searchparam reads a non-existent property and evaluates to undefined. The useEffect then early-returns on if (!searchparam) and the search request never fires — the page always shows "No results found." Your sibling account/profile/[profileid]/page.jsx already uses the correct use(params) pattern; mirroring it fixes this.

🔧 Suggested fix
-import React, { useEffect, useState } from "react";
+import React, { use, useEffect, useState } from "react";
 ...
 const Page = ({ params }) => {
-  const searchparam = params?.searchparam || "";
+  const { searchparam = "" } = use(params);

As per path instructions: "Flag use of deprecated patterns (synchronous params/cookies, pages-router idioms)".

📝 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.

import React, { use, useEffect, useState } from "react";

const Page = ({ params }) => {
  const { searchparam = "" } = use(params);
  const [results, setResults] = useState([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    if (!searchparam) return;
    setLoading(true);
    searchQuery(searchparam)
      .then((data) => setResults(data))
      .catch(() => setResults([]))
      .finally(() => setLoading(false));
  }, [searchparam]);
🧰 Tools
🪛 ast-grep (0.44.1)

[warning] 64-64: Avoid using the initial state variable in setState
Context: setResults(data)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(setstate-same-var)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/dashboard/search/[searchparam]/page.jsx around lines 56 - 68,
Update the client component Page to unwrap the Promise-based params with React’s
use() before reading searchparam, mirroring the existing pattern in the sibling
profile page. Replace the synchronous params?.searchparam access while
preserving the current useEffect searchQuery flow and loading/result behavior.

Source: Path instructions


259-271: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

reel results will crash the page — typeLinks/typeLabels have no reel key.

The card renders a dedicated reel branch (Line 259), but typeLinks and typeLabels (Lines 9-21) only define course, book, user, space. For a reel item, typeLinks[item.type] is undefined, so typeLinks[item.type](item.id) throws TypeError: typeLinks.reel is not a function during render — and because it's inside results.map, a single reel result blanks the entire results grid. Please add a reel entry (or guard the link).

🛡️ Suggested fix (add reel mappings + guard)
 const typeLabels = {
   course: "Course",
   book: "Book",
   user: "User",
   space: "Space",
+  reel: "Reel",
 };

 const typeLinks = {
   course: (id) => `/dashboard/courses/${id}`,
   book: (id) => `/dashboard/library/${id}`,
   user: (id) => `/account/profile/${id}`,
   space: (id) => `/dashboard/spaces/${id}`,
+  reel: (id) => `/dashboard/reels/${id}`,
 };

And defensively skip the link if the type is unknown:

-                <Link
-                  href={typeLinks[item.type](item.id)}
+                {typeLinks[item.type] && (
+                <Link
+                  href={typeLinks[item.type](item.id)}
                   className="block w-full text-center px-4 py-2 rounded-full bg-accent text-white text-sm font-semibold shadow hover:bg-highlight transition-colors mt-2"
                 >
                   View {typeLabels[item.type]}
-                </Link>
+                </Link>
+                )}
📝 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.

                {item.type === "reel" && (
                  <>
                    <p className="text-sm text-muted-foreground line-clamp-2 mb-2">
                      {item.description}
                    </p>
                  </>
                )}
                {typeLinks[item.type] && (
                  <Link
                    href={typeLinks[item.type](item.id)}
                    className="block w-full text-center px-4 py-2 rounded-full bg-accent text-white text-sm font-semibold shadow hover:bg-highlight transition-colors mt-2"
                  >
                    View {typeLabels[item.type]}
                  </Link>
                )}
🧰 Tools
🪛 ast-grep (0.44.1)

[warning] 155-271: A list component should have a key to prevent re-rendering
Context:
{/* Detailed rendering by type */}
{item.type === "course" && (
<>


{item.description}



{item.price !== undefined && (

{item.price ? $${item.price} : "Free"}

)}

</>
)}
{item.type === "book" && (
<>


{item.description}



{item.category && (

{item.category}

)}
{item.price !== undefined && (

{item.price ? $${item.price} : "Free"}

)}
{item.author && (

By{" "}
{typeof item.author === "object"
? item.author.name
: item.author}

)}

</>
)}
{item.type === "user" && (
<>

{item.avatar && (
{item.name}
)}

{item.name}


{item.role && (

Role: {item.role}

)}
</>
)}
{item.type === "space" && (
<>


{item.description}



{item.status && (

{item.status.toUpperCase()}

)}
{item.price !== undefined && (

{item.price ? $${item.price} : "Free"}

)}
{item.eventDate && (

Event: {formatDate(item.eventDate)}

)}
{item.duration && (

Duration: {item.duration} min

)}
{item.host && (

Host:{" "}
{typeof item.host === "object"
? item.host.name
: item.host}

)}

</>
)}
{item.type === "reel" && (
<>


{item.description}


</>
)}

View {typeLabels[item.type]}


Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(list-component-needs-key)


[warning] 259-263: A list component should have a key to prevent re-rendering
Context: <>


{item.description}


</>
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(list-component-needs-key)


[warning] 260-262: A list component should have a key to prevent re-rendering
Context:


{item.description}


Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(list-component-needs-key)


[warning] 265-270: A list component should have a key to prevent re-rendering
Context:
View {typeLabels[item.type]}

Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(list-component-needs-key)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/dashboard/search/[searchparam]/page.jsx around lines 259 - 271,
Update the typeLinks and typeLabels mappings used by the results card to include
a reel entry matching the application’s reel destination and display label, so
the existing View link works for reel items. Also guard the link rendering
against unsupported item types before calling typeLinks[item.type](item.id),
while preserving the current mappings and rendering for known types.
app/[locale]/dashboard/spaces/[spacesid]/page.jsx (1)

43-45: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Guard space.status before .toUpperCase().

Unlike the price/host fields, space.status is dereferenced without a guard, so a space missing status will crash this server-rendered page with a TypeError (→ 500). A fallback keeps the badge resilient.

🛡️ Suggested guard
-                            <Badge variant="accent" className="bg-white/10 text-highlight">
-                                {space.status.toUpperCase()}
-                            </Badge>
+                            {space.status && (
+                              <Badge variant="accent" className="bg-white/10 text-highlight">
+                                {space.status.toUpperCase()}
+                              </Badge>
+                            )}
📝 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.

                            {space.status && (
                                <Badge variant="accent" className="bg-white/10 text-highlight">
                                    {space.status.toUpperCase()}
                                </Badge>
                            )}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/dashboard/spaces/[spacesid]/page.jsx around lines 43 - 45,
Update the status rendering in the Badge component to guard space.status before
calling toUpperCase, and render a safe fallback when status is missing. Preserve
the existing uppercase display for defined status values.
app/[locale]/profile-setup/page.jsx (1)

10-20: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== file list ==\n'
git ls-files 'app/[locale]/profile-setup/*' 'app/**/StepTwo*' 'app/**/useAuth*' 'app/**/auth*' | sed 's#^`#-` #'

printf '\n== outline page ==\n'
ast-grep outline app/[locale]/profile-setup/page.jsx --view expanded || true

printf '\n== page contents ==\n'
cat -n app/[locale]/profile-setup/page.jsx | sed -n '1,220p'

printf '\n== search for updateUser and user._id ==\n'
rg -n "updateUser\\(|user\\._id|useAuth|auth loading|loading|unauthenticated|redirect\\(" app --glob '!**/node_modules/**' || true

Repository: Deen-Bridge/dnb-frontend

Length of output: 8990


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== locate profile setup step-two and auth hook ==\n'
fd -a 'step-two.*|useAuth.*' app components src . 2>/dev/null | sed 's#^`#-` #'

printf '\n== ast outline for step-two candidates ==\n'
for f in $(fd -a 'step-two.*' app components src 2>/dev/null | head -n 5); do
  echo "--- $f"
  ast-grep outline "$f" --view expanded || true
done

printf '\n== ast outline for useAuth candidates ==\n'
for f in $(fd -a 'useAuth.*' app components src hooks 2>/dev/null | head -n 5); do
  echo "--- $f"
  ast-grep outline "$f" --view expanded || true
done

printf '\n== read step-two and hook snippets ==\n'
for f in $(fd -a 'step-two.*' app components src 2>/dev/null | head -n 5); do
  echo "--- $f"
  wc -l "$f"
  cat -n "$f" | sed -n '1,260p'
done
for f in $(fd -a 'useAuth.*' app components src hooks 2>/dev/null | head -n 5); do
  echo "--- $f"
  wc -l "$f"
  cat -n "$f" | sed -n '1,260p'
done

Repository: Deen-Bridge/dnb-frontend

Length of output: 17949


Guard profile setup until user is available.
StepTwo calls updateUser(user._id, ...), but this page renders the wizard before auth has settled. If user is still null, the final submit will throw. Use the useAuth loading/auth state to gate the wizard or disable submission until a user exists.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/profile-setup/page.jsx around lines 10 - 20, Update
ProfileSetupPage to consume useAuth and prevent the setup wizard, especially
StepTwo submission, from rendering or being usable while authentication is
loading or user is unavailable. Render the wizard only once a valid user exists,
preserving the existing step navigation and form behavior for authenticated
users.

Source: Path instructions

messages/ar.json (1)

12-13: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Keep the locale catalogs schema-compatible.

messages/en.json defines auth.dashboard, auth.login, and auth.signup, but messages/ar.json has no auth namespace. Add Arabic translations for those keys or implement the fallback described in i18n/request.js.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@messages/ar.json` around lines 12 - 13, Add the missing auth namespace to the
Arabic locale catalog, including auth.dashboard, auth.login, and auth.signup
with Arabic translations matching the English catalog schema; preserve the
existing dashboard translations and key structure.

@zeemscript

Copy link
Copy Markdown
Collaborator

Thank you for your work here, @Ayinkx. This PR has been inactive for ~3+ weeks, so we're reclaiming #104 for the current campaign — and the issue description has been substantially refreshed with a clearer, more detailed spec and acceptance criteria. Closing this PR for now so the issue can be reassigned. You're very welcome to apply for another open campaign issue, and if you'd like to pick this one back up, please reach out to the maintainers. JazakumAllahu khayran 🙏

@zeemscript zeemscript closed this Aug 17, 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.

[Enhancement] Internationalization with next-intl and full RTL Arabic support (locale routing, switcher, logical-property audit)

2 participants