perf(ci): implement repository-wide CI optimization and Nx Cloud integration#12102
perf(ci): implement repository-wide CI optimization and Nx Cloud integration#12102
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughThis PR migrates guided-learning from day-based schedules to milestone-based plans (types, data, hooks, components), adds a LearningModeSwitcher and UI/layout refinements, adds extensive tests, moves studyPlans into utilities and re-exports them, and substantially changes the GitHub Actions → Vercel deployment workflow to use prebuilt deploys with Next.js caching and frozen lockfile installs. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR connects the workspace to Nx Cloud and updates the manual parallel deployment workflow to deploy prebuilt artifacts to Vercel (building on the GitHub runner) with added caching to reduce CI/CD runtime.
Changes:
- Added
nxCloudIdtonx.jsonto enable Nx Cloud connectivity. - Updated
.github/workflows/manual-deploy-parallel.ymlto usepnpm install --frozen-lockfile, cache Next.js build cache directories, runvercel build, and deploy viavercel deploy --prebuilt. - Patched Vercel project settings in the workflow to use
pnpm --filter <app> buildand setinstallCommandtotrue.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 9 comments.
| File | Description |
|---|---|
| nx.json | Adds Nx Cloud workspace connection (nxCloudId). |
| .github/workflows/manual-deploy-parallel.yml | Switches manual deploy to local vercel build + --prebuilt deploy and introduces caching + frozen lockfile installs. |
| for attempt in 1 2 3 4 5 6; do | ||
| echo "Deploy website attempt ${attempt}/6" | ||
| deploy_log="$(mktemp)" | ||
| timeout 20m npx vercel deploy --prod --yes --token=${{ secrets.VERCEL_TOKEN }} 2>&1 | tee "$deploy_log" || true | ||
| if grep -Eq 'Failed to process project graph|Could not find ".modules.yaml"|ENOENT: no such file or directory|Error: Command "npx nx build|Error: Command "npm run build" exited with 1|Error: Command "npm install" exited with 1' "$deploy_log"; then | ||
| echo "Deploy output indicates explicit build failure; failing deployment step." | ||
| exit 1 | ||
| fi | ||
| prod_url="$(grep -Eo 'https://[^ ]+\.vercel\.app' "$deploy_log" | tail -n 1 || true)" | ||
| if [ -n "$prod_url" ]; then | ||
| echo "Deploy command produced URL: $prod_url" | ||
| inspect_log="$(mktemp)" | ||
| if timeout 12m npx vercel inspect "$prod_url" --wait --timeout 12m --token=${{ secrets.VERCEL_TOKEN }} >"$inspect_log" 2>&1; then | ||
| cat "$inspect_log" | ||
| deploy_output=$(npx vercel deploy --prebuilt --prod --yes --token=${{ secrets.VERCEL_TOKEN }}) | ||
| if [ $? -eq 0 ]; then |
There was a problem hiding this comment.
With set -euo pipefail, the assignment deploy_output=$(npx vercel deploy ...) will cause the script to exit immediately on a non-zero deploy, so the retry loop will never retry. Restructure to run the deploy in the if condition (e.g., if deploy_output=$(...); then ... else ... fi) or otherwise suppress errexit for that command so failures fall through to the retry logic.
| if [ $? -eq 0 ]; then | ||
| # Extract URL from output if possible, or use standard prod URL for aliasing | ||
| prod_url=$(echo "$deploy_output" | grep -Eo 'https://[^ ]+\.vercel\.app' | tail -n 1 || echo "https://elzatona-web.com") | ||
| echo "Admin deployment is ready in production." |
There was a problem hiding this comment.
This log line is in the website deploy job but says "Admin deployment is ready"; it should reference the website deployment to avoid confusing CI logs and on-call troubleshooting.
| echo "Admin deployment is ready in production." | |
| echo "Website deployment is ready in production." |
| # Extract URL from output if possible, or use standard prod URL for aliasing | ||
| prod_url=$(echo "$deploy_output" | grep -Eo 'https://[^ ]+\.vercel\.app' | tail -n 1 || echo "https://elzatona-web.com") | ||
| echo "Admin deployment is ready in production." | ||
| set_website_aliases "$prod_url" |
There was a problem hiding this comment.
If the deploy output parsing fails, prod_url falls back to https://elzatona-web.com, but vercel alias set expects a deployment URL/id (not an existing alias/domain). This will likely make aliasing fail or point aliases at the wrong target. Better to fail fast when no deployment URL can be extracted (or query the deployment via vercel inspect/vercel ls to retrieve the actual deployment URL) before calling set_website_aliases.
| with: | ||
| path: | | ||
| apps/website/.next/cache | ||
| key: ${{ runner.os }}-website-next-cache-${{ github.sha }} |
There was a problem hiding this comment.
The Next.js cache key is based on github.sha, which effectively creates a new cache entry every commit and reduces cache hit rates across PR updates (restore-keys helps, but you still churn caches). For better CI performance and lower cache storage churn, consider keying on the lockfile (e.g., hashFiles('pnpm-lock.yaml')) plus an app identifier, and optionally include a Next.js/version component if needed.
| key: ${{ runner.os }}-website-next-cache-${{ github.sha }} | |
| key: ${{ runner.os }}-website-next-cache-${{ hashFiles('pnpm-lock.yaml') }} |
| key: ${{ runner.os }}-next-cache-${{ github.sha }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-next-cache- |
There was a problem hiding this comment.
The admin Next.js cache key is also tied to github.sha, which causes frequent cache misses/churn. Consider using a lockfile-based key (e.g., hashFiles('pnpm-lock.yaml')) with an admin prefix so the cache is reusable across commits while still invalidating when deps change.
| key: ${{ runner.os }}-next-cache-${{ github.sha }} | |
| restore-keys: | | |
| ${{ runner.os }}-next-cache- | |
| key: ${{ runner.os }}-admin-next-cache-${{ hashFiles('pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-admin-next-cache- |
| -H "Authorization: Bearer ${{ secrets.VERCEL_TOKEN }}" \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"buildCommand":"npm run build","outputDirectory":"apps/website/.next"}' \ | ||
| -d '{"buildCommand":"pnpm --filter website build","outputDirectory":"apps/website/.next","installCommand":"true"}' \ |
There was a problem hiding this comment.
Patching Vercel project settings to set installCommand to true will globally change the project behavior on Vercel (including dashboard/preview builds), not just this workflow. Since --prebuilt deployments don’t need Vercel to install dependencies anyway, consider avoiding changing installCommand (or restoring the previous value at the end) to prevent unexpected breakages for non-GitHub-Actions deployments.
| -d '{"buildCommand":"pnpm --filter website build","outputDirectory":"apps/website/.next","installCommand":"true"}' \ | |
| -d '{"buildCommand":"pnpm --filter website build","outputDirectory":"apps/website/.next"}' \ |
| -H "Authorization: Bearer ${{ secrets.VERCEL_TOKEN }}" \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"buildCommand":"npm run build","outputDirectory":".next"}' \ | ||
| -d '{"buildCommand":"pnpm --filter admin build","outputDirectory":"apps/admin/.next","installCommand":"true"}' \ |
There was a problem hiding this comment.
Same concern for the admin project: setting installCommand to true via the API PATCH changes the project’s global Vercel configuration and may break preview/dashboard builds that expect installs. Consider not mutating installCommand here (or resetting it), and rely on the local vercel build + vercel deploy --prebuilt path for this workflow.
| -d '{"buildCommand":"pnpm --filter admin build","outputDirectory":"apps/admin/.next","installCommand":"true"}' \ | |
| -d '{"buildCommand":"pnpm --filter admin build","outputDirectory":"apps/admin/.next"}' \ |
| - name: Validate and build (Local Vercel Build) | ||
| if: ${{ inputs.validate_build_admin }} | ||
| run: pnpm --dir apps/admin run build | ||
| run: | | ||
| npx vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }} | ||
| npx vercel build --prod --token=${{ secrets.VERCEL_TOKEN }} | ||
| env: |
There was a problem hiding this comment.
vercel deploy --prebuilt in the admin job also depends on .vercel/output from vercel build, but the build step is conditional (if: inputs.validate_build_admin). If validation is turned off, the deploy will fail on a clean runner. Consider always running vercel pull + vercel build when using --prebuilt, or falling back to a standard vercel deploy when skipping the build.
| - name: Validate and build (Local Vercel Build) | ||
| if: ${{ inputs.validate_build_website }} | ||
| run: pnpm --dir apps/website run build | ||
| run: | | ||
| npx vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }} | ||
| npx vercel build --prod --token=${{ secrets.VERCEL_TOKEN }} | ||
| env: |
There was a problem hiding this comment.
vercel deploy --prebuilt requires a locally generated .vercel/output (from vercel build). Right now the build step is conditional (if: inputs.validate_build_website), so disabling validation will make the deploy step fail on a fresh runner because there is no prebuilt output to upload. Consider always running vercel pull + vercel build (and keep an input only for skipping extra checks, not the build itself), or switch back to non-prebuilt deploy when validation is disabled.
- Overhauled landing page aesthetics with glassmorphism and premium typography - Refactored Guided Learning from day-based to modular milestone-based logic - Updated StudyPlan data structures and hooks for flexible progress tracking - Implemented Vitest unit tests and Playwright E2E verification suite - Resolved build issues and optimized test-specific CI scripts - Standardized husky pre-commit hook to use npm
There was a problem hiding this comment.
Actionable comments posted: 19
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (8)
apps/website/src/app/features/guided-learning/components/PlanSelectionView.tsx (1)
1-111:⚠️ Potential issue | 🟡 MinorResolve Prettier formatting issue flagged by CI.
The pipeline reports a code style issue detected by
format:check:tracked. Run the project's formatting command (typicallypnpm formatornpm run format) to auto-fix before merging.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/website/src/app/features/guided-learning/components/PlanSelectionView.tsx` around lines 1 - 111, Prettier formatting failed for the PlanSelectionView component; run the repository formatter (e.g., pnpm format or npm run format) and commit the auto-formatted changes so the file containing the PlanSelectionView function and its JSX (including LearningPlanCard usage and the completedPlans block) matches the project's style; re-run format:check:tracked (or CI) to confirm the formatting issue is resolved before pushing.package.json (1)
1-170:⚠️ Potential issue | 🟡 MinorCI format:check is failing across 15 files — run
pnpm run format.The CI quality gate is red on Prettier. Please run
pnpm run formatand commit the diff before requesting review again.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@package.json` around lines 1 - 170, CI is failing Prettier formatting across 15 files; run the repository formatting script and commit the changes to satisfy the quality gate: execute the existing npm script "format" (pnpm run format) to apply Prettier fixes, review the changed files, stage and commit the resulting diffs, then push the commit so "format:check:tracked" / "format" checks in CI will pass.apps/website/tests/config/playwright.config.ts (1)
203-215:⚠️ Potential issue | 🟡 MinorStale comment — references
dev:light:testbut command now runsdev:website:test.Line 203's comment still says "Use dev:light:test to ensure Next.js loads .env.test.local for test database", but the command on line 205 is
npm run dev:website:test. Also, since npm resolves toapps/website/package.json'sdev:website:test(a barenext dev --port 3000), the.env.test.localloading behavior depends entirely on Next.js's built-inNODE_ENV=testcascade — not ondotenv-cli. Update the comment to match reality, and see my note onapps/website/package.jsonline 7 for the deeper divergence.🛠️ Suggested comment update
- // Use dev:light:test to ensure Next.js loads .env.test.local for test database + // Use dev:website:test on port 3000; Next.js loads .env.test.local via NODE_ENV=test cascade command: "npm run dev:website:test", // Use light mode for 8GB RAM with test environment🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/website/tests/config/playwright.config.ts` around lines 203 - 215, Update the stale comment to accurately describe the current command and behavior: replace the "dev:light:test" reference with the actual command "npm run dev:website:test" and note that Next.js will load .env.test.local via the NODE_ENV/NEXT_PUBLIC_APP_ENV set to "test" (not via dotenv-cli); reference the command string "npm run dev:website:test" and the env keys APP_ENV, NEXT_PUBLIC_APP_ENV, NODE_ENV so readers know why the test DB is picked up..github/workflows/manual-deploy-parallel.yml (1)
123-141:⚠️ Potential issue | 🔴 Critical🐛 Retry loop is broken:
set -e+ command substitution will short-circuit retries, plus a copy-paste log message.Three issues packed into this website deploy loop:
- Retries never happen on failure.
set -euo pipefailcombined withdeploy_output=$(npx vercel deploy ...)makes the whole step exit the momentvercel deployreturns non-zero — the subsequentif [ $? -eq 0 ]check and the retry logic below become unreachable. You need to execute the command in anifcondition (or append|| true) soset -edoesn't abort.- Stdout is captured, so users won't see Vercel's progress live in the Actions UI — only after the command completes (or never, if it fails silently as above).
- "Admin deployment is ready in production." is logged inside the website job (line 129) — copy-paste from the admin job.
🛠️ Proposed fix
for attempt in 1 2 3 4 5 6; do echo "Deploy website attempt ${attempt}/6" - deploy_output=$(npx vercel deploy --prebuilt --prod --yes --token=${{ secrets.VERCEL_TOKEN }}) - if [ $? -eq 0 ]; then - # Extract URL from output if possible, or use standard prod URL for aliasing - prod_url=$(echo "$deploy_output" | grep -Eo 'https://[^ ]+\.vercel\.app' | tail -n 1 || echo "https://elzatona-web.com") - echo "Admin deployment is ready in production." - set_website_aliases "$prod_url" - exit 0 - fi + deploy_log="$(mktemp)" + if npx vercel deploy --prebuilt --prod --yes --token=${{ secrets.VERCEL_TOKEN }} 2>&1 | tee "$deploy_log"; then + prod_url=$(grep -Eo 'https://[^ ]+\.vercel\.app' "$deploy_log" | tail -n 1 || echo "https://elzatona-web.com") + echo "Website deployment is ready in production: ${prod_url}" + set_website_aliases "$prod_url" + exit 0 + fiNote:
pipe + teeworks becausepipefailis set — the overall pipeline's status reflectsvercel deploy's exit code.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/manual-deploy-parallel.yml around lines 123 - 141, The deploy loop currently aborts because command substitution with set -e causes the step to exit on non-zero and it also hides live output and logs the wrong message; change the loop so the vercel command is executed in a conditional (e.g. if npx vercel ...; then) or run it piped through tee while still checking its exit status so set -e doesn't short-circuit (preserve capturing into deploy_output while streaming stdout), ensure the exit check uses that conditional rather than testing $?, and update the success echo from "Admin deployment is ready in production." to "Website deployment is ready in production." before calling set_website_aliases with the extracted prod_url.apps/website/package.json (1)
1-33:⚠️ Potential issue | 🟡 MinorCI format:check is failing — run Prettier.
Pipeline reports 15 files with Prettier issues. Please run
pnpm run formatat the repo root before pushing.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/website/package.json` around lines 1 - 33, CI failed due to Prettier formatting errors; run the repository formatter and commit the fixes. From the repo root run the project's format command (pnpm run format) or execute a Prettier write (e.g., pnpm -w prettier --write .) to fix the 15 files, then stage and push the changed files; also add a consistent "format" script to the root package.json if missing so future contributors can run pnpm run format easily (note: the apps/website/package.json currently defines "scripts" but no local "format" script).apps/website/src/app/features/guided-learning/components/GuidedLearningHeader.tsx (1)
5-12:⚠️ Potential issue | 🔴 CriticalRemove the stale required
daysRangeprop.The component no longer uses
daysRange, andGuidedLearningPageno longer passes it. Keeping it required will break the updated call site.Proposed fix
interface GuidedLearningHeaderProps { readonly questionsRange: string; - readonly daysRange: string; } export function GuidedLearningHeader({ questionsRange, }: Readonly<GuidedLearningHeaderProps>) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/website/src/app/features/guided-learning/components/GuidedLearningHeader.tsx` around lines 5 - 12, Remove the stale daysRange prop from the component props: delete daysRange from the GuidedLearningHeaderProps interface and remove it from the GuidedLearningHeader parameter destructuring so the function only takes questionsRange (update the Readonly<GuidedLearningHeaderProps> usage accordingly); also scan for any internal references to daysRange inside GuidedLearningHeader and remove or replace them so the component compiles without that prop.apps/website/src/app/features/guided-learning/page.tsx (1)
25-31:⚠️ Potential issue | 🟠 MajorRemove the unused
milestoneRangebinding.Line 30 destructures
milestoneRange, but the page no longer passes or renders it.Proposed fix
const { plans, isLoading: plansLoading, error, questionsRange, - milestoneRange, } = useGuidedLearningPlans();As per coding guidelines,
No unused variables in TypeScript or explicit disable with reason.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/website/src/app/features/guided-learning/page.tsx` around lines 25 - 31, The destructured binding milestoneRange from useGuidedLearningPlans is unused; remove milestoneRange from the destructuring in page.tsx so the hook return is only assigned to plans, isLoading (plansLoading), error, and questionsRange. Locate the const { ... } = useGuidedLearningPlans() statement and delete the milestoneRange identifier (or if you prefer, replace the full destructure with an explicit list excluding milestoneRange) to satisfy the "no unused variables" rule.apps/website/src/app/features/guided-learning/hooks/useActivePlan.ts (1)
26-39:⚠️ Potential issue | 🟠 MajorValidate migrated localStorage before restoring milestone state.
A legacy
active-guided-plancan parse but have nomilestones, andplan-progress-${plan.id}can point to a removed milestone. In both cases this hook restores an invalid active plan instead of clearing or falling back.Suggested fix
- const stored = localStorage.getItem("active-guided-plan"); - if (stored) { - try { - const plan = JSON.parse(stored) as LearningPlan; - setCurrentPlan(plan); - setMilestones(plan.milestones || []); - - // Track current milestone from storage or default to first - const storedMilestone = localStorage.getItem(`plan-progress-${plan.id}`); - if (storedMilestone) { - setCurrentMilestoneId(storedMilestone); - } else if (plan.milestones && plan.milestones.length > 0) { - setCurrentMilestoneId(plan.milestones[0].id); - } - } catch { - console.warn("Failed to parse active guided plan from localStorage"); - localStorage.removeItem("active-guided-plan"); - } - } + try { + const stored = localStorage.getItem("active-guided-plan"); + if (!stored) return; + + const parsedPlan = JSON.parse(stored) as Partial<LearningPlan>; + if ( + typeof parsedPlan.id !== "string" || + !Array.isArray(parsedPlan.milestones) + ) { + localStorage.removeItem("active-guided-plan"); + return; + } + + const plan = parsedPlan as LearningPlan; + const storedMilestone = localStorage.getItem(`plan-progress-${plan.id}`); + const nextMilestoneId = + storedMilestone && + plan.milestones.some((milestone) => milestone.id === storedMilestone) + ? storedMilestone + : plan.milestones[0]?.id ?? null; + + setCurrentPlan(plan); + setMilestones(plan.milestones); + setCurrentMilestoneId(nextMilestoneId); + } catch (error) { + console.warn("Failed to restore active guided plan from localStorage", error); + localStorage.removeItem("active-guided-plan"); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/website/src/app/features/guided-learning/hooks/useActivePlan.ts` around lines 26 - 39, The hook currently restores a parsed legacy plan without validating milestones or the saved milestone id; update useActivePlan to verify that plan.milestones is a non-empty array and that any stored milestone id (from localStorage.getItem(`plan-progress-${plan.id}`)) exists in plan.milestones before calling setCurrentPlan, setMilestones, and setCurrentMilestoneId; if plan.milestones is missing/empty or the stored milestone id is not found, clear the invalid keys from localStorage (remove "active-guided-plan" and `plan-progress-${plan.id}`) and call setCurrentPlan(null)/setMilestones([])/setCurrentMilestoneId(null) or, if milestones exist but no stored id, fallback to plan.milestones[0].id; use explicit checks (Array.isArray(plan.milestones), find by id) around setCurrentPlan, setMilestones, and setCurrentMilestoneId to locate this logic in useActivePlan.
🧹 Nitpick comments (1)
apps/website/src/app/lib/studyPlans.test.ts (1)
15-18: Use@ts-expect-errorso the legacy-field checks keep guarding the type.
@ts-ignorewill keep passing even ifscheduleorestimatedTimePerDayare reintroduced.@ts-expect-errormakes that regression visible.Proposed fix
- // `@ts-ignore` - checking that legacy schedule is gone + // `@ts-expect-error` - checking that legacy schedule is gone expect(plan?.schedule).toBeUndefined(); @@ - // `@ts-ignore` + // `@ts-expect-error` - checking that legacy estimatedTimePerDay is gone expect(plan?.estimatedTimePerDay).toBeUndefined();Also applies to: 33-37
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/website/src/app/lib/studyPlans.test.ts` around lines 15 - 18, Replace the non-asserting TypeScript ignores in the tests with assertions so regressions surface: in apps/website/src/app/lib/studyPlans.test.ts, change the two occurrences that use "@ts-ignore" before checking legacy fields on the result of getStudyPlanById("one-week-intensive") (the assertions expecting plan?.schedule toBeUndefined and the later check for plan?.estimatedTimePerDay) to "@ts-expect-error" so the TypeScript compiler fails if those legacy fields are ever reintroduced.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/manual-deploy-parallel.yml:
- Around line 125-130: The fallback prod_url currently set from deploy_output
uses the alias domain ("https://elzatona-web.com"), which can cause vercel alias
to no-op or fail; update the deployment logic around deploy_output/prod_url (the
block that runs `npx vercel deploy --prebuilt --prod --yes --token=...` and then
calls set_website_aliases) so that when the grep extraction fails you do not
default to the alias—either fail loudly (exit with non-zero and log the full
deploy_output) or replace the fallback with a robust lookup (use `vercel ls
--prod` or `vercel inspect` to retrieve the latest deployment URL) and only call
set_website_aliases with a real deployment URL; ensure deploy_output, prod_url,
and set_website_aliases are updated accordingly.
- Around line 178-185: The cache key for the "Setup Vercel and Next.js Caching"
step is too generic and may collide with other jobs; update the cache key and
restore-keys to include an "admin" scope (e.g., change ${{ runner.os
}}-next-cache-${{ github.sha }} and ${{ runner.os }}-next-cache- to include
"admin" such as ${{ runner.os }}-admin-next-cache-${{ github.sha }} and ${{
runner.os }}-admin-next-cache-) so this job's cache is uniquely namespaced to
admin.
- Around line 94-107: The deploy uses vercel deploy --prebuilt which requires
.vercel/output produced by the build steps gated by inputs
validate_build_website/validate_build_admin; make those inputs non-optional or
add a pre-deploy guard in the deploy job to fail if .vercel/output is missing
(e.g., check for the directory and exit with error) so deploy never runs with a
stale/missing .vercel/output; update the workflow so either
validate_build_website and validate_build_admin default to true or include an
explicit conditional/step before vercel deploy --prebuilt that verifies
.vercel/output exists and fails fast if it does not.
- Around line 88-92: Update the JSON payload in the curl PATCH request that sets
Vercel project settings: replace the current "installCommand":"true" with the
recommended pattern ("installCommand": null) or an empty string
("installCommand": "") in the -d '{"buildCommand":"pnpm --filter website
build","outputDirectory":"apps/website/.next","installCommand":"true"}' payload
so the API explicitly skips install per Vercel docs; ensure the modified payload
preserves buildCommand and outputDirectory exactly as shown.
In `@apps/website/package.json`:
- Line 7: The app-level npm script "dev:website:test" diverges from the root
script and prevents .env.test.local and NODE_OPTIONS from being applied; either
remove this app-level "dev:website:test" so Playwright's npm run resolves to the
root script, or change the app-level "dev:website:test" to match the root
behavior by invoking dotenv to load .env.test.local and setting
NODE_OPTIONS=--max-old-space-size=1536 (so Playwright's webServer.command: "npm
run dev:website:test" and the comment in playwright.config.ts remain correct).
Ensure you update the script named dev:website:test and keep Playwright's
webServer.command and the env-loading semantics consistent.
In `@apps/website/src/app/features/guided-learning/components/ActivePlanView.tsx`:
- Around line 29-35: The current calculation of currentMilestoneIndex (using
milestones.findIndex(...) + 1) can produce 1/0 when milestones is empty; change
the logic to guard the empty state by checking totalMilestones (or
milestones.length) first and set currentMilestoneIndex to 0 or null when there
are no milestones, update any rendering logic that uses currentMilestoneIndex
(and the related display code around lines 94-96) to show an empty/placeholder
state (e.g., “—” or “0/0”) instead of "1/0", keeping the existing variables
completedMilestones, totalMilestones, progressPercent and currentMilestoneId to
locate and adjust the code paths.
- Around line 47-49: The JSX currently falls back to plan.name which doesn't
exist on the LearningPlan/StudyPlan type; update the rendering in ActivePlanView
to use only plan.title (remove the || plan.name fallback) and ensure any
references to plan.name in the ActivePlanView component are eliminated so the
code compiles against the StudyPlan/LearningPlan types.
In
`@apps/website/src/app/features/guided-learning/components/LearningPlanCard.tsx`:
- Around line 63-65: Remove the nonexistent fallback to plan.name and use
plan.title only: in LearningPlanCard (the h3 rendering that currently uses
{plan.title || plan.name}) replace that expression with just plan.title, and do
the same fix in ActivePlanView.tsx where a similar expression appears (around
the heading/render at line ~48) so code conforms to the LearningPlan/StudyPlan
type which guarantees title is present.
In
`@apps/website/src/app/features/guided-learning/types/guided-learning.types.ts`:
- Around line 1-6: The LearningPlan interface references a non-existent type
LearningPlanSection via the sections field; remove the legacy sections?:
LearningPlanSection[] property from the LearningPlan interface (which already
extends StudyPlan and therefore has milestones: StudyMilestone[]) to fix the
TypeScript compile error and rely on StudyPlan.milestones instead.
In `@apps/website/src/app/features/guided-learning/utils/plan-helpers.ts`:
- Around line 71-73: Update the type signature of getMilestoneRange so the
parameter uses unknown[] instead of any[]: change plans: { milestones?: any[]
}[] to plans: { milestones?: unknown[] }[] in the getMilestoneRange function
declaration; ensure no other references rely on any-specific operations (the
function only reads .length, so no runtime changes needed).
In `@apps/website/src/app/lib/network/routes/guided-learning/plans/route.ts`:
- Around line 46-75: Ensure the hardcoded fallback is always used first and that
Supabase errors never cause a 500: initialize plans from hardcodedPlans
immediately (keep plans = [...hardcodedPlans]) and acquire the client with
getSupabaseClient/getSupabaseClientWithAnonKey inside a try/catch that only logs
and returns plans on failure instead of throwing. When fetching from supabase
(.from("learning_plans").select(...)), wrap the fetch in try/catch and validate
each dbPlans item before merging by checking it has the new shape (e.g., a
milestones array and expected fields, and does NOT have legacy schedule), filter
out invalid/legacy rows, dedupe by id (use hardcodedIds = new Set(plans.map(p =>
p.id))) and then append only validated db plans; ensure any errors are caught
and logged so the route still returns the hardcoded plans.
In `@apps/website/src/app/lib/studyPlans.ts`:
- Around line 597-602: The milestone object with id "long-m3" uses the slug
"performance-optimization" in its topics array; update that entry to the
human-readable topic title that matches StudyTopic.title (e.g., "Performance
Optimization") so it consistently matches other milestones and consumers
expecting StudyTopic.title; modify the topics array on the "long-m3" object to
replace the slug with the correct title string.
In `@docs/admin/deployment-troubleshooting.md`:
- Around line 1-32: Remove the trailing space in the document (the extra space
after "Vercel." in the "Silent Build Failures on Vercel" section) and reformat
the file with Prettier; run `pnpm run format` or `prettier --write
docs/admin/deployment-troubleshooting.md` and commit the formatted file so the
CI Prettier check passes.
In `@libs/common-ui/src/common/LearningModeSwitcher.tsx`:
- Around line 21-37: Remove the trailing spaces after the opening JSX tags that
are causing Prettier format failures in LearningModeSwitcher.tsx (notably the
first container <div and the inner active-pill <div> around the
isScrolled/isGuided logic), then reformat the file with the repo Prettier
settings (run pnpm prettier --write or your project's formatter command) so
indentation and spacing match the rest of libs/common-ui; ensure no extra spaces
exist after tag names and that the JSX attributes follow the project's 2-space
indentation style.
- Around line 40-63: The two toggle buttons in LearningModeSwitcher lack ARIA
semantics; update the component so the container wrapping the buttons (around
the Compass/Map buttons) uses role="group" with an aria-label (e.g., "Learning
mode") and add aria-pressed to each button driven by isGuided
(aria-pressed={isGuided} on the "Guided" button and aria-pressed={!isGuided} on
the "Free Style" button); keep the existing onClick handlers (setUserType) and
visible labels but ensure buttons have these ARIA attributes so screen readers
can announce the active state.
In `@libs/common-ui/src/components/molecules/HeroSection.tsx`:
- Around line 23-80: Prettier formatting failed for HeroSection.tsx; run the
repo formatter and commit the changes: run pnpm prettier --write (or your
monorepo's nx format:write) targeting
libs/common-ui/src/components/molecules/HeroSection.tsx (or run the global
format script), then stage and push the updated file so the CI
`format:check:tracked` passes; no code changes to AnimatedTitle, CTAButton,
UserStatistics, showAnimation, or personalizedContent logic are required—only
apply and commit the formatting fixes.
- Around line 57-66: The outer wrapper's anonymous "group" lets Tailwind's
group-hover variants trigger on both the glow and the CTAButton, causing the
shine to activate when hovering the blur; fix by converting the wrapper to a
named group (e.g., add a class like "group-wrapper" and use
"group-wrapper-hover:" scoped variants on the blur) and ensure the CTAButton
itself uses its own distinct named group (e.g., "group-cta") so its group-hover
styles only fire when hovering the button; also add pointer-events-none to the
glow element (the absolute -inset-1 bg-gradient... div) so hovering the halo
doesn't pass pointer events to the button's hover state.
In `@libs/common-ui/src/components/organisms/HomePageLayout.tsx`:
- Around line 38-78: Prettier formatting failed for the changed component; run
the project's formatter and re-stage the changes so the file containing the
HomePageLayout component is formatted according to the repo rules (fix
whitespace/linebreaks in the JSX wrapper and props), then re-run the pipeline;
ensure you run the same script invoked by format:check:tracked and commit the
formatted result.
In `@libs/types/src/lib/studyPlans.ts`:
- Around line 15-18: The StudyPlan type now requires milestones:
StudyMilestone[] and estimatedTotalTime: number, but the study plan data in the
utilities module still uses the legacy schedule field; update the data source
(the StudyPlan[] objects in libs/utilities/src/lib/studyPlans.ts) to either
replace schedule with a proper milestones array (each item matching
StudyMilestone) and add a numeric estimatedTotalTime for each plan, or add a
migration step that converts existing schedule -> milestones and computes
estimatedTotalTime before exporting the StudyPlan[]; ensure no objects keep the
old schedule field so strict type-checking against StudyPlan, StudyMilestone,
milestones and estimatedTotalTime passes.
---
Outside diff comments:
In @.github/workflows/manual-deploy-parallel.yml:
- Around line 123-141: The deploy loop currently aborts because command
substitution with set -e causes the step to exit on non-zero and it also hides
live output and logs the wrong message; change the loop so the vercel command is
executed in a conditional (e.g. if npx vercel ...; then) or run it piped through
tee while still checking its exit status so set -e doesn't short-circuit
(preserve capturing into deploy_output while streaming stdout), ensure the exit
check uses that conditional rather than testing $?, and update the success echo
from "Admin deployment is ready in production." to "Website deployment is ready
in production." before calling set_website_aliases with the extracted prod_url.
In `@apps/website/package.json`:
- Around line 1-33: CI failed due to Prettier formatting errors; run the
repository formatter and commit the fixes. From the repo root run the project's
format command (pnpm run format) or execute a Prettier write (e.g., pnpm -w
prettier --write .) to fix the 15 files, then stage and push the changed files;
also add a consistent "format" script to the root package.json if missing so
future contributors can run pnpm run format easily (note: the
apps/website/package.json currently defines "scripts" but no local "format"
script).
In
`@apps/website/src/app/features/guided-learning/components/GuidedLearningHeader.tsx`:
- Around line 5-12: Remove the stale daysRange prop from the component props:
delete daysRange from the GuidedLearningHeaderProps interface and remove it from
the GuidedLearningHeader parameter destructuring so the function only takes
questionsRange (update the Readonly<GuidedLearningHeaderProps> usage
accordingly); also scan for any internal references to daysRange inside
GuidedLearningHeader and remove or replace them so the component compiles
without that prop.
In
`@apps/website/src/app/features/guided-learning/components/PlanSelectionView.tsx`:
- Around line 1-111: Prettier formatting failed for the PlanSelectionView
component; run the repository formatter (e.g., pnpm format or npm run format)
and commit the auto-formatted changes so the file containing the
PlanSelectionView function and its JSX (including LearningPlanCard usage and the
completedPlans block) matches the project's style; re-run format:check:tracked
(or CI) to confirm the formatting issue is resolved before pushing.
In `@apps/website/src/app/features/guided-learning/hooks/useActivePlan.ts`:
- Around line 26-39: The hook currently restores a parsed legacy plan without
validating milestones or the saved milestone id; update useActivePlan to verify
that plan.milestones is a non-empty array and that any stored milestone id (from
localStorage.getItem(`plan-progress-${plan.id}`)) exists in plan.milestones
before calling setCurrentPlan, setMilestones, and setCurrentMilestoneId; if
plan.milestones is missing/empty or the stored milestone id is not found, clear
the invalid keys from localStorage (remove "active-guided-plan" and
`plan-progress-${plan.id}`) and call
setCurrentPlan(null)/setMilestones([])/setCurrentMilestoneId(null) or, if
milestones exist but no stored id, fallback to plan.milestones[0].id; use
explicit checks (Array.isArray(plan.milestones), find by id) around
setCurrentPlan, setMilestones, and setCurrentMilestoneId to locate this logic in
useActivePlan.
In `@apps/website/src/app/features/guided-learning/page.tsx`:
- Around line 25-31: The destructured binding milestoneRange from
useGuidedLearningPlans is unused; remove milestoneRange from the destructuring
in page.tsx so the hook return is only assigned to plans, isLoading
(plansLoading), error, and questionsRange. Locate the const { ... } =
useGuidedLearningPlans() statement and delete the milestoneRange identifier (or
if you prefer, replace the full destructure with an explicit list excluding
milestoneRange) to satisfy the "no unused variables" rule.
In `@apps/website/tests/config/playwright.config.ts`:
- Around line 203-215: Update the stale comment to accurately describe the
current command and behavior: replace the "dev:light:test" reference with the
actual command "npm run dev:website:test" and note that Next.js will load
.env.test.local via the NODE_ENV/NEXT_PUBLIC_APP_ENV set to "test" (not via
dotenv-cli); reference the command string "npm run dev:website:test" and the env
keys APP_ENV, NEXT_PUBLIC_APP_ENV, NODE_ENV so readers know why the test DB is
picked up.
In `@package.json`:
- Around line 1-170: CI is failing Prettier formatting across 15 files; run the
repository formatting script and commit the changes to satisfy the quality gate:
execute the existing npm script "format" (pnpm run format) to apply Prettier
fixes, review the changed files, stage and commit the resulting diffs, then push
the commit so "format:check:tracked" / "format" checks in CI will pass.
---
Nitpick comments:
In `@apps/website/src/app/lib/studyPlans.test.ts`:
- Around line 15-18: Replace the non-asserting TypeScript ignores in the tests
with assertions so regressions surface: in
apps/website/src/app/lib/studyPlans.test.ts, change the two occurrences that use
"@ts-ignore" before checking legacy fields on the result of
getStudyPlanById("one-week-intensive") (the assertions expecting plan?.schedule
toBeUndefined and the later check for plan?.estimatedTimePerDay) to
"@ts-expect-error" so the TypeScript compiler fails if those legacy fields are
ever reintroduced.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 826da84f-2b72-4832-9b58-97e3892770b3
📒 Files selected for processing (27)
.github/workflows/manual-deploy-parallel.yml.husky/pre-commitapps/website/package.jsonapps/website/src/app/features/guided-learning/components/ActivePlanView.tsxapps/website/src/app/features/guided-learning/components/GuidedLearningHeader.tsxapps/website/src/app/features/guided-learning/components/LearningPlanCard.tsxapps/website/src/app/features/guided-learning/components/PlanSelectionView.tsxapps/website/src/app/features/guided-learning/hooks/useActivePlan.tsapps/website/src/app/features/guided-learning/hooks/useGuidedLearningPlans.tsapps/website/src/app/features/guided-learning/page.tsxapps/website/src/app/features/guided-learning/types/guided-learning.types.tsapps/website/src/app/features/guided-learning/utils/daily-goals.tsapps/website/src/app/features/guided-learning/utils/index.tsapps/website/src/app/features/guided-learning/utils/plan-helpers.tsapps/website/src/app/layout.tsxapps/website/src/app/lib/network/routes/guided-learning/plans/route.tsapps/website/src/app/lib/studyPlans.test.tsapps/website/src/app/lib/studyPlans.tsapps/website/tests/config/playwright.config.tsdocs/admin/deployment-troubleshooting.mdlibs/common-ui/src/common/LearningModeSwitcher.tsxlibs/common-ui/src/common/NavbarSimple.tsxlibs/common-ui/src/components/molecules/HeroSection.tsxlibs/common-ui/src/components/organisms/HomePageLayout.tsxlibs/types/src/lib/studyPlans.tsnx.jsonpackage.json
💤 Files with no reviewable changes (2)
- apps/website/src/app/features/guided-learning/utils/index.ts
- apps/website/src/app/features/guided-learning/utils/daily-goals.ts
| curl -fsS -X PATCH \ | ||
| -H "Authorization: Bearer ${{ secrets.VERCEL_TOKEN }}" \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"buildCommand":"npm run build","outputDirectory":"apps/website/.next"}' \ | ||
| -d '{"buildCommand":"pnpm --filter website build","outputDirectory":"apps/website/.next","installCommand":"true"}' \ | ||
| "https://api.vercel.com/v9/projects/${{ secrets.VERCEL_PROJECT_ID_WEBSITE }}" |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Vercel project settings installCommand skip install with prebuilt deploy
💡 Result:
To skip the install step in Vercel project settings for a prebuilt deployment, use the Install Command override in the Build & Development Settings. In the Vercel dashboard: 1. Go to your Project Settings > Build & Development. 2. Enable the Override toggle for Install Command. 3. Leave the Install Command field empty. This skips the install step entirely, similar to how leaving the Build Command empty skips the build step for static projects. For prebuilt deployments via CLI: - Run vercel build locally to generate .vercel/output (this may run install unless configured otherwise). - Then vercel deploy --prebuilt to upload prebuilt artifacts without re-running build/install on Vercel servers. In vercel.json, set "installCommand": null or "" (type allows null, likely skips). Note: Prebuilt deployments skip the build process on Vercel (including install), using local build output. For Git deployments with prebuilt assets already in repo (e.g., static site), empty installCommand + empty buildCommand + set Output Directory serves directly without install/build.
Citations:
- 1: https://vercel.com/docs/builds/configure-a-build
- 2: https://vercel.com/docs/project-configuration/vercel-json
- 3: https://vercel.com/docs/cli/build
- 4: https://www.vercel.com/docs/cli/deploy
Use null or empty string for installCommand instead of "true".
Vercel's documented pattern for skipping install is "installCommand": null or "installCommand": "". The current "true" works incidentally (as a shell no-op) but deviates from the recommended configuration and could become fragile if Vercel changes how it handles unrecognized commands. Update to match Vercel's API expectations.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/manual-deploy-parallel.yml around lines 88 - 92, Update
the JSON payload in the curl PATCH request that sets Vercel project settings:
replace the current "installCommand":"true" with the recommended pattern
("installCommand": null) or an empty string ("installCommand": "") in the -d
'{"buildCommand":"pnpm --filter website
build","outputDirectory":"apps/website/.next","installCommand":"true"}' payload
so the API explicitly skips install per Vercel docs; ensure the modified payload
preserves buildCommand and outputDirectory exactly as shown.
| - name: Setup Vercel and Next.js Caching | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: | | ||
| apps/admin/.next/cache | ||
| key: ${{ runner.os }}-next-cache-${{ github.sha }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-next-cache- |
There was a problem hiding this comment.
Admin cache key isn't scoped to "admin" — collision risk with any future ${runner.os}-next-cache-* entry.
The website job uses ${{ runner.os }}-website-next-cache-... (line 74-76), but the admin job uses the generic ${{ runner.os }}-next-cache-.... If another workflow/job ever writes under the same prefix, admin could restore a website (or other) .next/cache. Low severity today, but easy to fix and consistent with the website job.
🛠️ Proposed fix
- key: ${{ runner.os }}-next-cache-${{ github.sha }}
+ key: ${{ runner.os }}-admin-next-cache-${{ github.sha }}
restore-keys: |
- ${{ runner.os }}-next-cache-
+ ${{ runner.os }}-admin-next-cache-📝 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.
| - name: Setup Vercel and Next.js Caching | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| apps/admin/.next/cache | |
| key: ${{ runner.os }}-next-cache-${{ github.sha }} | |
| restore-keys: | | |
| ${{ runner.os }}-next-cache- | |
| - name: Setup Vercel and Next.js Caching | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| apps/admin/.next/cache | |
| key: ${{ runner.os }}-admin-next-cache-${{ github.sha }} | |
| restore-keys: | | |
| ${{ runner.os }}-admin-next-cache- |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/manual-deploy-parallel.yml around lines 178 - 185, The
cache key for the "Setup Vercel and Next.js Caching" step is too generic and may
collide with other jobs; update the cache key and restore-keys to include an
"admin" scope (e.g., change ${{ runner.os }}-next-cache-${{ github.sha }} and
${{ runner.os }}-next-cache- to include "admin" such as ${{ runner.os
}}-admin-next-cache-${{ github.sha }} and ${{ runner.os }}-admin-next-cache-) so
this job's cache is uniquely namespaced to admin.
| "private": true, | ||
| "scripts": { | ||
| "dev": "next dev", | ||
| "dev:website:test": "next dev --port 3000", |
There was a problem hiding this comment.
Two scripts with the same name behave differently — Playwright will pick this one, which doesn't load .env.test.local explicitly.
Root package.json defines dev:website:test as ... dotenv -e .env.test.local -- nx serve website --port=3000 with explicit memory cap and env forcing. This app-level one is a plain next dev --port 3000. Playwright's webServer.command: "npm run dev:website:test" (run with cwd inside apps/website) resolves to this script, not the root one, so:
.env.test.localisn't loaded viadotenv-cli— you're relying on Next.js's built-in env cascade plus Playwright'swebServer.envforcingNODE_ENV=test. That usually works, but silently diverges from the documented "loads.env.test.local" comment inplaywright.config.ts(line 203).- No
NODE_OPTIONS=--max-old-space-size=1536, so the 8GB-RAM guardrail mentioned elsewhere is lost in E2E runs.
Consider aligning one of the two (either drop this app-level script and point Playwright at the root script via npm run --prefix ../.. dev:website:test, or replicate the env loading + memory flag here).
🧰 Tools
🪛 GitHub Actions: CI Checks (Lint, Type Check, Build)
[error] format check failed: Code style issues found in 15 files. Command 'pnpm run format:check:tracked' (bash scripts/ci-quality-gate.sh format) exited with code 1.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/website/package.json` at line 7, The app-level npm script
"dev:website:test" diverges from the root script and prevents .env.test.local
and NODE_OPTIONS from being applied; either remove this app-level
"dev:website:test" so Playwright's npm run resolves to the root script, or
change the app-level "dev:website:test" to match the root behavior by invoking
dotenv to load .env.test.local and setting
NODE_OPTIONS=--max-old-space-size=1536 (so Playwright's webServer.command: "npm
run dev:website:test" and the comment in playwright.config.ts remain correct).
Ensure you update the script named dev:website:test and keep Playwright's
webServer.command and the env-loading semantics consistent.
| <div className="relative inline-block group"> | ||
| <div className="absolute -inset-1 bg-gradient-to-r from-indigo-500 to-purple-600 rounded-2xl blur opacity-25 group-hover:opacity-50 transition duration-1000 group-hover:duration-200"></div> | ||
| <CTAButton | ||
| href={personalizedContent.ctaLink} | ||
| text={personalizedContent.cta} | ||
| icon={personalizedContent.icon} | ||
| color={personalizedContent.color} | ||
| showAnimation={showAnimation} | ||
| /> | ||
| </div> |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Does Tailwind CSS support named groups like group/nameandgroup-hover/name:* in the version commonly used (Tailwind v3.2+)?
💡 Result:
Yes, Tailwind CSS supports named groups using the syntax group/name on the parent element and group-hover/name:* (or other state variants like group-hover/name:bg-red-500) on child elements, starting from version 3.2 and available in all subsequent versions including v3.2+ which is the commonly used version as of 2026. This feature was introduced in Tailwind CSS v3.2 via variant modifiers for nested groups and multiple peers. The official documentation explicitly documents this syntax for differentiating nested groups: apply group/{name} to the specific parent and use group-hover/{name} (etc.) on descendants. Examples from official sources: - Parent:
Citations:
- 1: Named Groups tailwindlabs/tailwindcss#7639
- 2: https://tailwindcss.com/docs/hover-focus-and-other-states
- 3: https://v3.tailwindcss.com/blog/tailwindcss-v3-2
- 4: https://tailwindcss.com/blog/tailwindcss-v3-2
Use named groups to prevent hover state leakage.
The nested unnamed .group here will cause Tailwind to match group-hover:* variants on both the outer wrapper and the inner CTAButton, triggering the shine animation when hovering over the blur halo instead of just the button itself.
Use named groups to scope them independently:
Fix using named groups
- <div className="relative inline-block group">
- <div className="absolute -inset-1 bg-gradient-to-r from-indigo-500 to-purple-600 rounded-2xl blur opacity-25 group-hover:opacity-50 transition duration-1000 group-hover:duration-200"></div>
+ <div className="relative inline-block group/cta">
+ <div className="absolute -inset-1 bg-gradient-to-r from-indigo-500 to-purple-600 rounded-2xl blur opacity-25 group-hover/cta:opacity-50 transition duration-1000 group-hover/cta:duration-200 pointer-events-none"></div>
<CTAButtonAdding pointer-events-none on the glow prevents accidental hover triggers from the blur halo tail.
📝 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.
| <div className="relative inline-block group"> | |
| <div className="absolute -inset-1 bg-gradient-to-r from-indigo-500 to-purple-600 rounded-2xl blur opacity-25 group-hover:opacity-50 transition duration-1000 group-hover:duration-200"></div> | |
| <CTAButton | |
| href={personalizedContent.ctaLink} | |
| text={personalizedContent.cta} | |
| icon={personalizedContent.icon} | |
| color={personalizedContent.color} | |
| showAnimation={showAnimation} | |
| /> | |
| </div> | |
| <div className="relative inline-block group/cta"> | |
| <div className="absolute -inset-1 bg-gradient-to-r from-indigo-500 to-purple-600 rounded-2xl blur opacity-25 group-hover/cta:opacity-50 transition duration-1000 group-hover/cta:duration-200 pointer-events-none"></div> | |
| <CTAButton | |
| href={personalizedContent.ctaLink} | |
| text={personalizedContent.cta} | |
| icon={personalizedContent.icon} | |
| color={personalizedContent.color} | |
| showAnimation={showAnimation} | |
| /> | |
| </div> |
🧰 Tools
🪛 GitHub Actions: CI Checks (Lint, Type Check, Build)
[warning] Code style issue detected by format:check:tracked (Prettier).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@libs/common-ui/src/components/molecules/HeroSection.tsx` around lines 57 -
66, The outer wrapper's anonymous "group" lets Tailwind's group-hover variants
trigger on both the glow and the CTAButton, causing the shine to activate when
hovering the blur; fix by converting the wrapper to a named group (e.g., add a
class like "group-wrapper" and use "group-wrapper-hover:" scoped variants on the
blur) and ensure the CTAButton itself uses its own distinct named group (e.g.,
"group-cta") so its group-hover styles only fire when hovering the button; also
add pointer-events-none to the glow element (the absolute -inset-1
bg-gradient... div) so hovering the halo doesn't pass pointer events to the
button's hover state.
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
libs/utilities/src/lib/studyPlans.ts (1)
670-731:⚠️ Potential issue | 🟡 Minor3-Month plan has very sparse task coverage.
The 3-month comprehensive plan advertises 36 total hours but only defines 4 tasks totaling ~9 hours (
js-advanced-study90m,js-advanced-practice90m,complex-design180m,expert-mock180m). Compared to the 1-week plan (13 tasks) and 1-month plan (12 tasks), this will likely feel empty in the UI once users drill into milestones. Worth adding more tasks per milestone so the task list matches the advertised duration.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@libs/utilities/src/lib/studyPlans.ts` around lines 670 - 731, The 3-month plan's milestones (ids: "m1","m2","m3") only contain four tasks (task ids: "js-advanced-study","js-advanced-practice","complex-design","expert-mock") whose estimatedTime totals far less than the advertised 36 hours; update the milestones array by adding more tasks (or splitting existing ones) to each milestone so the sum of estimatedTime across tasks matches ~2160 minutes (36h), ensuring each new task object includes id, title, description, type, estimatedTime and optional resourceUrl; modify the tasks arrays inside the milestone objects in libs/utilities/src/lib/studyPlans.ts (look for the milestones variable) to add multiple smaller study/practice/reading/review tasks per milestone and balance time distribution across m1, m2 and m3.
♻️ Duplicate comments (2)
apps/website/src/app/features/guided-learning/utils/plan-helpers.ts (1)
71-71:⚠️ Potential issue | 🟠 MajorReplace
any[]withunknown[].
getMilestoneRangeonly reads.length, sounknown[]keeps the strict-typing posture without weakening inference.Proposed fix
-export function getMilestoneRange(plans: { milestones?: any[] }[]): string { +export function getMilestoneRange(plans: { milestones?: unknown[] }[]): string {As per coding guidelines: "No
anytypes in TypeScript without expliciteslint-disableand justification comment."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/website/src/app/features/guided-learning/utils/plan-helpers.ts` at line 71, Update the parameter type in getMilestoneRange to avoid using any: change the plans parameter from { milestones?: any[] }[] to { milestones?: unknown[] }[] so the function preserves strict typing while still allowing reads like .length; keep the rest of the function unchanged and do not add eslint-disable comments.libs/common-ui/src/components/molecules/HeroSection.tsx (1)
65-66:⚠️ Potential issue | 🟡 MinorScope the CTA hover group and ignore the glow for pointer events.
The anonymous outer
groupcan leak hover state into nestedgroup-hover:*styles insideCTAButton, and the absolute glow can trigger hover outside the button bounds. Use a named group and make the glow non-interactive.🎯 Proposed scoped hover fix
- <div className="relative inline-block group"> - <div className="absolute -inset-1 bg-gradient-to-r from-indigo-500 to-purple-600 rounded-2xl blur opacity-25 group-hover:opacity-50 transition duration-1000 group-hover:duration-200"></div> + <div className="relative inline-block group/cta"> + <div + aria-hidden="true" + className="absolute -inset-1 bg-gradient-to-r from-indigo-500 to-purple-600 rounded-2xl blur opacity-25 pointer-events-none group-hover/cta:opacity-50 transition duration-1000 group-hover/cta:duration-200" + />Verify the workspace Tailwind version supports named groups before applying this exact syntax:
#!/bin/bash # Description: Check Tailwind version references. Named groups require Tailwind support for group modifiers. rg -n -C2 '"tailwindcss"\s*:' --iglob 'package.json'🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@libs/common-ui/src/components/molecules/HeroSection.tsx` around lines 65 - 66, The outer anonymous hover group and glowing absolute element can leak hover state and catch pointer events; scope the hover by renaming the wrapper group (e.g., change class "group" to a named group like "group-cta") and update any nested group-hover usages in CTAButton to use that named group (e.g., "group-cta/group-hover:*"); also make the glow element non-interactive by adding pointer-events-none to the absolute glow div. Verify your Tailwind version supports named groups before applying the named-group modifier changes.
🧹 Nitpick comments (2)
apps/website/src/app/features/guided-learning/components/LearningPlanCard.tsx (1)
37-39: Nit:toLowerCase()is redundant given the typed union.
StudyPlan.difficultyis already typed as"beginner" | "intermediate" | "advanced"inlibs/types/src/lib/studyPlans.ts, sotoLowerCase()plus the|| difficultyColors.intermediatefallback is defensive against a case that the type system rules out. Not a defect — just dead insurance. If you want to keep the belt-and-suspenders for runtime safety against bad data, leaving it is fine; otherwise simplify toplan.difficulty as keyof typeof difficultyColors.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/website/src/app/features/guided-learning/components/LearningPlanCard.tsx` around lines 37 - 39, The code calls plan.difficulty.toLowerCase() before casting to difficultyColors keys, which is redundant because StudyPlan.difficulty is already the union "beginner"|"intermediate"|"advanced"; simplify by removing the runtime toLowerCase() and cast plan.difficulty directly (update the binding that defines planDifficulty in LearningPlanCard.tsx to use plan.difficulty as keyof typeof difficultyColors and remove the unnecessary fallback logic tied to toLowerCase()).apps/website/src/app/features/guided-learning/utils/plan-helpers.ts (1)
31-45: Unknown difficulties will sort to the top.
difficultyOrder[diffX] || 0maps any unknown difficulty to0, which places it beforebeginner(1). If a plan ever arrives with a stray/typo difficulty, it silently jumps to the front of the list. Minor, but mapping unknown toNumber.POSITIVE_INFINITY(or99) pushes such entries to the end, which is usually the safer default.Proposed tweak
- return (difficultyOrder[diffA] || 0) - (difficultyOrder[diffB] || 0); + return ( + (difficultyOrder[diffA] ?? Number.POSITIVE_INFINITY) - + (difficultyOrder[diffB] ?? Number.POSITIVE_INFINITY) + );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/website/src/app/features/guided-learning/utils/plan-helpers.ts` around lines 31 - 45, The sortPlansByDifficulty function currently treats unknown difficulty keys as 0 which sorts them before "beginner"; change the fallback for difficulty lookup in the comparator to a large value (e.g., Number.POSITIVE_INFINITY or 99) so unknown values are pushed to the end. Update the comparator where diffA/diffB are used with (difficultyOrder[diffA] ?? Number.POSITIVE_INFINITY) and (difficultyOrder[diffB] ?? Number.POSITIVE_INFINITY) (or use 99), keeping the same diffA/diffB variables and difficultyOrder map to locate the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/website/src/app/features/guided-learning/utils/plan-helpers.ts`:
- Around line 47-69: getQuestionsRange currently returns two different semantic
formats (counts like "A-B" or time like "A-B Hours"), which can cause callers to
mislabel values; change getQuestionsRange to return a discriminated shape
instead of a raw string (for example { kind: "questions" | "hours", label:
string, min: number, max: number }) so callers can decide how to render; update
the function logic that currently reads plans -> totalQuestions and plans ->
duration?.totalHours to populate the appropriate kind and numeric min/max, and
then update callers of getQuestionsRange to use the discriminant (kind) and
label or format the string themselves.
In `@failed_job_log.txt`:
- Around line 1-415: Remove the committed CI run log file (failed_job_log.txt)
from the repository, commit the deletion (git rm failed_job_log.txt; git commit
-m "chore: remove CI run log"), add a rule to .gitignore to prevent future
checkins (e.g. failed_job_log.txt or a logs/ pattern), include a short sanitized
excerpt in docs if needed, and if the log is already in main history scrub it
from the repo using a history-rewrite tool (git filter-repo or BFG) and
force-push the cleaned branch; attach full raw logs to the GitHub Actions
artifacts instead of committing them.
- Line 415: Update all GitHub workflow files that reference actions pinned to v4
by replacing actions/checkout@v4, actions/setup-node@v4, actions/cache@v4, and
pnpm/action-setup@v4 with their v5 releases (actions/checkout@v5,
actions/setup-node@v5, actions/cache@v5, pnpm/action-setup@v5); search for those
exact strings in your .github/workflows/*.yml files and perform the version
bumps, then run CI to verify; if you need a temporary opt-in instead of bumping,
set the FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true runner/workflow environment
variable, but prefer updating the action versions.
In `@libs/common-ui/src/components/molecules/HeroSection.tsx`:
- Around line 26-27: In HeroSection (the two decorative blob <div>s that
currently include "animate-pulse"), make the animation respect user motion
preferences by replacing "animate-pulse" with "motion-safe:animate-pulse
motion-reduce:animate-none" and mark each decorative element as non-semantic by
adding aria-hidden="true"; locate the elements by their existing class strings
(e.g., "absolute top-10 left-10 w-72 h-72 bg-indigo-300..." and "absolute
bottom-10 right-10 w-72 h-72 bg-purple-300...") and apply these changes to both
blobs.
---
Outside diff comments:
In `@libs/utilities/src/lib/studyPlans.ts`:
- Around line 670-731: The 3-month plan's milestones (ids: "m1","m2","m3") only
contain four tasks (task ids:
"js-advanced-study","js-advanced-practice","complex-design","expert-mock") whose
estimatedTime totals far less than the advertised 36 hours; update the
milestones array by adding more tasks (or splitting existing ones) to each
milestone so the sum of estimatedTime across tasks matches ~2160 minutes (36h),
ensuring each new task object includes id, title, description, type,
estimatedTime and optional resourceUrl; modify the tasks arrays inside the
milestone objects in libs/utilities/src/lib/studyPlans.ts (look for the
milestones variable) to add multiple smaller study/practice/reading/review tasks
per milestone and balance time distribution across m1, m2 and m3.
---
Duplicate comments:
In `@apps/website/src/app/features/guided-learning/utils/plan-helpers.ts`:
- Line 71: Update the parameter type in getMilestoneRange to avoid using any:
change the plans parameter from { milestones?: any[] }[] to { milestones?:
unknown[] }[] so the function preserves strict typing while still allowing reads
like .length; keep the rest of the function unchanged and do not add
eslint-disable comments.
In `@libs/common-ui/src/components/molecules/HeroSection.tsx`:
- Around line 65-66: The outer anonymous hover group and glowing absolute
element can leak hover state and catch pointer events; scope the hover by
renaming the wrapper group (e.g., change class "group" to a named group like
"group-cta") and update any nested group-hover usages in CTAButton to use that
named group (e.g., "group-cta/group-hover:*"); also make the glow element
non-interactive by adding pointer-events-none to the absolute glow div. Verify
your Tailwind version supports named groups before applying the named-group
modifier changes.
---
Nitpick comments:
In
`@apps/website/src/app/features/guided-learning/components/LearningPlanCard.tsx`:
- Around line 37-39: The code calls plan.difficulty.toLowerCase() before casting
to difficultyColors keys, which is redundant because StudyPlan.difficulty is
already the union "beginner"|"intermediate"|"advanced"; simplify by removing the
runtime toLowerCase() and cast plan.difficulty directly (update the binding that
defines planDifficulty in LearningPlanCard.tsx to use plan.difficulty as keyof
typeof difficultyColors and remove the unnecessary fallback logic tied to
toLowerCase()).
In `@apps/website/src/app/features/guided-learning/utils/plan-helpers.ts`:
- Around line 31-45: The sortPlansByDifficulty function currently treats unknown
difficulty keys as 0 which sorts them before "beginner"; change the fallback for
difficulty lookup in the comparator to a large value (e.g.,
Number.POSITIVE_INFINITY or 99) so unknown values are pushed to the end. Update
the comparator where diffA/diffB are used with (difficultyOrder[diffA] ??
Number.POSITIVE_INFINITY) and (difficultyOrder[diffB] ??
Number.POSITIVE_INFINITY) (or use 99), keeping the same diffA/diffB variables
and difficultyOrder map to locate the change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: f8af5858-733b-490e-a536-afe390cb289e
📒 Files selected for processing (17)
apps/website/src/app/features/guided-learning/components/ActivePlanView.tsxapps/website/src/app/features/guided-learning/components/GuidedLearningHeader.tsxapps/website/src/app/features/guided-learning/components/LearningPlanCard.tsxapps/website/src/app/features/guided-learning/components/PlanSelectionView.tsxapps/website/src/app/features/guided-learning/hooks/useActivePlan.tsapps/website/src/app/features/guided-learning/page.tsxapps/website/src/app/features/guided-learning/types/guided-learning.types.tsapps/website/src/app/features/guided-learning/utils/plan-helpers.tsapps/website/src/app/lib/network/routes/guided-learning/plans/route.tsapps/website/src/app/lib/studyPlans.tsapps/website/tests/config/playwright.config.tsdocs/admin/deployment-troubleshooting.mdfailed_job_log.txtlibs/common-ui/src/common/LearningModeSwitcher.tsxlibs/common-ui/src/components/molecules/HeroSection.tsxlibs/common-ui/src/components/organisms/HomePageLayout.tsxlibs/utilities/src/lib/studyPlans.ts
✅ Files skipped from review due to trivial changes (2)
- apps/website/src/app/features/guided-learning/components/PlanSelectionView.tsx
- docs/admin/deployment-troubleshooting.md
🚧 Files skipped from review as they are similar to previous changes (8)
- apps/website/tests/config/playwright.config.ts
- apps/website/src/app/lib/network/routes/guided-learning/plans/route.ts
- apps/website/src/app/features/guided-learning/components/GuidedLearningHeader.tsx
- libs/common-ui/src/common/LearningModeSwitcher.tsx
- apps/website/src/app/features/guided-learning/types/guided-learning.types.ts
- libs/common-ui/src/components/organisms/HomePageLayout.tsx
- apps/website/src/app/features/guided-learning/hooks/useActivePlan.ts
- apps/website/src/app/lib/studyPlans.ts
| Deploy Admin Set up job 2026-04-17T21:49:40.1792838Z Current runner version: '2.333.1' | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1814075Z ##[group]Runner Image Provisioner | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1814844Z Hosted Compute Agent | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1815275Z Version: 20260213.493 | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1815801Z Commit: 5c115507f6dd24b8de37d8bbe0bb4509d0cc0fa3 | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1816410Z Build Date: 2026-02-13T00:28:41Z | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1816947Z Worker ID: {9b823b10-5fdd-4177-ae5b-ee3693d7aefd} | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1817501Z Azure Region: westcentralus | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1818043Z ##[endgroup] | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1819093Z ##[group]Operating System | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1819629Z Ubuntu | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1820055Z 24.04.4 | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1820659Z LTS | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1821098Z ##[endgroup] | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1821550Z ##[group]Runner Image | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1822051Z Image: ubuntu-24.04 | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1822558Z Version: 20260413.86.1 | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1823421Z Included Software: https://github.com/actions/runner-images/blob/ubuntu24/20260413.86/images/ubuntu/Ubuntu2404-Readme.md | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1824759Z Image Release: https://github.com/actions/runner-images/releases/tag/ubuntu24%2F20260413.86 | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1825557Z ##[endgroup] | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1826476Z ##[group]GITHUB_TOKEN Permissions | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1827967Z Contents: read | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1828500Z Metadata: read | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1828984Z ##[endgroup] | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1830946Z Secret source: Actions | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.1831650Z Prepare workflow directory | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.2093414Z Prepare all required actions | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.2125691Z Getting action download info | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.6277710Z Download action repository 'actions/checkout@v4' (SHA:34e114876b0b11c390a56381ad16ebd13914f8d5) | ||
| Deploy Admin Set up job 2026-04-17T21:49:40.7467332Z Download action repository 'pnpm/action-setup@v4' (SHA:b906affcce14559ad1aafd4ab0e942779e9f58b1) | ||
| Deploy Admin Set up job 2026-04-17T21:49:41.7547115Z Download action repository 'actions/setup-node@v4' (SHA:49933ea5288caeca8642d1e84afbd3f7d6820020) | ||
| Deploy Admin Set up job 2026-04-17T21:49:41.8884348Z Download action repository 'actions/cache@v4' (SHA:0057852bfaa89a56745cba8c7296529d2fc39830) | ||
| Deploy Admin Set up job 2026-04-17T21:49:42.2554390Z Complete job name: Deploy Admin | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.3083483Z ##[group]Run actions/checkout@v4 | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.3083959Z with: | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.3084185Z ref: feat/optimize-ci-and-deploy-sync | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.3084470Z repository: FoushWare/elzatona_web | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.3084858Z token: *** | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.3085065Z ssh-strict: true | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.3085277Z ssh-user: git | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.3085495Z persist-credentials: true | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.3085728Z clean: true | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.3085944Z sparse-checkout-cone-mode: true | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.3086198Z fetch-depth: 1 | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.3086396Z fetch-tags: false | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.3086597Z show-progress: true | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.3086796Z lfs: false | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.3086977Z submodules: false | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.3087173Z set-safe-directory: true | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.3087641Z ##[endgroup] | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.3924143Z Syncing repository: FoushWare/elzatona_web | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.3925511Z ##[group]Getting Git version info | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.3925984Z Working directory is '/home/runner/work/elzatona_web/elzatona_web' | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.3926774Z [command]/usr/bin/git version | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4488056Z git version 2.53.0 | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4506709Z ##[endgroup] | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4518316Z Temporarily overriding HOME='/home/runner/work/_temp/10bbbbd5-1677-4e7b-aec4-c0c87085f301' before making global git config changes | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4519009Z Adding repository directory to the temporary git global config as a safe directory | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4529552Z [command]/usr/bin/git config --global --add safe.directory /home/runner/work/elzatona_web/elzatona_web | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4555557Z Deleting the contents of '/home/runner/work/elzatona_web/elzatona_web' | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4558329Z ##[group]Initializing the repository | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4561830Z [command]/usr/bin/git init /home/runner/work/elzatona_web/elzatona_web | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4638784Z hint: Using 'master' as the name for the initial branch. This default branch name | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4639579Z hint: will change to "main" in Git 3.0. To configure the initial branch name | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4640447Z hint: to use in all of your new repositories, which will suppress this warning, | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4641018Z hint: call: | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4641312Z hint: | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4641726Z hint: git config --global init.defaultBranch <name> | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4642179Z hint: | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4642603Z hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4643296Z hint: 'development'. The just-created branch can be renamed via this command: | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4643834Z hint: | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4644134Z hint: git branch -m <name> | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4644473Z hint: | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4644932Z hint: Disable this message with "git config set advice.defaultBranchName false" | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4645824Z Initialized empty Git repository in /home/runner/work/elzatona_web/elzatona_web/.git/ | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4649534Z [command]/usr/bin/git remote add origin https://github.com/FoushWare/elzatona_web | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4674461Z ##[endgroup] | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4675063Z ##[group]Disabling automatic garbage collection | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4677852Z [command]/usr/bin/git config --local gc.auto 0 | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4701509Z ##[endgroup] | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4702064Z ##[group]Setting up auth | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4706624Z [command]/usr/bin/git config --local --name-only --get-regexp core\.sshCommand | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4729256Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :" | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.4978726Z [command]/usr/bin/git config --local --name-only --get-regexp http\.https\:\/\/github\.com\/\.extraheader | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.5002821Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.https\:\/\/github\.com\/\.extraheader' && git config --local --unset-all 'http.https://github.com/.extraheader' || :" | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.5182110Z [command]/usr/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir: | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.5205656Z [command]/usr/bin/git submodule foreach --recursive git config --local --show-origin --name-only --get-regexp remote.origin.url | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.5393859Z [command]/usr/bin/git config --local http.https://github.com/.extraheader AUTHORIZATION: basic *** | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.5419826Z ##[endgroup] | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.5420596Z ##[group]Fetching the repository | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:42.5427568Z [command]/usr/bin/git -c protocol.version=2 fetch --no-tags --prune --no-recurse-submodules --depth=1 origin +refs/heads/feat/optimize-ci-and-deploy-sync*:refs/remotes/origin/feat/optimize-ci-and-deploy-sync* +refs/tags/feat/optimize-ci-and-deploy-sync*:refs/tags/feat/optimize-ci-and-deploy-sync* | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:45.3160847Z From https://github.com/FoushWare/elzatona_web | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:45.3161764Z * [new branch] feat/optimize-ci-and-deploy-sync -> origin/feat/optimize-ci-and-deploy-sync | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:45.3185167Z ##[endgroup] | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:45.3185960Z ##[group]Determining the checkout info | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:45.3191981Z [command]/usr/bin/git branch --list --remote origin/feat/optimize-ci-and-deploy-sync | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:45.3211755Z origin/feat/optimize-ci-and-deploy-sync | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:45.3215369Z ##[endgroup] | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:45.3219364Z [command]/usr/bin/git sparse-checkout disable | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:45.3746742Z [command]/usr/bin/git config --local --unset-all extensions.worktreeConfig | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:45.3769738Z ##[group]Checking out the ref | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:45.3773705Z [command]/usr/bin/git checkout --progress --force -B feat/optimize-ci-and-deploy-sync refs/remotes/origin/feat/optimize-ci-and-deploy-sync | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:45.7538516Z Switched to a new branch 'feat/optimize-ci-and-deploy-sync' | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:45.7539366Z branch 'feat/optimize-ci-and-deploy-sync' set up to track 'origin/feat/optimize-ci-and-deploy-sync'. | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:45.7562655Z ##[endgroup] | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:45.7598898Z [command]/usr/bin/git log -1 --format=%H | ||
| Deploy Admin Checkout repository 2026-04-17T21:49:45.7617639Z ceb93242f18483b5d1227a8efb9f8bd607a42fd4 | ||
| Deploy Admin Install pnpm 2026-04-17T21:49:45.7772976Z ##[group]Run pnpm/action-setup@v4 | ||
| Deploy Admin Install pnpm 2026-04-17T21:49:45.7827870Z with: | ||
| Deploy Admin Install pnpm 2026-04-17T21:49:45.7828084Z version: 10.33.0 | ||
| Deploy Admin Install pnpm 2026-04-17T21:49:45.7828261Z dest: ~/setup-pnpm | ||
| Deploy Admin Install pnpm 2026-04-17T21:49:45.7828444Z run_install: null | ||
| Deploy Admin Install pnpm 2026-04-17T21:49:45.7828626Z cache: false | ||
| Deploy Admin Install pnpm 2026-04-17T21:49:45.7828828Z cache_dependency_path: pnpm-lock.yaml | ||
| Deploy Admin Install pnpm 2026-04-17T21:49:45.7829089Z package_json_file: package.json | ||
| Deploy Admin Install pnpm 2026-04-17T21:49:45.7829316Z standalone: false | ||
| Deploy Admin Install pnpm 2026-04-17T21:49:45.7829490Z ##[endgroup] | ||
| Deploy Admin Install pnpm 2026-04-17T21:49:45.8909521Z ##[group]Running self-installer... | ||
| Deploy Admin Install pnpm 2026-04-17T21:49:46.4357716Z Progress: resolved 1, reused 0, downloaded 0, added 0 | ||
| Deploy Admin Install pnpm 2026-04-17T21:49:46.4443587Z Packages: +1 | ||
| Deploy Admin Install pnpm 2026-04-17T21:49:46.4444129Z + | ||
| Deploy Admin Install pnpm 2026-04-17T21:49:46.8629595Z Progress: resolved 1, reused 0, downloaded 1, added 1, done | ||
| Deploy Admin Install pnpm 2026-04-17T21:49:47.0262160Z | ||
| Deploy Admin Install pnpm 2026-04-17T21:49:47.0262649Z dependencies: | ||
| Deploy Admin Install pnpm 2026-04-17T21:49:47.0262909Z + pnpm 10.33.0 | ||
| Deploy Admin Install pnpm 2026-04-17T21:49:47.0263015Z | ||
| Deploy Admin Install pnpm 2026-04-17T21:49:47.0291355Z Done in 1s | ||
| Deploy Admin Install pnpm 2026-04-17T21:49:47.0438226Z ##[endgroup] | ||
| Deploy Admin Install pnpm 2026-04-17T21:49:47.0441259Z Installation Completed! | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:47.0566648Z ##[group]Run actions/setup-node@v4 | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:47.0566868Z with: | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:47.0567026Z node-version: 20 | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:47.0567191Z cache: pnpm | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:47.0567341Z always-auth: false | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:47.0567515Z check-latest: false | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:47.0567783Z token: *** | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:47.0567943Z env: | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:47.0568155Z PNPM_HOME: /home/runner/setup-pnpm/node_modules/.bin | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:47.0568413Z ##[endgroup] | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:47.3153221Z Found in cache @ /opt/hostedtoolcache/node/20.20.2/x64 | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:47.3159347Z ##[group]Environment details | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:47.9824514Z node: v20.20.2 | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:47.9824881Z npm: 10.8.2 | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:47.9825042Z yarn: 1.22.22 | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:47.9825952Z ##[endgroup] | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:47.9845294Z [command]/home/runner/setup-pnpm/node_modules/.bin/pnpm store path --silent | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:48.2512537Z /home/runner/setup-pnpm/node_modules/.bin/store/v10 | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:48.4660494Z Cache hit for: node-cache-Linux-x64-pnpm-c1fa4408a2f88a7a3398f75e9407cf077d6dc825f6d3648966b2ca686a6fa52f | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:49.6962787Z Received 33554432 of 436469304 (7.7%), 32.0 MBs/sec | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:50.7011151Z Received 192937984 of 436469304 (44.2%), 91.9 MBs/sec | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:51.6980388Z Received 377487360 of 436469304 (86.5%), 119.9 MBs/sec | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:52.3791755Z Received 436469304 of 436469304 (100.0%), 113.0 MBs/sec | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:52.3792562Z Cache Size: ~416 MB (436469304 B) | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:52.3820597Z [command]/usr/bin/tar -xf /home/runner/work/_temp/4753f2c7-5887-4950-a697-1e1831cb846d/cache.tzst -P -C /home/runner/work/elzatona_web/elzatona_web --use-compress-program unzstd | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:57.4183725Z Cache restored successfully | ||
| Deploy Admin Setup Node.js 2026-04-17T21:49:57.4359607Z Cache restored from key: node-cache-Linux-x64-pnpm-c1fa4408a2f88a7a3398f75e9407cf077d6dc825f6d3648966b2ca686a6fa52f | ||
| Deploy Admin Enable Corepack 2026-04-17T21:49:57.4576167Z ##[group]Run corepack enable | ||
| Deploy Admin Enable Corepack 2026-04-17T21:49:57.4576448Z [36;1mcorepack enable[0m | ||
| Deploy Admin Enable Corepack 2026-04-17T21:49:57.4600917Z shell: /usr/bin/bash -e {0} | ||
| Deploy Admin Enable Corepack 2026-04-17T21:49:57.4601127Z env: | ||
| Deploy Admin Enable Corepack 2026-04-17T21:49:57.4601343Z PNPM_HOME: /home/runner/setup-pnpm/node_modules/.bin | ||
| Deploy Admin Enable Corepack 2026-04-17T21:49:57.4601595Z ##[endgroup] | ||
| Deploy Admin Install root dependencies 2026-04-17T21:49:57.5569423Z ##[group]Run pnpm install --frozen-lockfile | ||
| Deploy Admin Install root dependencies 2026-04-17T21:49:57.5569734Z [36;1mpnpm install --frozen-lockfile[0m | ||
| Deploy Admin Install root dependencies 2026-04-17T21:49:57.5588524Z shell: /usr/bin/bash -e {0} | ||
| Deploy Admin Install root dependencies 2026-04-17T21:49:57.5588730Z env: | ||
| Deploy Admin Install root dependencies 2026-04-17T21:49:57.5588938Z PNPM_HOME: /home/runner/setup-pnpm/node_modules/.bin | ||
| Deploy Admin Install root dependencies 2026-04-17T21:49:57.5589185Z ##[endgroup] | ||
| Deploy Admin Install root dependencies 2026-04-17T21:49:57.6308583Z ! Corepack is about to download https://registry.npmjs.org/pnpm/-/pnpm-10.33.0.tgz | ||
| Deploy Admin Install root dependencies 2026-04-17T21:49:58.8950414Z Scope: all 13 workspace projects | ||
| Deploy Admin Install root dependencies 2026-04-17T21:49:58.9895940Z Lockfile is up to date, resolution step is skipped | ||
| Deploy Admin Install root dependencies 2026-04-17T21:49:59.1134895Z Progress: resolved 1, reused 0, downloaded 0, added 0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:49:59.2622246Z Packages: +2431 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:49:59.2622999Z ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:00.1144346Z Progress: resolved 2431, reused 360, downloaded 0, added 0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:01.1149684Z Progress: resolved 2431, reused 1085, downloaded 0, added 0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:02.1151068Z Progress: resolved 2431, reused 2282, downloaded 0, added 0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:03.1154314Z Progress: resolved 2431, reused 2417, downloaded 0, added 81 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:04.1165302Z Progress: resolved 2431, reused 2417, downloaded 0, added 103 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:05.1171370Z Progress: resolved 2431, reused 2417, downloaded 0, added 378 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:06.1167251Z Progress: resolved 2431, reused 2417, downloaded 0, added 677 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:07.1166003Z Progress: resolved 2431, reused 2417, downloaded 0, added 903 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:08.1171558Z Progress: resolved 2431, reused 2417, downloaded 0, added 1043 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:09.1177070Z Progress: resolved 2431, reused 2417, downloaded 0, added 1084 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:10.1177952Z Progress: resolved 2431, reused 2417, downloaded 0, added 1202 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:11.1183109Z Progress: resolved 2431, reused 2417, downloaded 0, added 1411 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:12.1183918Z Progress: resolved 2431, reused 2417, downloaded 0, added 1861 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:13.1187277Z Progress: resolved 2431, reused 2417, downloaded 0, added 1939 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:14.2940851Z Progress: resolved 2431, reused 2417, downloaded 0, added 1940 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:15.2234768Z Progress: resolved 2431, reused 2417, downloaded 0, added 2431, done | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3987436Z | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3988020Z dependencies: | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3988397Z + @daily-co/daily-js 0.84.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3988764Z + @heroicons/react 2.2.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3989104Z + @monaco-editor/react 4.7.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3989600Z + @radix-ui/react-checkbox 1.3.3 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3990022Z + @radix-ui/react-collapsible 1.1.12 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3990595Z + @radix-ui/react-dialog 1.1.15 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3990951Z + @radix-ui/react-label 2.1.8 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3991399Z + @radix-ui/react-popover 1.1.15 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3991784Z + @radix-ui/react-progress 1.1.8 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3992167Z + @radix-ui/react-scroll-area 1.2.10 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3992565Z + @radix-ui/react-select 2.2.6 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3992950Z + @radix-ui/react-separator 1.1.8 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3993329Z + @radix-ui/react-slot 1.2.4 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3993683Z + @radix-ui/react-switch 1.2.6 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3994020Z + @radix-ui/react-tabs 1.1.13 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3994322Z + @reactour/tour 3.8.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3994596Z + @sentry/nextjs 10.46.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3994939Z + @supabase/supabase-js 2.100.1 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3995311Z + @tanstack/react-query 5.95.2 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3995729Z + @tanstack/react-query-devtools 5.95.2 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3996149Z + @types/bcryptjs 2.4.6 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3996465Z + @types/dompurify 3.2.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3996778Z + @types/jsonwebtoken 9.0.10 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3997176Z + @types/react-syntax-highlighter 15.5.13 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3997576Z + @types/swagger-jsdoc 6.0.4 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3997952Z + @types/swagger-ui-react 5.18.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3998307Z + @vercel/speed-insights 1.3.1 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3998614Z + axios 1.15.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3998861Z + bcryptjs 3.0.3 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3999648Z + cheerio 1.2.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.3999984Z + class-variance-authority 0.7.1 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4000529Z + cloudinary 2.9.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4000825Z + clsx 2.1.1 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4001081Z + critters 0.0.23 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4001249Z + date-fns 4.1.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4001424Z + dompurify 3.4.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4001587Z + dotenv 17.3.1 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4001852Z + firebase 12.11.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4002054Z + html-to-image 1.11.13 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4002524Z + jotai 2.19.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4002741Z + jsonwebtoken 9.0.3 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4003000Z + lucide-react 0.542.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4003172Z + next 16.2.3 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4003346Z + next-auth 4.24.13 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4003520Z + node-cron 4.2.1 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4003697Z + node-fetch 2.7.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4003862Z + nuqs 2.8.9 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4004018Z + react 19.2.4 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4004290Z + react-day-picker 9.14.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4004578Z + react-dom 19.2.4 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4004783Z + react-markdown 10.1.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4005019Z + react-resizable-panels 2.1.9 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4005408Z + react-syntax-highlighter 16.1.1 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4005736Z + shiki 3.23.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4005989Z + sonner 2.0.7 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4006193Z + swagger-jsdoc 6.2.8 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4006386Z + swagger-ui-react 5.32.1 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4006581Z + tailwind-merge 3.5.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4006776Z + tailwindcss-animate 1.0.7 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4006985Z + validator 13.15.26 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4007152Z + xss 1.0.15 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4007311Z + zod 4.3.6 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4007407Z | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4007489Z devDependencies: | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4007677Z + @eslint/eslintrc 3.3.5 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4007971Z + @mermaid-js/mermaid-cli 11.12.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4008227Z + @nx/eslint 22.6.3 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4008397Z + @nx/jest 22.6.3 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4008564Z + @nx/next 22.6.3 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4008728Z + @nx/vite 22.6.3 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4008902Z + @playwright/test 1.58.2 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4009182Z + @swc-node/register 1.11.1 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4009477Z + @swc/core 1.15.21 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4009753Z + @testing-library/jest-dom 6.9.1 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4010090Z + @testing-library/react 16.3.2 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4010731Z + @testing-library/user-event 14.6.1 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4011062Z + @types/jest 30.0.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4011330Z + @types/node 20.19.37 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4011599Z + @types/react 19.2.14 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4011883Z + @types/react-dom 19.2.3 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4012198Z + @vitest/coverage-v8 3.2.4 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4012498Z + autoprefixer 10.4.27 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4012803Z + dotenv-cli 11.0.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4013001Z + eslint 9.39.4 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4013186Z + eslint-config-next 15.5.1 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4013380Z + glob 11.1.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4013536Z + husky 9.1.7 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4013775Z + jest 30.3.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4014052Z + jest-environment-jsdom 30.3.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4014346Z + js-yaml 3.14.2 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4014559Z + lint-staged 16.4.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4014756Z + minimatch 10.2.4 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4014917Z + msw 2.12.14 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4015080Z + null-loader 4.0.1 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4015242Z + nx 22.6.3 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4015404Z + oe-sonar-mcp 1.0.4 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4015571Z + prettier 3.8.1 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4015743Z + puppeteer 23.11.1 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4015907Z + semver 7.7.4 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4016083Z + tailwindcss 3.4.19 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4016245Z + ts-jest 29.4.6 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4016406Z + tsx 4.21.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4016602Z + typescript 5.9.3 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4016767Z + vercel 50.44.0 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4016968Z + vite 7.3.2 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4017146Z + vite-tsconfig-paths 6.1.1 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4017352Z + vitest 3.2.4 | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4017448Z | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4380704Z . prepare$ husky | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4799410Z . prepare: Done | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4925212Z ╭ Warning ─────────────────────────────────────────────────────────────────────╮ | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4926208Z │ │ | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4927435Z │ Ignored build scripts: @firebase/util@1.15.0, @parcel/watcher@2.5.6, │ | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4928354Z │ @scarf/scarf@1.4.0, @sentry/cli@2.58.5, @swc/core@1.15.21, │ | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4929220Z │ @tree-sitter-grammars/tree-sitter-yaml@0.7.1, core-js-pure@3.49.0, │ | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4930128Z │ esbuild@0.27.0, esbuild@0.27.4, esbuild@0.27.7, less@4.5.1, msw@2.12.14, │ | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4931465Z │ nx@22.6.3, protobufjs@7.5.4, puppeteer@23.11.1, sharp@0.34.5, │ | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4932338Z │ tree-sitter-json@0.24.8, tree-sitter@0.21.1, tree-sitter@0.22.4, │ | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4933186Z │ unrs-resolver@1.11.1. │ | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4934052Z │ Run "pnpm approve-builds" to pick which dependencies should be allowed │ | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4934962Z │ to run scripts. │ | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4935501Z │ │ | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.4935982Z ╰──────────────────────────────────────────────────────────────────────────────╯ | ||
| Deploy Admin Install root dependencies 2026-04-17T21:50:16.5128324Z Done in 17.8s using pnpm v10.33.0 | ||
| Deploy Admin Setup Nx and Vercel Caching 2026-04-17T21:50:16.5422878Z ##[group]Run actions/cache@v4 | ||
| Deploy Admin Setup Nx and Vercel Caching 2026-04-17T21:50:16.5423103Z with: | ||
| Deploy Admin Setup Nx and Vercel Caching 2026-04-17T21:50:16.5423278Z path: .nx/cache | ||
| Deploy Admin Setup Nx and Vercel Caching apps/admin/.next/cache | ||
| Deploy Admin Setup Nx and Vercel Caching | ||
| Deploy Admin Setup Nx and Vercel Caching 2026-04-17T21:50:16.5423605Z key: Linux-build-cache-ceb93242f18483b5d1227a8efb9f8bd607a42fd4 | ||
| Deploy Admin Setup Nx and Vercel Caching 2026-04-17T21:50:16.5423904Z restore-keys: Linux-build-cache- | ||
| Deploy Admin Setup Nx and Vercel Caching | ||
| Deploy Admin Setup Nx and Vercel Caching 2026-04-17T21:50:16.5424129Z enableCrossOsArchive: false | ||
| Deploy Admin Setup Nx and Vercel Caching 2026-04-17T21:50:16.5424333Z fail-on-cache-miss: false | ||
| Deploy Admin Setup Nx and Vercel Caching 2026-04-17T21:50:16.5424521Z lookup-only: false | ||
| Deploy Admin Setup Nx and Vercel Caching 2026-04-17T21:50:16.5424691Z save-always: false | ||
| Deploy Admin Setup Nx and Vercel Caching 2026-04-17T21:50:16.5424849Z env: | ||
| Deploy Admin Setup Nx and Vercel Caching 2026-04-17T21:50:16.5425047Z PNPM_HOME: /home/runner/setup-pnpm/node_modules/.bin | ||
| Deploy Admin Setup Nx and Vercel Caching 2026-04-17T21:50:16.5425294Z ##[endgroup] | ||
| Deploy Admin Setup Nx and Vercel Caching 2026-04-17T21:50:16.8307544Z Cache not found for input keys: Linux-build-cache-ceb93242f18483b5d1227a8efb9f8bd607a42fd4, Linux-build-cache- | ||
| Deploy Admin Validate Vercel token 2026-04-17T21:50:16.8371819Z ##[group]Run if [ -z "***" ]; then | ||
| Deploy Admin Validate Vercel token 2026-04-17T21:50:16.8372147Z [36;1mif [ -z "***" ]; then[0m | ||
| Deploy Admin Validate Vercel token 2026-04-17T21:50:16.8372394Z [36;1m echo "::error::Missing VERCEL_TOKEN secret"[0m | ||
| Deploy Admin Validate Vercel token 2026-04-17T21:50:16.8372638Z [36;1m exit 1[0m | ||
| Deploy Admin Validate Vercel token 2026-04-17T21:50:16.8372803Z [36;1mfi[0m | ||
| Deploy Admin Validate Vercel token 2026-04-17T21:50:16.8372995Z [36;1mecho "VERCEL_TOKEN is configured"[0m | ||
| Deploy Admin Validate Vercel token 2026-04-17T21:50:16.8391384Z shell: /usr/bin/bash -e {0} | ||
| Deploy Admin Validate Vercel token 2026-04-17T21:50:16.8391582Z env: | ||
| Deploy Admin Validate Vercel token 2026-04-17T21:50:16.8391791Z PNPM_HOME: /home/runner/setup-pnpm/node_modules/.bin | ||
| Deploy Admin Validate Vercel token 2026-04-17T21:50:16.8392043Z ##[endgroup] | ||
| Deploy Admin Validate Vercel token 2026-04-17T21:50:16.8429593Z VERCEL_TOKEN is configured | ||
| Deploy Admin Configure admin Vercel project settings 2026-04-17T21:50:16.8449104Z ##[group]Run curl -fsS -X PATCH \ | ||
| Deploy Admin Configure admin Vercel project settings 2026-04-17T21:50:16.8449352Z [36;1mcurl -fsS -X PATCH \[0m | ||
| Deploy Admin Configure admin Vercel project settings 2026-04-17T21:50:16.8449788Z [36;1m -H "Authorization: ***" \[0m | ||
| Deploy Admin Configure admin Vercel project settings 2026-04-17T21:50:16.8450045Z [36;1m -H "Content-Type: application/json" \[0m | ||
| Deploy Admin Configure admin Vercel project settings 2026-04-17T21:50:16.8450619Z [36;1m -d '{"buildCommand":"pnpm --filter admin build","outputDirectory":"apps/admin/.next"}' \[0m | ||
| Deploy Admin Configure admin Vercel project settings 2026-04-17T21:50:16.8451100Z [36;1m "https://api.vercel.com/v9/projects/***"[0m | ||
| Deploy Admin Configure admin Vercel project settings 2026-04-17T21:50:16.8467187Z shell: /usr/bin/bash -e {0} | ||
| Deploy Admin Configure admin Vercel project settings 2026-04-17T21:50:16.8467395Z env: | ||
| Deploy Admin Configure admin Vercel project settings 2026-04-17T21:50:16.8467597Z PNPM_HOME: /home/runner/setup-pnpm/node_modules/.bin | ||
| Deploy Admin Configure admin Vercel project settings 2026-04-17T21:50:16.8467847Z ##[endgroup] | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:17.2953978Z ##[group]Run npx vercel pull --yes --environment=production --token=*** | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:17.2954473Z [36;1mnpx vercel pull --yes --environment=production --token=***[0m | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:17.2954844Z [36;1mnpx vercel build --prod --token=***[0m | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:17.2973268Z shell: /usr/bin/bash -e {0} | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:17.2973479Z env: | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:17.2973693Z PNPM_HOME: /home/runner/setup-pnpm/node_modules/.bin | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:17.2974007Z VERCEL_ORG_ID: *** | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:17.2974249Z VERCEL_PROJECT_ID: *** | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:17.2974447Z NEXT_PUBLIC_APP_ENV: production | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:17.2974660Z NEXT_TELEMETRY_DISABLED: 1 | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:17.2974958Z NEXT_PUBLIC_SUPABASE_URL: *** | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:17.2976020Z NEXT_PUBLIC_SUPABASE_ANON_KEY: *** | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:17.2976253Z ##[endgroup] | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:18.3366033Z > NOTE: The Vercel CLI now collects telemetry regarding usage of the CLI. | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:18.3366846Z > This information is used to shape the CLI roadmap and prioritize features. | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:18.3367614Z > You can learn more, including how to opt-out if you'd not like to participate in this program, by visiting the following URL: | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:18.3368131Z > https://vercel.com/docs/cli/about-telemetry | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:18.5262325Z Retrieving project… | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:18.6645830Z > Downloading `production` Environment Variables for foushwares-projects/elzatona-admin | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:18.6646486Z Downloading | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:18.7739457Z Created .vercel/.env.production.local file [109ms] | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:18.7739852Z | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:18.7740030Z > Downloading project settings | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:18.7744116Z Downloaded project settings to ~/work/elzatona_web/elzatona_web/.vercel/project.json [0ms] | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:19.5798815Z WARNING! Build not running on Vercel. System environment variables will not be available. | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:19.8059668Z Warning: Detected "engines": { "node": ">=20.0.0" } in your `package.json` that will automatically upgrade when a new major Node.js Version is released. Learn More: https://vercel.link/node-version | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:19.8843823Z Detected `pnpm-lock.yaml` version 9 generated by pnpm@10.x with package.json#packageManager pnpm@10.33.0 | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:19.8876415Z Running "install" command: `npm install`... | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:28.6678258Z npm error Cannot read properties of null (reading 'package') | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:28.6679315Z npm error A complete log of this run can be found in: /home/runner/.npm/_logs/2026-04-17T21_50_19_938Z-debug-0.log | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:28.6877573Z Error: Command "npm install" exited with 1 | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:28.9340059Z ╭──────────────────────────────────────────────────────────────────────────────╮ | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:28.9341461Z │ │ | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:28.9342235Z │ Update available! v50.44.0 ≫ v51.6.1 │ | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:28.9343060Z │ Changelog: https://github.com/vercel/vercel/releases/tag/vercel%4051.6.1 │ | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:28.9343973Z │ Run `pnpm i vercel@latest` to update. │ | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:28.9344724Z │ │ | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:28.9345480Z │ The latest update may fix any errors that occurred. │ | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:28.9346191Z │ │ | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:28.9346921Z ╰──────────────────────────────────────────────────────────────────────────────╯ | ||
| Deploy Admin Validate and build (Local Vercel Build) 2026-04-17T21:50:28.9570448Z ##[error]Process completed with exit code 1. | ||
| Deploy Admin Post Install pnpm 2026-04-17T21:50:28.9829362Z Post job cleanup. | ||
| Deploy Admin Post Install pnpm 2026-04-17T21:50:29.1060031Z Pruning is unnecessary. | ||
| Deploy Admin Post Checkout repository 2026-04-17T21:50:29.1255154Z Post job cleanup. | ||
| Deploy Admin Post Checkout repository 2026-04-17T21:50:29.2100861Z [command]/usr/bin/git version | ||
| Deploy Admin Post Checkout repository 2026-04-17T21:50:29.2129638Z git version 2.53.0 | ||
| Deploy Admin Post Checkout repository 2026-04-17T21:50:29.2169350Z Temporarily overriding HOME='/home/runner/work/_temp/f09a2c0b-7fea-43c6-acc3-2541c6adc2e6' before making global git config changes | ||
| Deploy Admin Post Checkout repository 2026-04-17T21:50:29.2170822Z Adding repository directory to the temporary git global config as a safe directory | ||
| Deploy Admin Post Checkout repository 2026-04-17T21:50:29.2174495Z [command]/usr/bin/git config --global --add safe.directory /home/runner/work/elzatona_web/elzatona_web | ||
| Deploy Admin Post Checkout repository 2026-04-17T21:50:29.2367036Z [command]/usr/bin/git config --local --name-only --get-regexp core\.sshCommand | ||
| Deploy Admin Post Checkout repository 2026-04-17T21:50:29.2400726Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :" | ||
| Deploy Admin Post Checkout repository 2026-04-17T21:50:29.2667384Z [command]/usr/bin/git config --local --name-only --get-regexp http\.https\:\/\/github\.com\/\.extraheader | ||
| Deploy Admin Post Checkout repository 2026-04-17T21:50:29.2690740Z http.https://github.com/.extraheader | ||
| Deploy Admin Post Checkout repository 2026-04-17T21:50:29.2702196Z [command]/usr/bin/git config --local --unset-all http.https://github.com/.extraheader | ||
| Deploy Admin Post Checkout repository 2026-04-17T21:50:29.2949472Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.https\:\/\/github\.com\/\.extraheader' && git config --local --unset-all 'http.https://github.com/.extraheader' || :" | ||
| Deploy Admin Post Checkout repository 2026-04-17T21:50:29.3161130Z [command]/usr/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir: | ||
| Deploy Admin Post Checkout repository 2026-04-17T21:50:29.3186091Z [command]/usr/bin/git submodule foreach --recursive git config --local --show-origin --name-only --get-regexp remote.origin.url | ||
| Deploy Admin Complete job 2026-04-17T21:50:29.3532055Z Cleaning up orphan processes | ||
| Deploy Admin Complete job 2026-04-17T21:50:29.3865757Z Terminate orphan process: pid (2261) (node) | ||
| Deploy Admin Complete job 2026-04-17T21:50:29.3892557Z Terminate orphan process: pid (2324) (node) | ||
| Deploy Admin Complete job 2026-04-17T21:50:29.3901920Z ##[warning]Node.js 20 actions are deprecated. The following actions are running on Node.js 20 and may not work as expected: actions/cache@v4, actions/checkout@v4, actions/setup-node@v4, pnpm/action-setup@v4. Actions will be forced to run with Node.js 24 by default starting June 2nd, 2026. Node.js 20 will be removed from the runner on September 16th, 2026. Please check if updated versions of these actions are available that support Node.js 24. To opt into Node.js 24 now, set the FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true environment variable on the runner or in your workflow file. Once Node.js 24 becomes the default, you can temporarily opt out by setting ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/ |
There was a problem hiding this comment.
Remove raw CI run logs from the repository
This file is a transient execution artifact, not source/config. Keeping full job logs in git adds high-churn noise and leaks unnecessary operational metadata (runner internals, workspace paths, dependency inventory, timestamps). Keep a short sanitized excerpt in docs instead, and attach full logs to PR/Actions artifacts.
🧰 Tools
🪛 LanguageTool
[uncategorized] ~85-~85: The official name of this software platform is spelled with a capital “H”.
Context: ...name-only --get-regexp http.https://github.com/.extraheader Deploy Admin Checko...
(GITHUB)
[uncategorized] ~86-~86: The official name of this software platform is spelled with a capital “H”.
Context: ...ame-only --get-regexp 'http.https://github.com/.extraheader' && git config --lo...
(GITHUB)
[style] ~397-~397: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...:50:29.1060031Z Pruning is unnecessary. Deploy Admin Post Checkout repository 2026-04...
(ENGLISH_WORD_REPEAT_BEGINNING_RULE)
[style] ~398-~398: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...-17T21:50:29.1255154Z Post job cleanup. Deploy Admin Post Checkout repository 2026-04-...
(ENGLISH_WORD_REPEAT_BEGINNING_RULE)
[uncategorized] ~406-~406: The official name of this software platform is spelled with a capital “H”.
Context: ...name-only --get-regexp http.https://github.com/.extraheader Deploy Admin Post C...
(GITHUB)
[uncategorized] ~409-~409: The official name of this software platform is spelled with a capital “H”.
Context: ...ame-only --get-regexp 'http.https://github.com/.extraheader' && git config --lo...
(GITHUB)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@failed_job_log.txt` around lines 1 - 415, Remove the committed CI run log
file (failed_job_log.txt) from the repository, commit the deletion (git rm
failed_job_log.txt; git commit -m "chore: remove CI run log"), add a rule to
.gitignore to prevent future checkins (e.g. failed_job_log.txt or a logs/
pattern), include a short sanitized excerpt in docs if needed, and if the log is
already in main history scrub it from the repo using a history-rewrite tool (git
filter-repo or BFG) and force-push the cleaned branch; attach full raw logs to
the GitHub Actions artifacts instead of committing them.
| Deploy Admin Complete job 2026-04-17T21:50:29.3532055Z Cleaning up orphan processes | ||
| Deploy Admin Complete job 2026-04-17T21:50:29.3865757Z Terminate orphan process: pid (2261) (node) | ||
| Deploy Admin Complete job 2026-04-17T21:50:29.3892557Z Terminate orphan process: pid (2324) (node) | ||
| Deploy Admin Complete job 2026-04-17T21:50:29.3901920Z ##[warning]Node.js 20 actions are deprecated. The following actions are running on Node.js 20 and may not work as expected: actions/cache@v4, actions/checkout@v4, actions/setup-node@v4, pnpm/action-setup@v4. Actions will be forced to run with Node.js 24 by default starting June 2nd, 2026. Node.js 20 will be removed from the runner on September 16th, 2026. Please check if updated versions of these actions are available that support Node.js 24. To opt into Node.js 24 now, set the FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true environment variable on the runner or in your workflow file. Once Node.js 24 becomes the default, you can temporarily opt out by setting ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/ |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Inspect action pins and Node24 opt-in readiness in workflows.
set -euo pipefail
echo "== Actions currently pinned in workflows =="
rg -n 'uses:\s*actions/(checkout|setup-node|cache)@|uses:\s*pnpm/action-setup@|FORCE_JAVASCRIPT_ACTIONS_TO_NODE24|ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION' .github/workflows
echo
echo "== List all action references (for upgrade audit) =="
rg -n 'uses:\s*[[:alnum:]_.-]+/[[:alnum:]_.-]+@' .github/workflowsRepository: FoushWare/elzatona_web
Length of output: 8644
🌐 Web query:
actions/checkout v5 Node.js 24 support GitHub
💡 Result:
Yes, actions/checkout v5 supports Node.js 24. It was updated to use the node24 runtime as part of its v5.0.0 release in August 2025. This requires a minimum GitHub Actions runner version of v2.327.1. Self-hosted runners must be updated to this version or newer, and may need Node.js 24 pre-installed in some cases. The action's action.yml specifies 'using: node24', and the README confirms "Checkout v5 now supports Node.js 24". Examples in GitHub docs use actions/checkout@v5 with Node.js setups including 24.x.
Citations:
- 1: https://github.com/actions/checkout
- 2: actions/checkout@v4...v5.0.0
- 3: actions/checkout@v4...v5
- 4: Breaking change with v5 release - can't run any builds - Parameter ''using: node24' is not supported actions/checkout#2240
- 5: https://github.com/actions/checkout/releases
- 6: https://docs.github.com/actions/guides/building-and-testing-nodejs
🌐 Web query:
actions/setup-node v5 Node.js 24 support
💡 Result:
Yes, actions/setup-node v5 fully supports Node.js 24. It can install Node.js 24 (e.g., node-version: '24'), as shown in official documentation and examples. Additionally, v5 itself runs on Node.js 24 (upgraded from Node 20), requiring GitHub Actions runners version v2.327.1 or later for compatibility.
Citations:
- 1: https://github.com/actions/setup-node/releases/v5.0.0
- 2: https://github.com/actions/setup-node/releases/tag/v5.0.0
- 3: https://github.com/actions/setup-node
- 4: https://github.com/actions/setup-node/blob/main/README.md
- 5: https://github.com/actions/setup-node?tab=readme-ov-file
- 6: https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md
- 7: https://github.com/marketplace/actions/setup-node-js-environment
🌐 Web query:
pnpm/action-setup v5 Node.js 24 support
💡 Result:
Yes, pnpm/action-setup v5 supports Node.js 24. It runs on Node.js 24 runtime, as updated in v5.0.0 (released March 17, 2026). The action.yml specifies "using: node24", changing from "node20" in v4. This ensures compatibility with GitHub Actions runners using Node.js 24, which became standard around March 2026. Note that this action installs pnpm but does not set up Node.js itself; pair it with actions/setup-node for Node.js 24.
Citations:
- 1: Should upgrade node20 to node24 pnpm/action-setup#182
- 2: Bump pnpm/action-setup from 4 to 5 actions/setup-node#1513
- 3: https://github.com/pnpm/action-setup
- 4: pnpm/action-setup@v4...v5
- 5: https://github.com/pnpm/action-setup/releases
- 6: pnpm/action-setup@fc06bc1
Upgrade all deprecated actions to v5 before June 2, 2026
All GitHub Actions pinned to v4 (actions/checkout, actions/setup-node, actions/cache, pnpm/action-setup) are running on deprecated Node.js 20. GitHub will force Node.js 24 as the default on June 2, 2026—just 6 weeks away. Node.js 20 runners are removed entirely on September 16, 2026.
Bump affected workflows to v5:
actions/checkout@v5actions/setup-node@v5actions/cache@v5pnpm/action-setup@v5
All v5 releases are Node.js 24-ready and already published. No functional changes required; this is a straightforward version bump across .github/workflows/. Prioritize this to avoid CI breakage when the default flips.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@failed_job_log.txt` at line 415, Update all GitHub workflow files that
reference actions pinned to v4 by replacing actions/checkout@v4,
actions/setup-node@v4, actions/cache@v4, and pnpm/action-setup@v4 with their v5
releases (actions/checkout@v5, actions/setup-node@v5, actions/cache@v5,
pnpm/action-setup@v5); search for those exact strings in your
.github/workflows/*.yml files and perform the version bumps, then run CI to
verify; if you need a temporary opt-in instead of bumping, set the
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true runner/workflow environment variable,
but prefer updating the action versions.
| <div className="absolute top-10 left-10 w-72 h-72 bg-indigo-300 dark:bg-indigo-600 rounded-full blur-[120px] animate-pulse" /> | ||
| <div className="absolute bottom-10 right-10 w-72 h-72 bg-purple-300 dark:bg-purple-600 rounded-full blur-[120px] animate-pulse delay-700" /> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check Tailwind version/config references for motion-safe/motion-reduce support.
rg -n -C2 '"tailwindcss"\s*:' --iglob 'package.json'
rg -n -C3 'motionSafe|motionReduce|motion-safe|motion-reduce|variants' --iglob 'tailwind*.{js,ts,cjs,mjs}'Repository: FoushWare/elzatona_web
Length of output: 542
🏁 Script executed:
fd -t f 'HeroSection.tsx' --full-pathRepository: FoushWare/elzatona_web
Length of output: 121
🏁 Script executed:
#!/bin/bash
# Find and check Tailwind config files for motion variant configuration
fd -t f 'tailwind.config' -e js -e ts -e cjs -e mjsRepository: FoushWare/elzatona_web
Length of output: 127
🏁 Script executed:
#!/bin/bash
# Check if HeroSection component exists and read relevant sections
if [ -f "libs/common-ui/src/components/molecules/HeroSection.tsx" ]; then
head -n 100 "libs/common-ui/src/components/molecules/HeroSection.tsx" | cat -n
fiRepository: FoushWare/elzatona_web
Length of output: 4325
🏁 Script executed:
cat -n apps/admin/tailwind.config.tsRepository: FoushWare/elzatona_web
Length of output: 4833
🏁 Script executed:
cat -n apps/website/tailwind.config.tsRepository: FoushWare/elzatona_web
Length of output: 9668
Add motion-safe/motion-reduce variants and aria-hidden to decorative blobs.
Lines 26–27 use animate-pulse without respecting prefers-reduced-motion. Users who prefer reduced motion for accessibility reasons (vestibular disorders, etc.) still see the continuous animation. Additionally, the decorative container should be marked aria-hidden since it serves no semantic purpose.
Proposed fix
- <div className="absolute top-0 left-1/2 -translate-x-1/2 w-full max-w-4xl h-full -z-10 opacity-30 pointer-events-none">
- <div className="absolute top-10 left-10 w-72 h-72 bg-indigo-300 dark:bg-indigo-600 rounded-full blur-[120px] animate-pulse" />
- <div className="absolute bottom-10 right-10 w-72 h-72 bg-purple-300 dark:bg-purple-600 rounded-full blur-[120px] animate-pulse delay-700" />
+ <div
+ aria-hidden="true"
+ className="absolute top-0 left-1/2 -translate-x-1/2 w-full max-w-4xl h-full -z-10 opacity-30 pointer-events-none"
+ >
+ <div className="absolute top-10 left-10 w-72 h-72 bg-indigo-300 dark:bg-indigo-600 rounded-full blur-[120px] motion-safe:animate-pulse motion-reduce:animate-none" />
+ <div className="absolute bottom-10 right-10 w-72 h-72 bg-purple-300 dark:bg-purple-600 rounded-full blur-[120px] motion-safe:animate-pulse motion-reduce:animate-none delay-700" />Tailwind 3.3+ includes built-in motion-safe and motion-reduce variants, so no config changes needed.
📝 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.
| <div className="absolute top-10 left-10 w-72 h-72 bg-indigo-300 dark:bg-indigo-600 rounded-full blur-[120px] animate-pulse" /> | |
| <div className="absolute bottom-10 right-10 w-72 h-72 bg-purple-300 dark:bg-purple-600 rounded-full blur-[120px] animate-pulse delay-700" /> | |
| <div | |
| aria-hidden="true" | |
| className="absolute top-0 left-1/2 -translate-x-1/2 w-full max-w-4xl h-full -z-10 opacity-30 pointer-events-none" | |
| > | |
| <div className="absolute top-10 left-10 w-72 h-72 bg-indigo-300 dark:bg-indigo-600 rounded-full blur-[120px] motion-safe:animate-pulse motion-reduce:animate-none" /> | |
| <div className="absolute bottom-10 right-10 w-72 h-72 bg-purple-300 dark:bg-purple-600 rounded-full blur-[120px] motion-safe:animate-pulse motion-reduce:animate-none delay-700" /> | |
| </div> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@libs/common-ui/src/components/molecules/HeroSection.tsx` around lines 26 -
27, In HeroSection (the two decorative blob <div>s that currently include
"animate-pulse"), make the animation respect user motion preferences by
replacing "animate-pulse" with "motion-safe:animate-pulse
motion-reduce:animate-none" and mark each decorative element as non-semantic by
adding aria-hidden="true"; locate the elements by their existing class strings
(e.g., "absolute top-10 left-10 w-72 h-72 bg-indigo-300..." and "absolute
bottom-10 right-10 w-72 h-72 bg-purple-300...") and apply these changes to both
blobs.
…rage) - Centralize studyPlans data into libs/utilities - Implement unit tests for guided learning components and utilities - Enhance studyPlan data coverage to 100% - Fix lucide-react mock for Trophy icon
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
libs/utilities/src/lib/test-utils/mocks/lucide-react.tsx (1)
65-114:⚠️ Potential issue | 🟡 Minor
Trophymissing fromlucideMockdefault export 🏆The named export
Trophywas added on line 23, but it was not added to thelucideMockobject. Any test reaching forlucideMock.Trophy(or consuming the default import) will getundefined, whileimport { Trophy }works fine. This also contradicts the PR summary which states the default export was updated to include it.🔧 Proposed fix
Award, + Trophy, ExternalLink,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@libs/utilities/src/lib/test-utils/mocks/lucide-react.tsx` around lines 65 - 114, The default export object lucideMock is missing the Trophy entry even though Trophy was added as a named export; update the lucideMock object to include Trophy (e.g., add "Trophy," alongside the other icon entries) so lucideMock.Trophy is defined and the default export matches the named exports.libs/utilities/src/lib/studyPlans.ts (1)
171-287:⚠️ Potential issue | 🟠 MajorKeep milestone task estimates aligned with the advertised plan duration.
The milestone task totals are much lower than
estimatedTotalTimefor the longer plans, especiallythree-months-comprehensiveat 9h of tasks vs 36h advertised. Either add the missing task coverage or derive/display a separate “guided tasks estimate” so users are not promised a plan length the milestones do not represent.Also applies to: 412-522, 648-699
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@libs/utilities/src/lib/studyPlans.ts` around lines 171 - 287, The plan's milestones (created via createMilestones and the milestones arrays) do not sum to the advertised estimatedTotalTime; update either the milestone entries or the metadata so they align: either increase/extend milestone estimatedTime values or add additional milestone objects to reach the advertised estimatedTotalTime for the affected plans (e.g., the three-months-comprehensive entry), or else compute and expose a derived "guidedTasksEstimate" from the sum of milestone. Ensure changes touch createMilestones input arrays and the estimatedTotalTime field consistently so that the sum of all milestone estimatedTime values equals (or is explicitly reflected by) the plan's estimatedTotalTime.
♻️ Duplicate comments (2)
apps/website/src/app/features/guided-learning/utils/plan-helpers.ts (2)
71-82:⚠️ Potential issue | 🟡 MinorReplace
any[]withunknown[]for milestone range input.This helper only reads
.length, sounknown[]preserves strict typing without weakening the API.Proposed fix
-export function getMilestoneRange(plans: { milestones?: any[] }[]): string { +export function getMilestoneRange(plans: { milestones?: unknown[] }[]): string {As per coding guidelines,
No any types in TypeScript without explicit eslint-disable and justification comment.#!/bin/bash # Verify remaining explicit any usage in guided-learning helpers. rg -n -C2 '\bany\b' --iglob '*plan-helpers.ts'🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/website/src/app/features/guided-learning/utils/plan-helpers.ts` around lines 71 - 82, The function signature for getMilestoneRange uses a loose any[] for milestones; update the parameter type to use unknown[] (e.g., plans: { milestones?: unknown[] }[]) since the function only reads .length, preserving stricter typing without changing behavior; update the declaration of getMilestoneRange accordingly and ensure no other uses in this file require widening back to any.
47-69:⚠️ Potential issue | 🟡 MinorSeparate question counts from hour estimates before rendering.
getQuestionsRangenow returnstotalQuestionswhen present, otherwiseduration.totalHours, but the returned value is still exposed asquestionsRangebyuseGuidedLearningPlans. That can silently render hours as “questions”; return a discriminated result or split this intogetQuestionsRangeandgetHoursRange.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/website/src/app/features/guided-learning/utils/plan-helpers.ts` around lines 47 - 69, getQuestionsRange currently returns either question counts or hour estimates and is surfaced as questionsRange by useGuidedLearningPlans, causing hours to be displayed as "questions"; change the API so the values are not conflated by either (A) split into two clear helpers getQuestionsRange and getHoursRange and have useGuidedLearningPlans expose both questionsRange and hoursRange, or (B) make getQuestionsRange return a discriminated union like { type: "questions" | "hours"; min: number; max: number } and update useGuidedLearningPlans to pass through the discriminant so callers can render appropriately; update references to getQuestionsRange and useGuidedLearningPlans to consume the new shape or the separate helper names.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@apps/website/src/app/features/guided-learning/components/__tests__/ActivePlanView.test.tsx`:
- Around line 111-123: The component currently falls back to rendering "1/0"
when milestones array is empty; change the rendering logic in ActivePlanView so
that when milestones.length === 0 it displays "0/0" (or hides the counter) by
computing displayIndex = 0 and total = milestones.length before formatting, and
update the test expectation in ActivePlanView.test.tsx to assert "0/0" instead
of "1/0"; adjust the code path that derives the counter (the logic using props
milestones and currentMilestoneId inside ActivePlanView) and update the test
assertion accordingly.
In `@libs/common-ui/src/common/__tests__/LearningModeSwitcher.test.tsx`:
- Around line 61-67: The test "applies scrolled styles when isScrolled is true"
in LearningModeSwitcher.test.tsx destructures an unused container from render;
remove the unused destructure by replacing "const { container } =
render(<LearningModeSwitcher isScrolled={true} />);" with a simple
"render(<LearningModeSwitcher isScrolled={true} />);" (keeping the existing
screen.getByRole assertions intact for the group role) and run the tests to
verify.
In `@tests/config/playwright.config.ts`:
- Around line 2-10: Update the Playwright config dotenv usage to fail loudly and
perform CI-aware validation: stop calling dotenv.config() without checks;
instead attempt dotenv.config({ path: path.resolve(__dirname,
"../../.env.test.local") }) and if it returns an error/failed parse in local
mode (when process.env.CI and process.env.GITHUB_ACTIONS are not set) throw an
error so tests don’t silently load the wrong file, and in CI mode (when
process.env.CI or process.env.GITHUB_ACTIONS is set) validate that required test
env vars (list the keys your tests need) exist in process.env before exporting
the config, throwing a clear error if any are missing; ensure you reference
dotenv.config() return values and use process.env.CI /
process.env.GITHUB_ACTIONS to branch behavior so there is no silent fallback to
the default .env.
---
Outside diff comments:
In `@libs/utilities/src/lib/studyPlans.ts`:
- Around line 171-287: The plan's milestones (created via createMilestones and
the milestones arrays) do not sum to the advertised estimatedTotalTime; update
either the milestone entries or the metadata so they align: either
increase/extend milestone estimatedTime values or add additional milestone
objects to reach the advertised estimatedTotalTime for the affected plans (e.g.,
the three-months-comprehensive entry), or else compute and expose a derived
"guidedTasksEstimate" from the sum of milestone. Ensure changes touch
createMilestones input arrays and the estimatedTotalTime field consistently so
that the sum of all milestone estimatedTime values equals (or is explicitly
reflected by) the plan's estimatedTotalTime.
In `@libs/utilities/src/lib/test-utils/mocks/lucide-react.tsx`:
- Around line 65-114: The default export object lucideMock is missing the Trophy
entry even though Trophy was added as a named export; update the lucideMock
object to include Trophy (e.g., add "Trophy," alongside the other icon entries)
so lucideMock.Trophy is defined and the default export matches the named
exports.
---
Duplicate comments:
In `@apps/website/src/app/features/guided-learning/utils/plan-helpers.ts`:
- Around line 71-82: The function signature for getMilestoneRange uses a loose
any[] for milestones; update the parameter type to use unknown[] (e.g., plans: {
milestones?: unknown[] }[]) since the function only reads .length, preserving
stricter typing without changing behavior; update the declaration of
getMilestoneRange accordingly and ensure no other uses in this file require
widening back to any.
- Around line 47-69: getQuestionsRange currently returns either question counts
or hour estimates and is surfaced as questionsRange by useGuidedLearningPlans,
causing hours to be displayed as "questions"; change the API so the values are
not conflated by either (A) split into two clear helpers getQuestionsRange and
getHoursRange and have useGuidedLearningPlans expose both questionsRange and
hoursRange, or (B) make getQuestionsRange return a discriminated union like {
type: "questions" | "hours"; min: number; max: number } and update
useGuidedLearningPlans to pass through the discriminant so callers can render
appropriately; update references to getQuestionsRange and useGuidedLearningPlans
to consume the new shape or the separate helper names.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: f87d730f-424a-4b16-821e-4143edfcab37
⛔ Files ignored due to path filters (1)
libs/common-ui/src/common/__snapshots__/ProgressTracker.test.tsx.snapis excluded by!**/*.snap
📒 Files selected for processing (14)
apps/website/src/app/features/guided-learning/components/__tests__/ActivePlanView.test.tsxapps/website/src/app/features/guided-learning/components/__tests__/LearningPlanCard.test.tsxapps/website/src/app/features/guided-learning/utils/plan-helpers.test.tsapps/website/src/app/features/guided-learning/utils/plan-helpers.tsapps/website/src/app/lib/network/routes/guided-learning/plans/route.tsapps/website/src/app/lib/studyPlans.tslibs/common-ui/src/common/LearningModeSwitcher.tsxlibs/common-ui/src/common/__tests__/LearningModeSwitcher.test.tsxlibs/common-ui/src/components/molecules/HeroSection.tsxlibs/utilities/src/index.tslibs/utilities/src/lib/studyPlans.spec.tslibs/utilities/src/lib/studyPlans.tslibs/utilities/src/lib/test-utils/mocks/lucide-react.tsxtests/config/playwright.config.ts
💤 Files with no reviewable changes (1)
- apps/website/src/app/lib/studyPlans.ts
✅ Files skipped from review due to trivial changes (2)
- apps/website/src/app/lib/network/routes/guided-learning/plans/route.ts
- libs/common-ui/src/common/LearningModeSwitcher.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- libs/common-ui/src/components/molecules/HeroSection.tsx
| it("handles zero milestones gracefully", () => { | ||
| render( | ||
| <ActivePlanView | ||
| plan={mockPlan} | ||
| milestones={[]} | ||
| currentMilestoneId={null} | ||
| onResume={onResumeMock} | ||
| onReset={onResetMock} | ||
| />, | ||
| ); | ||
| expect(screen.getByText("0%")).toBeInTheDocument(); | ||
| expect(screen.getByText("1/0")).toBeInTheDocument(); // index fallback to 0 + 1 / total 0 | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify how ActivePlanView computes and renders the current milestone counter.
rg -n -C4 'currentMilestone|milestone.*length|/\\s*\\{?|No milestones|ActivePlanView' --iglob '*ActivePlanView.tsx'Repository: FoushWare/elzatona_web
Length of output: 264
🏁 Script executed:
#!/bin/bash
# Find the ActivePlanView component file
fd -type f -name 'ActivePlanView.tsx' | head -5Repository: FoushWare/elzatona_web
Length of output: 236
🏁 Script executed:
#!/bin/bash
# Search for milestone counter rendering in the codebase
rg -n "currentMilestone\|milestones\[" --type ts --type tsx | grep -i activePlan | head -20Repository: FoushWare/elzatona_web
Length of output: 93
🏁 Script executed:
#!/bin/bash
# Find ActivePlanView component
find . -name 'ActivePlanView.tsx' -type f 2>/dev/null | grep -v node_modulesRepository: FoushWare/elzatona_web
Length of output: 143
🏁 Script executed:
#!/bin/bash
# Read the ActivePlanView component (not the test)
find . -name 'ActivePlanView.tsx' -type f 2>/dev/null | grep -v node_modules | grep -v test | head -1 | xargs cat -nRepository: FoushWare/elzatona_web
Length of output: 6836
The 1/0 milestone counter is confusing with zero milestones—update the component or test expectation.
Lines 101 in the component and line 122 in the test lock in 1/0 as the fallback for an empty milestone list. This is not a graceful user-facing value. When milestones = [], the counter becomes mathematically nonsensical. Update the component to show 0/0, hide the counter entirely, or display a message like "No milestones" instead.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/website/src/app/features/guided-learning/components/__tests__/ActivePlanView.test.tsx`
around lines 111 - 123, The component currently falls back to rendering "1/0"
when milestones array is empty; change the rendering logic in ActivePlanView so
that when milestones.length === 0 it displays "0/0" (or hides the counter) by
computing displayIndex = 0 and total = milestones.length before formatting, and
update the test expectation in ActivePlanView.test.tsx to assert "0/0" instead
of "1/0"; adjust the code path that derives the counter (the logic using props
milestones and currentMilestoneId inside ActivePlanView) and update the test
assertion accordingly.
| import path from "path"; | ||
| import dotenv from "dotenv"; | ||
|
|
||
| /** | ||
| * Read environment variables from file. | ||
| * https://github.com/motdotla/dotenv | ||
| */ | ||
| dotenv.config({ path: path.resolve(__dirname, "../../.env.test.local") }); | ||
| dotenv.config(); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Description: Inspect Playwright env-loading configs and validation helper.
# Expected: root config should fail fast like the website config and invoke requireTestEnvironment().
sed -n '1,90p' tests/config/playwright.config.ts
printf '\n--- matching validation helpers ---\n'
fd -a 'validate-test-env\.ts$' . | xargs -r sed -n '1,140p'
printf '\n--- Playwright configs using requireTestEnvironment ---\n'
rg -n -C3 'requireTestEnvironment|dotenv\.config|config\(\{' --glob 'playwright.config.ts'Repository: FoushWare/elzatona_web
Length of output: 5191
🏁 Script executed:
# Check if validate-test-env.ts exists in root tests directory
find . -path '*/tests/e2e/validate-test-env.ts' -o -path '*/tests/validate-test-env.ts' | head -20
# Check the full structure of root tests directory
echo "=== Root tests directory structure ==="
ls -la tests/ 2>/dev/null || echo "tests/ not found at root"
ls -la tests/e2e/ 2>/dev/null || echo "tests/e2e/ not found at root"
# Check if there's a validate-test-env anywhere at the root level
find tests -name 'validate-test-env*' 2>/dev/null || echo "No validate-test-env found"
# Verify what directories exist at root
echo "=== Root level directories ==="
ls -d */ 2>/dev/null | head -20Repository: FoushWare/elzatona_web
Length of output: 800
🏁 Script executed:
# Check if root playwright config is actually used or if apps have their own
echo "=== Apps with Playwright configs ==="
find apps -name 'playwright.config.ts' | sort
# Check if root playwright config is referenced in package.json or scripts
echo -e "\n=== Root package.json playwright references ==="
cat package.json 2>/dev/null | grep -A5 -B5 'playwright\|e2e' || echo "No e2e script in root"
# Check app-level package.json scripts
echo -e "\n=== App-level e2e scripts ==="
for app in apps/*/; do
if [ -f "$app/package.json" ]; then
echo "=== $(basename $app) ==="
grep -A2 '"e2e"' "$app/package.json" || true
fi
doneRepository: FoushWare/elzatona_web
Length of output: 11150
Add error handling and CI-aware env validation to root Playwright config.
Lines 9–10 call dotenv.config() without checking for errors. If .env.test.local is missing or invalid, the second call on line 10 silently falls back to the default .env, which may contain non-test credentials. This violates graceful error handling and can cause E2E tests to run against the wrong environment.
Add CI detection and explicit error checks similar to apps/website/tests/config/playwright.config.ts. At minimum:
- Detect CI/test environment using
process.env.CI,process.env.GITHUB_ACTIONS, or test-specific env vars - In local mode, require
.env.test.localto exist and throw on load failure - In CI mode, validate required test variables are present before exporting the config
- Avoid silent fallback to default
.env
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/config/playwright.config.ts` around lines 2 - 10, Update the Playwright
config dotenv usage to fail loudly and perform CI-aware validation: stop calling
dotenv.config() without checks; instead attempt dotenv.config({ path:
path.resolve(__dirname, "../../.env.test.local") }) and if it returns an
error/failed parse in local mode (when process.env.CI and
process.env.GITHUB_ACTIONS are not set) throw an error so tests don’t silently
load the wrong file, and in CI mode (when process.env.CI or
process.env.GITHUB_ACTIONS is set) validate that required test env vars (list
the keys your tests need) exist in process.env before exporting the config,
throwing a clear error if any are missing; ensure you reference dotenv.config()
return values and use process.env.CI / process.env.GITHUB_ACTIONS to branch
behavior so there is no silent fallback to the default .env.
- Fix 11 code smells (accessibility, complex logic, unused imports) - Add full test coverage for Guided Learning plans API route - Update component tests to match refactored logic
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (3)
libs/common-ui/src/common/LearningModeSwitcher.tsx (1)
20-40: Minor:isSelfDirected = !isGuidedmisrepresents thenullstate, and the two color helpers are near-duplicates.
UserTypeis"guided" | "self-directed" | null(seelibs/contexts/src/lib/UserTypeContextSafe.tsx). WhenuserTypeisnull(brief window before the provider hydrates fromlocalStorage),isGuidedisfalse, so the Free Style button will renderaria-pressed={true}and its pulse/pill — which is misleading. Considerconst isSelfDirected = userType === "self-directed";and let neither be "pressed" until hydration resolves.While you're here, the two
getXxxTextColorhelpers are identical except for the active flag — easy DRY win:♻️ Suggested refactor
- const isGuided = userType === "guided"; - const isSelfDirected = !isGuided; - - // Determine text colors based on state to avoid nested ternaries in JSX - const getGuidedTextColor = () => { - if (isGuided) { - return isScrolled ? "text-white" : "text-indigo-900 dark:text-white"; - } - return isScrolled - ? "text-gray-500 hover:text-gray-700 dark:text-gray-400" - : "text-white/70 hover:text-white"; - }; - - const getSelfDirectedTextColor = () => { - if (isSelfDirected) { - return isScrolled ? "text-white" : "text-indigo-900 dark:text-white"; - } - return isScrolled - ? "text-gray-500 hover:text-gray-700 dark:text-gray-400" - : "text-white/70 hover:text-white"; - }; + const isGuided = userType === "guided"; + const isSelfDirected = userType === "self-directed"; + + // Shared text-color resolver (active vs idle, scrolled vs transparent nav) + const getTextColor = (active: boolean) => { + if (active) { + return isScrolled ? "text-white" : "text-indigo-900 dark:text-white"; + } + return isScrolled + ? "text-gray-500 hover:text-gray-700 dark:text-gray-400" + : "text-white/70 hover:text-white"; + };Then use
getTextColor(isGuided)/getTextColor(isSelfDirected)in the JSX.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@libs/common-ui/src/common/LearningModeSwitcher.tsx` around lines 20 - 40, The current logic sets isSelfDirected = !isGuided which treats userType === null as self-directed and causes incorrect aria-pressed state; change isSelfDirected to userType === "self-directed" (keep isGuided as userType === "guided") so neither button is active during hydration, and collapse the two near-duplicate helpers getGuidedTextColor and getSelfDirectedTextColor into a single DRY function (e.g., getTextColor(activeFlag)) that returns the same color strings based on the passed active flag and isScrolled, then call getTextColor(isGuided) / getTextColor(isSelfDirected) in the JSX.apps/website/src/app/features/guided-learning/types/guided-learning.types.ts (1)
1-1: Use a type-only import forStudyPlan.
StudyPlanis only used in a type position (interface inheritance), soimport typeavoids unnecessary runtime import emission and signals the intent clearly.♻️ Proposed change
-import { StudyPlan } from "@elzatona/types"; +import type { StudyPlan } from "@elzatona/types";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/website/src/app/features/guided-learning/types/guided-learning.types.ts` at line 1, Replace the runtime import with a type-only import: change the existing import of StudyPlan to a type-only import (i.e., use "import type { StudyPlan } from '...';") in the guided-learning types file so StudyPlan is only emitted for TypeScript type-checking; update the existing import statement that currently reads "import { StudyPlan } ..." to use "import type" referencing the StudyPlan symbol.apps/website/src/app/lib/network/routes/guided-learning/plans/__tests__/route.test.ts (1)
80-93: Cover theidde-duplication contract in the merge test.The route filters DB rows that duplicate hardcoded plan IDs, but this test only covers unique DB rows.
Proposed test strengthening
- it("merges hardcoded plans with DB plans", async () => { + it("merges hardcoded plans with DB plans and deduplicates by id", async () => { mockSupabase.order.mockResolvedValue({ - data: [{ id: "db1", title: "DB Plan 1" }], + data: [ + { id: "h1", title: "Duplicate DB Plan" }, + { id: "db1", title: "DB Plan 1" }, + ], error: null, }); @@ - expect(json.data.length).toBe(2); + expect(json.data).toHaveLength(2); + expect(json.data).toContainEqual({ id: "h1", title: "Hardcoded Plan 1" }); + expect(json.data).toContainEqual({ id: "db1", title: "DB Plan 1" }); + expect( + json.data.filter((plan: { id: string }) => plan.id === "h1"), + ).toHaveLength(1); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/website/src/app/lib/network/routes/guided-learning/plans/__tests__/route.test.ts` around lines 80 - 93, The test "merges hardcoded plans with DB plans" should assert the route de-duplicates DB rows that share IDs with hardcoded plans: update the mocked DB response in the test (mockSupabase.order) to include both a unique DB row and a DB row whose id equals one of the hardcoded plan IDs, call GET as before, and then assert that the merged json.data contains only one entry for that duplicated id (e.g. expect(json.data.filter(p => p.id === '<hardcoded-id>').length).toBe(1)) and that the total length equals the count of unique plan IDs after deduplication.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@apps/website/src/app/lib/network/routes/guided-learning/plans/__tests__/route.test.ts`:
- Around line 146-156: The test "deletes a plan successfully" only checks a 200
and success flag but doesn't assert the deletion was scoped to the requested id;
update the test that calls DELETE(req) to also verify that only the plan with id
"delete-me" was removed and other plans remain—e.g., after DELETE(req) assert
the plan with id "delete-me" cannot be fetched (or is absent in a list) while
another seeded plan (different id) still exists; reference the DELETE handler
and the createMockRequest call to locate where to add these assertions.
- Around line 25-30: The helper createMockRequest currently types body as any
and uses a truthy check that drops valid falsy JSON bodies; update the signature
to use body?: unknown and add an explicit return type of NextRequest, then
change the conditional that sets the request body to check body !== undefined
(not a truthy check) so values like false, 0, and null are correctly
JSON.stringified when present; reference the createMockRequest function to make
these edits.
In `@libs/common-ui/src/common/LearningModeSwitcher.tsx`:
- Around line 51-54: The wrapper div in LearningModeSwitcher currently uses
aria-label="Learning Mode Selection" but lacks an explicit role, so screen
readers may ignore it; update the outer container (the div using
containerClasses inside the LearningModeSwitcher component) to include
role="group" (or convert to a proper radiogroup if you prefer mutually-exclusive
semantics) so the aria-label is announced—modify the JSX for the same div
element to add role="group" while keeping the existing aria-label and
classNames.
- Around line 63-82: The two toggle buttons in LearningModeSwitcher.tsx (the
elements that call setUserType("guided") and setUserType("self-directed") and
use isGuided/isSelfDirected, getGuidedTextColor/getSelfDirectedTextColor) lack
an explicit type and will default to type="submit" inside forms; update both
button elements to include type="button" to prevent accidental form submission
(leave existing onClick, aria-pressed and className logic unchanged).
---
Nitpick comments:
In
`@apps/website/src/app/features/guided-learning/types/guided-learning.types.ts`:
- Line 1: Replace the runtime import with a type-only import: change the
existing import of StudyPlan to a type-only import (i.e., use "import type {
StudyPlan } from '...';") in the guided-learning types file so StudyPlan is only
emitted for TypeScript type-checking; update the existing import statement that
currently reads "import { StudyPlan } ..." to use "import type" referencing the
StudyPlan symbol.
In
`@apps/website/src/app/lib/network/routes/guided-learning/plans/__tests__/route.test.ts`:
- Around line 80-93: The test "merges hardcoded plans with DB plans" should
assert the route de-duplicates DB rows that share IDs with hardcoded plans:
update the mocked DB response in the test (mockSupabase.order) to include both a
unique DB row and a DB row whose id equals one of the hardcoded plan IDs, call
GET as before, and then assert that the merged json.data contains only one entry
for that duplicated id (e.g. expect(json.data.filter(p => p.id ===
'<hardcoded-id>').length).toBe(1)) and that the total length equals the count of
unique plan IDs after deduplication.
In `@libs/common-ui/src/common/LearningModeSwitcher.tsx`:
- Around line 20-40: The current logic sets isSelfDirected = !isGuided which
treats userType === null as self-directed and causes incorrect aria-pressed
state; change isSelfDirected to userType === "self-directed" (keep isGuided as
userType === "guided") so neither button is active during hydration, and
collapse the two near-duplicate helpers getGuidedTextColor and
getSelfDirectedTextColor into a single DRY function (e.g.,
getTextColor(activeFlag)) that returns the same color strings based on the
passed active flag and isScrolled, then call getTextColor(isGuided) /
getTextColor(isSelfDirected) in the JSX.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: fd9c0b4c-4c10-4c15-aa06-165ef8635218
📒 Files selected for processing (6)
apps/website/src/app/features/guided-learning/components/LearningPlanCard.tsxapps/website/src/app/features/guided-learning/types/guided-learning.types.tsapps/website/src/app/lib/network/routes/guided-learning/plans/__tests__/route.test.tsapps/website/src/app/lib/network/routes/guided-learning/plans/route.tslibs/common-ui/src/common/LearningModeSwitcher.tsxlibs/common-ui/src/common/__tests__/LearningModeSwitcher.test.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
- apps/website/src/app/lib/network/routes/guided-learning/plans/route.ts
- apps/website/src/app/features/guided-learning/components/LearningPlanCard.tsx
- libs/common-ui/src/common/tests/LearningModeSwitcher.test.tsx
| const createMockRequest = (url: string, method = "GET", body?: any) => { | ||
| return new NextRequest(url, { | ||
| method, | ||
| body: body ? JSON.stringify(body) : undefined, | ||
| }); | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -nP '\bany\b|eslint-disable.*no-explicit-any' apps/website/src/app/lib/network/routes/guided-learning/plans/__tests__/route.test.tsRepository: FoushWare/elzatona_web
Length of output: 143
Replace any with unknown and add explicit return type.
Line 25 uses body?: any which violates the no-explicit-any rule. Additionally, the truthy check drops valid falsy JSON bodies like false, 0, or null.
Fix
- const createMockRequest = (url: string, method = "GET", body?: any) => {
+ const createMockRequest = (
+ url: string,
+ method = "GET",
+ body?: unknown,
+ ): NextRequest => {
return new NextRequest(url, {
method,
- body: body ? JSON.stringify(body) : undefined,
+ body: body === undefined ? undefined : JSON.stringify(body),
});
};🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/website/src/app/lib/network/routes/guided-learning/plans/__tests__/route.test.ts`
around lines 25 - 30, The helper createMockRequest currently types body as any
and uses a truthy check that drops valid falsy JSON bodies; update the signature
to use body?: unknown and add an explicit return type of NextRequest, then
change the conditional that sets the request body to check body !== undefined
(not a truthy check) so values like false, 0, and null are correctly
JSON.stringified when present; reference the createMockRequest function to make
these edits.
| it("deletes a plan successfully", async () => { | ||
| const req = createMockRequest( | ||
| "http://localhost/api/guided-learning/plans?id=delete-me", | ||
| "DELETE", | ||
| ); | ||
| const response = await DELETE(req); | ||
| const json = await response.json(); | ||
|
|
||
| expect(response.status).toBe(200); | ||
| expect(json.success).toBe(true); | ||
| }); |
There was a problem hiding this comment.
Assert the delete is scoped to the requested plan ID.
This is the important safety property for the destructive path; the current test would still pass if the handler accidentally deleted without an id filter.
Proposed assertion
expect(response.status).toBe(200);
expect(json.success).toBe(true);
+ expect(mockSupabase.delete).toHaveBeenCalledTimes(1);
+ expect(mockSupabase.eq).toHaveBeenCalledWith("id", "delete-me");
});🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/website/src/app/lib/network/routes/guided-learning/plans/__tests__/route.test.ts`
around lines 146 - 156, The test "deletes a plan successfully" only checks a 200
and success flag but doesn't assert the deletion was scoped to the requested id;
update the test that calls DELETE(req) to also verify that only the plan with id
"delete-me" was removed and other plans remain—e.g., after DELETE(req) assert
the plan with id "delete-me" cannot be fetched (or is absent in a list) while
another seeded plan (different id) still exists; reference the DELETE handler
and the createMockRequest call to locate where to add these assertions.
| <div | ||
| aria-label="Learning Mode Selection" | ||
| className={`relative flex items-center p-1 rounded-xl transition-all duration-300 ${containerClasses}`} | ||
| > |
There was a problem hiding this comment.
aria-label on a plain <div> needs a role to actually be announced.
Most screen readers ignore aria-label on generic containers (no implicit role). To make "Learning Mode Selection" reach assistive tech, give the wrapper role="group" (or model it as a radiogroup with role="radio" buttons, since this is a mutually-exclusive selection).
♿ Suggested tweak
<div
+ role="group"
aria-label="Learning Mode Selection"
className={`relative flex items-center p-1 rounded-xl transition-all duration-300 ${containerClasses}`}
>As per coding guidelines: "Follow WCAG guidelines for accessibility and implement proper ARIA labels and roles".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@libs/common-ui/src/common/LearningModeSwitcher.tsx` around lines 51 - 54, The
wrapper div in LearningModeSwitcher currently uses aria-label="Learning Mode
Selection" but lacks an explicit role, so screen readers may ignore it; update
the outer container (the div using containerClasses inside the
LearningModeSwitcher component) to include role="group" (or convert to a proper
radiogroup if you prefer mutually-exclusive semantics) so the aria-label is
announced—modify the JSX for the same div element to add role="group" while
keeping the existing aria-label and classNames.
| <button | ||
| onClick={() => setUserType("guided")} | ||
| aria-pressed={isGuided} | ||
| className={`relative z-10 flex items-center justify-center space-x-2 px-4 py-1.5 rounded-lg transition-colors duration-300 ${getGuidedTextColor()}`} | ||
| > | ||
| <Compass size={16} className={isGuided ? "animate-pulse" : ""} /> | ||
| <span className="text-sm font-semibold whitespace-nowrap">Guided</span> | ||
| </button> | ||
|
|
||
| {/* Free Style Option */} | ||
| <button | ||
| onClick={() => setUserType("self-directed")} | ||
| aria-pressed={isSelfDirected} | ||
| className={`relative z-10 flex items-center justify-center space-x-2 px-4 py-1.5 rounded-lg transition-colors duration-300 ${getSelfDirectedTextColor()}`} | ||
| > | ||
| <Map size={16} className={isSelfDirected ? "animate-pulse" : ""} /> | ||
| <span className="text-sm font-semibold whitespace-nowrap"> | ||
| Free Style | ||
| </span> | ||
| </button> |
There was a problem hiding this comment.
Add type="button" to both toggles.
Without an explicit type, these default to type="submit". If this switcher ever gets dropped inside a <form> (navbar search, onboarding, etc.), clicking "Guided"/"Free Style" will submit the form. Cheap defensive fix:
🛡️ Diff
<button
+ type="button"
onClick={() => setUserType("guided")}
aria-pressed={isGuided}
...
<button
+ type="button"
onClick={() => setUserType("self-directed")}
aria-pressed={isSelfDirected}📝 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.
| <button | |
| onClick={() => setUserType("guided")} | |
| aria-pressed={isGuided} | |
| className={`relative z-10 flex items-center justify-center space-x-2 px-4 py-1.5 rounded-lg transition-colors duration-300 ${getGuidedTextColor()}`} | |
| > | |
| <Compass size={16} className={isGuided ? "animate-pulse" : ""} /> | |
| <span className="text-sm font-semibold whitespace-nowrap">Guided</span> | |
| </button> | |
| {/* Free Style Option */} | |
| <button | |
| onClick={() => setUserType("self-directed")} | |
| aria-pressed={isSelfDirected} | |
| className={`relative z-10 flex items-center justify-center space-x-2 px-4 py-1.5 rounded-lg transition-colors duration-300 ${getSelfDirectedTextColor()}`} | |
| > | |
| <Map size={16} className={isSelfDirected ? "animate-pulse" : ""} /> | |
| <span className="text-sm font-semibold whitespace-nowrap"> | |
| Free Style | |
| </span> | |
| </button> | |
| <button | |
| type="button" | |
| onClick={() => setUserType("guided")} | |
| aria-pressed={isGuided} | |
| className={`relative z-10 flex items-center justify-center space-x-2 px-4 py-1.5 rounded-lg transition-colors duration-300 ${getGuidedTextColor()}`} | |
| > | |
| <Compass size={16} className={isGuided ? "animate-pulse" : ""} /> | |
| <span className="text-sm font-semibold whitespace-nowrap">Guided</span> | |
| </button> | |
| {/* Free Style Option */} | |
| <button | |
| type="button" | |
| onClick={() => setUserType("self-directed")} | |
| aria-pressed={isSelfDirected} | |
| className={`relative z-10 flex items-center justify-center space-x-2 px-4 py-1.5 rounded-lg transition-colors duration-300 ${getSelfDirectedTextColor()}`} | |
| > | |
| <Map size={16} className={isSelfDirected ? "animate-pulse" : ""} /> | |
| <span className="text-sm font-semibold whitespace-nowrap"> | |
| Free Style | |
| </span> | |
| </button> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@libs/common-ui/src/common/LearningModeSwitcher.tsx` around lines 63 - 82, The
two toggle buttons in LearningModeSwitcher.tsx (the elements that call
setUserType("guided") and setUserType("self-directed") and use
isGuided/isSelfDirected, getGuidedTextColor/getSelfDirectedTextColor) lack an
explicit type and will default to type="submit" inside forms; update both button
elements to include type="button" to prevent accidental form submission (leave
existing onClick, aria-pressed and className logic unchanged).
- Fix empty catch block in guided learning plans route - Update website Jest config to collect coverage from all src files - Add unit tests for useActivePlan, useGuidedLearningPlans, and studyPlans to exceed 80% new code coverage
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (1)
apps/website/src/app/lib/network/routes/guided-learning/plans/route.ts (1)
46-82:⚠️ Potential issue | 🟠 MajorSupabase client init still sits outside the DB try/catch — hardcoded fallback isn't actually guaranteed.
If
getSupabaseClient()throws and thengetSupabaseClientWithAnonKey()also throws (e.g., missingNEXT_PUBLIC_SUPABASE_URL/anon key in a given environment), execution escapes to the outercatchat line 85 and the route returns a 500 — even though we already have perfectly good hardcoded plans ready to serve. The whole point of the refactor ("hardcoded-first") is defeated in exactly the failure mode it's meant to protect against.Additionally,
dbPlansare merged at line 75 with no shape check. Any legacylearning_plansrows still using the oldschedulestructure will leak into the response and breakuseGuidedLearningPlansconsumers that now expectmilestones[]+estimatedTotalTime.Suggested direction (move client init inside the inner try, and validate rows before merging):
🛠️ Proposed fix
- // Otherwise, get all learning plans - // Use anon key for public read access (this is a public endpoint) - let supabase; - try { - supabase = getSupabaseClient(); // Try service role first - } catch (clientError) { - console.warn( - "Supabase service client initialization failed, falling back to anon key:", - clientError, - ); - supabase = getSupabaseClientWithAnonKey(); - } - - // Import hardcoded featured plans - const { studyPlans: hardcodedPlans } = await import("@elzatona/utilities"); - - // Convert hardcoded plans to match the expected API structure if needed - // The new structure is already what we want - let plans = [...hardcodedPlans]; - - try { - // Try to fetch additional plans from Supabase if available - const { data: dbPlans, error } = await supabase - .from("learning_plans") - .select("*") - .order("created_at", { ascending: false }); - - if (!error && dbPlans && dbPlans.length > 0) { - // Merge DB plans with hardcoded plans, avoiding duplicates by ID - const hardcodedIds = new Set(plans.map((p) => p.id)); - const filteredDbPlans = dbPlans.filter((p) => !hardcodedIds.has(p.id)); - plans = [...plans, ...filteredDbPlans]; - } - } catch (dbError) { + // 📚 Hardcoded-first: guarantee a working response even if Supabase is down + const { studyPlans: hardcodedPlans } = await import("@elzatona/utilities"); + let plans: StudyPlan[] = [...hardcodedPlans]; + + try { + let supabase; + try { + supabase = getSupabaseClient(); + } catch (clientError) { + console.warn( + "⚠️ Supabase service client init failed, falling back to anon key:", + clientError, + ); + supabase = getSupabaseClientWithAnonKey(); + } + + const { data: dbPlans, error } = await supabase + .from("learning_plans") + .select("*") + .order("created_at", { ascending: false }); + + if (!error && Array.isArray(dbPlans) && dbPlans.length > 0) { + const hardcodedIds = new Set(plans.map((plan) => plan.id)); + const filteredDbPlans = dbPlans.filter( + (plan): plan is StudyPlan => + isMilestonePlan(plan) && !hardcodedIds.has(plan.id), + ); + plans = [...plans, ...filteredDbPlans]; + } + } catch (dbError) { console.warn( "⚠️ Database fetch failed for learning plans, using hardcoded only:", dbError, ); }…with a small guard helper at module scope:
import type { StudyPlan } from "@elzatona/types"; const isMilestonePlan = (plan: unknown): plan is StudyPlan => { if (typeof plan !== "object" || plan === null) return false; const candidate = plan as Partial<StudyPlan>; return ( typeof candidate.id === "string" && Array.isArray(candidate.milestones) && candidate.milestones.length > 0 ); };Worth adding a test case where both client factories throw, asserting the route still responds
200with hardcoded plans — the current__tests__/route.test.tsonly covers the post-client failure path.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/website/src/app/lib/network/routes/guided-learning/plans/route.ts` around lines 46 - 82, The Supabase clients are initialized outside the DB try/catch so if both getSupabaseClient() and getSupabaseClientWithAnonKey() throw the route will bubble a 500 instead of returning the hardcoded plans; move the Supabase initialization into the inner try block (call getSupabaseClient(), fall back to getSupabaseClientWithAnonKey() there, and if both throw just log and continue without throwing) and before merging dbPlans into plans validate each row with a module-scoped type guard (e.g., isMilestonePlan) that ensures id is string and milestones is a non-empty array (and optionally estimatedTotalTime) then filter dbPlans with that guard before merging; also add a test where both client factories throw asserting the route returns 200 with only hardcoded plans.
🧹 Nitpick comments (2)
apps/website/src/app/lib/network/routes/guided-learning/plans/route.ts (1)
58-58: Minor: dynamicimport()inside the hot path.
await import("@elzatona/utilities")on every GET buys nothing here (no code-splitting benefit on a server route, and the module is tiny static data). A top-levelimport { studyPlans } from "@elzatona/utilities";is simpler, avoids anawaiton the happy path, and makes the dependency visible to bundler tree-shaking analysis. Non-blocking.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/website/src/app/lib/network/routes/guided-learning/plans/route.ts` at line 58, Replace the dynamic import with a top-level static import: remove the line using `await import("@elzatona/utilities")` and instead add a top-level `import { studyPlans as hardcodedPlans } from "@elzatona/utilities";` so that the GET handler (in this route module) no longer awaits a module at runtime, the dependency is visible to the bundler, and the `hardcodedPlans` symbol is available directly to any function (e.g., the route handler) that uses it.apps/website/src/app/features/guided-learning/__tests__/page.test.tsx (1)
94-110: Assert the milestone prop rewiring, not just the title.This PR changes
GuidedLearningPageto passmilestonesandcurrentMilestoneIdintoActivePlanView, but the test would still pass if those props were omitted. Have the mock render those props and assert them here.Example assertion-focused adjustment
- ActivePlanView: ({ plan }: ActivePlanViewMockProps) => ( - <div data-testid="active-plan">{plan.title}</div> + ActivePlanView: ({ + plan, + milestones, + currentMilestoneId, + }: ActivePlanViewMockProps & { + milestones: Array<{ id: string }>; + currentMilestoneId: string | null; + }) => ( + <div data-testid="active-plan"> + <span>{plan.title}</span> + <span data-testid="milestone-count">{milestones.length}</span> + <span data-testid="current-milestone">{currentMilestoneId}</span> + </div> ), @@ (useActivePlan as jest.Mock).mockReturnValue({ currentPlan: { id: "p1", title: "Active Plan" }, - milestones: [], + milestones: [{ id: "m1" }], currentMilestoneId: "m1", resumePlan: jest.fn(), resetPlan: jest.fn(), selectPlan: jest.fn(), @@ render(<GuidedLearningPage />); expect(screen.getByTestId("active-plan")).toBeDefined(); expect(screen.getByText("Active Plan")).toBeDefined(); + expect(screen.getByTestId("milestone-count")).toHaveTextContent("1"); + expect(screen.getByTestId("current-milestone")).toHaveTextContent("m1"); });Based on learnings, “Applies to **/*.test.{ts,tsx} : Update affected unit tests (
*.test.tsx,*.test.ts) when code changes - update existing tests, add new tests for new functionality, remove obsolete tests.”🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/website/src/app/features/guided-learning/__tests__/page.test.tsx` around lines 94 - 110, The test currently only asserts the Active Plan title but not that GuidedLearningPage forwards milestones/currentMilestoneId; update the useActivePlan mock to include an explicit milestones array and currentMilestoneId (e.g., milestones: [{id: "m1", title: "Milestone 1"}], currentMilestoneId: "m1") and then mock the ActivePlanView component (ActivePlanView as jest.Mock) and assert it was rendered with those props (expect(ActivePlanView).toHaveBeenCalledWith(expect.objectContaining({milestones: expect.any(Array), currentMilestoneId: "m1"}), expect.anything())). Ensure you reference useActivePlan, GuidedLearningPage, and ActivePlanView in the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/website/jest.config.js`:
- Around line 16-23: The collectCoverageFrom glob currently pulls non-source
files and co-located tests; update the collectCoverageFrom array (in
jest.config.js) to target only source under common-ui by changing
"../../libs/common-ui/**/*.{js,jsx,ts,tsx}" to
"../../libs/common-ui/src/**/*.{js,jsx,ts,tsx}" and add explicit exclusions for
co-located test files (e.g., exclude patterns like "!**/*.test.{ts,tsx,js,jsx}",
"!**/*.integration.test.{ts,tsx,js,jsx}" or a general "!**/*.test*" entry) so
test files such as *.test.tsx and *.integration.test.tsx are not counted in
coverage.
In `@apps/website/src/app/features/guided-learning/__tests__/page.test.tsx`:
- Around line 23-45: Replace all uses of `any` in the test mocks by declaring
explicit prop interfaces and typing the router: add interfaces
SignInCTABannerMockProps (with onSignIn: () => void), ActivePlanViewMockProps
(with plan: { title: string }), and PlanSelectionViewMockProps (with
onSelectPlan: (plan: { id: string }) => void) and use those in the jest.mock
component signatures instead of `any`; type `mockRouter` as Partial<NextRouter>
(import NextRouter from 'next/router') or the appropriate router type used in
the app, so the test accurately reflects prop shapes and router methods without
`any`.
In
`@apps/website/src/app/features/guided-learning/hooks/__tests__/useActivePlan.test.ts`:
- Around line 46-70: Remove the `as any` cast by typing `mockPlan` as
`LearningPlan`, then update the two tests around useActivePlan to assert
milestone progress persistence: in the select test (where
result.current.selectPlan is called) assert
localStorage.getItem("plan-progress-test-plan") === "m1" in addition to the
existing checks; in the reset test (where result.current.resetPlan is called)
assert localStorage.getItem("plan-progress-test-plan") is null; keep references
to the hook and methods (`useActivePlan`, `selectPlan`, `resetPlan`, and
`mockPlan`) so the assertions target the same behavior.
In `@apps/website/src/app/features/guided-learning/page.tsx`:
- Around line 63-66: The loading spinner (the div with
data-testid="loading-spinner") is visual-only; add accessibility attributes so
screen readers announce it: give that element role="status" and either
aria-live="polite" or an aria-label like "Loading…" and/or include a
visually-hidden text node (e.g., hidden span with "Loading…" for assistive tech)
inside the same component to convey progress to screen-reader users; update the
JSX in page.tsx where the spinner is rendered to include these attributes and/or
the hidden label.
---
Duplicate comments:
In `@apps/website/src/app/lib/network/routes/guided-learning/plans/route.ts`:
- Around line 46-82: The Supabase clients are initialized outside the DB
try/catch so if both getSupabaseClient() and getSupabaseClientWithAnonKey()
throw the route will bubble a 500 instead of returning the hardcoded plans; move
the Supabase initialization into the inner try block (call getSupabaseClient(),
fall back to getSupabaseClientWithAnonKey() there, and if both throw just log
and continue without throwing) and before merging dbPlans into plans validate
each row with a module-scoped type guard (e.g., isMilestonePlan) that ensures id
is string and milestones is a non-empty array (and optionally
estimatedTotalTime) then filter dbPlans with that guard before merging; also add
a test where both client factories throw asserting the route returns 200 with
only hardcoded plans.
---
Nitpick comments:
In `@apps/website/src/app/features/guided-learning/__tests__/page.test.tsx`:
- Around line 94-110: The test currently only asserts the Active Plan title but
not that GuidedLearningPage forwards milestones/currentMilestoneId; update the
useActivePlan mock to include an explicit milestones array and
currentMilestoneId (e.g., milestones: [{id: "m1", title: "Milestone 1"}],
currentMilestoneId: "m1") and then mock the ActivePlanView component
(ActivePlanView as jest.Mock) and assert it was rendered with those props
(expect(ActivePlanView).toHaveBeenCalledWith(expect.objectContaining({milestones:
expect.any(Array), currentMilestoneId: "m1"}), expect.anything())). Ensure you
reference useActivePlan, GuidedLearningPage, and ActivePlanView in the change.
In `@apps/website/src/app/lib/network/routes/guided-learning/plans/route.ts`:
- Line 58: Replace the dynamic import with a top-level static import: remove the
line using `await import("@elzatona/utilities")` and instead add a top-level
`import { studyPlans as hardcodedPlans } from "@elzatona/utilities";` so that
the GET handler (in this route module) no longer awaits a module at runtime, the
dependency is visible to the bundler, and the `hardcodedPlans` symbol is
available directly to any function (e.g., the route handler) that uses it.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 6c0d2ca1-1b07-415e-85b3-036e8b297c55
📒 Files selected for processing (7)
apps/website/jest.config.jsapps/website/src/app/features/guided-learning/__tests__/page.test.tsxapps/website/src/app/features/guided-learning/hooks/__tests__/useActivePlan.test.tsapps/website/src/app/features/guided-learning/hooks/__tests__/useGuidedLearningPlans.test.tsapps/website/src/app/features/guided-learning/page.tsxapps/website/src/app/lib/network/routes/guided-learning/plans/route.tslibs/utilities/src/lib/studyPlans.test.ts
| collectCoverageFrom: [ | ||
| "src/**/*.{js,jsx,ts,tsx}", | ||
| "../../libs/common-ui/**/*.{js,jsx,ts,tsx}", | ||
| "!**/*.d.ts", | ||
| "!**/node_modules/**", | ||
| "!**/__tests__/**", | ||
| "!**/test-utils/**", | ||
| ], |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Inspect common-ui JS/TS files that the broad coverage glob may include unintentionally.
# Expected: Ideally no "outside src" entries, and co-located test/spec files should be explicitly excluded.
fd -e js -e jsx -e ts -e tsx . libs/common-ui \
| sed 's#^\./##' \
| grep -Ev '(^|/)(node_modules|__tests__|test-utils)/|\.d\.ts$' \
| awk '
/\/src\/.*\.(test|spec)\.[jt]sx?$/ { print "co-located test/spec: " $0 }
!/\/src\// { print "outside src: " $0 }
' \
| head -200Repository: FoushWare/elzatona_web
Length of output: 3278
Exclude co-located test files from coverage and narrow the common-ui glob to source only.
The current ../../libs/common-ui/**/*.{js,jsx,ts,tsx} glob pulls in files outside src/ (e.g., vite.config.ts) and includes all co-located test files (36 instances: *.test.tsx, *.integration.test.tsx). The existing exclusion list doesn't cover them, causing coverage metrics to inflate. Tighten to src/ and add an explicit test-file exclusion.
Suggested fix
collectCoverageFrom: [
"src/**/*.{js,jsx,ts,tsx}",
- "../../libs/common-ui/**/*.{js,jsx,ts,tsx}",
+ "../../libs/common-ui/src/**/*.{js,jsx,ts,tsx}",
"!**/*.d.ts",
+ "!**/*.{test,spec}.{js,jsx,ts,tsx}",
"!**/node_modules/**",
"!**/__tests__/**",
"!**/test-utils/**",
],🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/website/jest.config.js` around lines 16 - 23, The collectCoverageFrom
glob currently pulls non-source files and co-located tests; update the
collectCoverageFrom array (in jest.config.js) to target only source under
common-ui by changing "../../libs/common-ui/**/*.{js,jsx,ts,tsx}" to
"../../libs/common-ui/src/**/*.{js,jsx,ts,tsx}" and add explicit exclusions for
co-located test files (e.g., exclude patterns like "!**/*.test.{ts,tsx,js,jsx}",
"!**/*.integration.test.{ts,tsx,js,jsx}" or a general "!**/*.test*" entry) so
test files such as *.test.tsx and *.integration.test.tsx are not counted in
coverage.
| // Mock the components to keep tests focused | ||
| jest.mock("../components", () => ({ | ||
| GuidedLearningHeader: () => <div data-testid="header" />, | ||
| SignInCTABanner: ({ onSignIn }: any) => ( | ||
| <button data-testid="signin-banner" onClick={onSignIn}> | ||
| Sign In CTA | ||
| </button> | ||
| ), | ||
| ActivePlanView: ({ plan }: any) => ( | ||
| <div data-testid="active-plan">{plan.title}</div> | ||
| ), | ||
| PlanSelectionView: ({ onSelectPlan }: any) => ( | ||
| <button | ||
| data-testid="select-plan" | ||
| onClick={() => onSelectPlan({ id: "p1" })} | ||
| > | ||
| Select Plan | ||
| </button> | ||
| ), | ||
| })); | ||
|
|
||
| describe("GuidedLearningPage", () => { | ||
| let mockRouter: any; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Verify explicit `any` is removed from the page test.
rg -nP '\bas\s+any\b|:\s*any\b' apps/website/src/app/features/guided-learning/__tests__/page.test.tsxRepository: FoushWare/elzatona_web
Length of output: 233
🏁 Script executed:
sed -n '20,50p' apps/website/src/app/features/guided-learning/__tests__/page.test.tsx | cat -nRepository: FoushWare/elzatona_web
Length of output: 1076
Replace any types with explicit prop and variable type definitions.
The mock component props and mockRouter use explicit any, violating the no-any rule. Define proper types for SignInCTABannerMockProps, ActivePlanViewMockProps, PlanSelectionViewMockProps, and the mockRouter variable to maintain type safety.
Suggested typing cleanup
+type SignInCTABannerMockProps = {
+ onSignIn: () => void;
+};
+
+type ActivePlanViewMockProps = {
+ plan: { title: string };
+};
+
+type PlanSelectionViewMockProps = {
+ onSelectPlan: (plan: { id: string }) => void;
+};
+
// Mock the components to keep tests focused
jest.mock("../components", () => ({
GuidedLearningHeader: () => <div data-testid="header" />,
- SignInCTABanner: ({ onSignIn }: any) => (
+ SignInCTABanner: ({ onSignIn }: SignInCTABannerMockProps) => (
<button data-testid="signin-banner" onClick={onSignIn}>
Sign In CTA
</button>
),
- ActivePlanView: ({ plan }: any) => (
+ ActivePlanView: ({ plan }: ActivePlanViewMockProps) => (
<div data-testid="active-plan">{plan.title}</div>
),
- PlanSelectionView: ({ onSelectPlan }: any) => (
+ PlanSelectionView: ({ onSelectPlan }: PlanSelectionViewMockProps) => (
<button
data-testid="select-plan"
onClick={() => onSelectPlan({ id: "p1" })}
>
Select Plan
</button>
),
}));
describe("GuidedLearningPage", () => {
- let mockRouter: any;
+ let mockRouter: { refresh: jest.Mock };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/website/src/app/features/guided-learning/__tests__/page.test.tsx` around
lines 23 - 45, Replace all uses of `any` in the test mocks by declaring explicit
prop interfaces and typing the router: add interfaces SignInCTABannerMockProps
(with onSignIn: () => void), ActivePlanViewMockProps (with plan: { title: string
}), and PlanSelectionViewMockProps (with onSelectPlan: (plan: { id: string }) =>
void) and use those in the jest.mock component signatures instead of `any`; type
`mockRouter` as Partial<NextRouter> (import NextRouter from 'next/router') or
the appropriate router type used in the app, so the test accurately reflects
prop shapes and router methods without `any`.
| it("should select a plan and update localStorage", () => { | ||
| const { result } = renderHook(() => useActivePlan(true)); | ||
|
|
||
| act(() => { | ||
| result.current.selectPlan(mockPlan as any); | ||
| }); | ||
|
|
||
| expect(result.current.currentPlan).toEqual(mockPlan); | ||
| expect(localStorage.getItem("active-guided-plan")).toBe( | ||
| JSON.stringify(mockPlan), | ||
| ); | ||
| expect(mockPush).toHaveBeenCalledWith("/guided-practice?plan=test-plan"); | ||
| }); | ||
|
|
||
| it("should reset plan and clear localStorage", () => { | ||
| localStorage.setItem("active-guided-plan", JSON.stringify(mockPlan)); | ||
| const { result } = renderHook(() => useActivePlan(true)); | ||
|
|
||
| act(() => { | ||
| result.current.resetPlan(); | ||
| }); | ||
|
|
||
| expect(result.current.currentPlan).toBeNull(); | ||
| expect(localStorage.getItem("active-guided-plan")).toBeNull(); | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Verify explicit `any` is removed from the useActivePlan test.
rg -nP '\bas\s+any\b|:\s*any\b' apps/website/src/app/features/guided-learning/hooks/__tests__/useActivePlan.test.tsRepository: FoushWare/elzatona_web
Length of output: 118
🏁 Script executed:
cat -n apps/website/src/app/features/guided-learning/hooks/__tests__/useActivePlan.test.tsRepository: FoushWare/elzatona_web
Length of output: 3539
🏁 Script executed:
fd -t f "useActivePlan" --exclude "*.test.ts" apps/website/src/app/features/guided-learningRepository: FoushWare/elzatona_web
Length of output: 134
🏁 Script executed:
cat -n apps/website/src/app/features/guided-learning/hooks/useActivePlan.tsRepository: FoushWare/elzatona_web
Length of output: 3725
Remove the as any cast and verify milestone progress storage in tests.
Line 50 uses as any which violates TypeScript strict typing guidelines. Additionally, the hook actively persists plan-progress-${plan.id} on plan selection (set to the first milestone) and clears it on reset, but these assertions are missing from the tests.
Type mockPlan as LearningPlan and add assertions for the milestone progress key in both the select and reset tests:
- Verify
localStorage.getItem("plan-progress-test-plan")equals"m1"after selection - Verify it's removed (null) after reset
Suggested fix
describe("useActivePlan", () => {
let mockPush: jest.Mock;
const mockPlan = {
+ id: "test-plan",
+ title: "Test Plan",
+ duration: { weeks: 1, hoursPerWeek: 10, totalHours: 10 },
+ difficulty: "beginner",
+ milestones: [{ id: "m1", title: "Milestone 1", tasks: [] }],
};
it("should select a plan and update localStorage", () => {
const { result } = renderHook(() => useActivePlan(true));
act(() => {
- result.current.selectPlan(mockPlan as any);
+ result.current.selectPlan(mockPlan);
});
expect(result.current.currentPlan).toEqual(mockPlan);
expect(localStorage.getItem("active-guided-plan")).toBe(
JSON.stringify(mockPlan),
);
+ expect(localStorage.getItem("plan-progress-test-plan")).toBe("m1");
expect(mockPush).toHaveBeenCalledWith("/guided-practice?plan=test-plan");
});
it("should reset plan and clear localStorage", () => {
localStorage.setItem("active-guided-plan", JSON.stringify(mockPlan));
+ localStorage.setItem("plan-progress-test-plan", "m1");
const { result } = renderHook(() => useActivePlan(true));
act(() => {
result.current.resetPlan();
});
expect(result.current.currentPlan).toBeNull();
expect(localStorage.getItem("active-guided-plan")).toBeNull();
+ expect(localStorage.getItem("plan-progress-test-plan")).toBeNull();
});🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/website/src/app/features/guided-learning/hooks/__tests__/useActivePlan.test.ts`
around lines 46 - 70, Remove the `as any` cast by typing `mockPlan` as
`LearningPlan`, then update the two tests around useActivePlan to assert
milestone progress persistence: in the select test (where
result.current.selectPlan is called) assert
localStorage.getItem("plan-progress-test-plan") === "m1" in addition to the
existing checks; in the reset test (where result.current.resetPlan is called)
assert localStorage.getItem("plan-progress-test-plan") is null; keep references
to the hook and methods (`useActivePlan`, `selectPlan`, `resetPlan`, and
`mockPlan`) so the assertions target the same behavior.
| <div | ||
| data-testid="loading-spinner" | ||
| className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-500" | ||
| ></div> |
There was a problem hiding this comment.
Expose the loading state to assistive technologies.
The spinner is currently visual-only. Add a status role and accessible label so screen-reader users receive the loading feedback too.
Accessibility fix
<div
data-testid="loading-spinner"
+ role="status"
+ aria-label="Loading guided learning"
className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-500"
- ></div>
+ >
+ <span className="sr-only">Loading guided learning...</span>
+ </div>As per coding guidelines, “Ensure responsive and accessible design in UI components.”
📝 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.
| <div | |
| data-testid="loading-spinner" | |
| className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-500" | |
| ></div> | |
| <div | |
| data-testid="loading-spinner" | |
| role="status" | |
| aria-label="Loading guided learning" | |
| className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-500" | |
| > | |
| <span className="sr-only">Loading guided learning...</span> | |
| </div> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/website/src/app/features/guided-learning/page.tsx` around lines 63 - 66,
The loading spinner (the div with data-testid="loading-spinner") is visual-only;
add accessibility attributes so screen readers announce it: give that element
role="status" and either aria-live="polite" or an aria-label like "Loading…"
and/or include a visually-hidden text node (e.g., hidden span with "Loading…"
for assistive tech) inside the same component to convey progress to
screen-reader users; update the JSX in page.tsx where the spinner is rendered to
include these attributes and/or the hidden label.
…nt errors - Consolidated Admin Questions API handler into shared utility library - Decomposed high-complexity routes (Plan Hierarchy, Progress Sync, Topics, Check Project) - Refactored Frontend Task Validator to use React 19 createRoot and modular evaluation - Cleaned up Environment Loaders and Learning Resources Service - Resolved lint errors by adding displayNames to memoized dashboard components - Verified fix for constant return value blocker in auth-config.ts
- Resolved critical ReactDOM.render blocker in frontend validator - Fixed ternary operator code smells in content management hooks - Improved type safety for guided learning utilities (RangeInfo refactor) - Consolidated and restored missing shared utility functions - Added null-safety guards and fixed JSX syntax in dashboard - Performed global formatting sweep to resolve Prettier CI blockers - Verified local build and lint successfully
- Refactor TopicFormModal for accessibility (semantic buttons) - Add aria-hidden to decorative checkbox icons - Implement 50+ unit tests for utility libraries (81.4% coverage) - Update SonarCloud workflow to include new coverage files - Apply Prettier formatting
- Refactored 'authorize' in auth-config.ts to resolve S3516 (always returns same value) - Refactored 'EnhancedDashboard.tsx' into 'useDashboardData' hook to reduce complexity - Fixed 'ReactDOM.createRoot' mock in website utility tests - Aligned error messages and logging in FrontendTaskValidator - Improved validation utility modularity
- Completed refactor of EnhancedDashboard for complexity - Fixed Prettier formatting in newly added tests - Verified all quality checks pass locally
…reduce cognitive complexity
…ator and questions-handler
…ties, and plan hierarchy to resolve SonarQube new code coverage gate
…overage - Resolved regex warnings in markdown-question-parser.ts - Removed unused imports and variables in dashboard and editor components - Fixed deprecated ZodIssue and global parseInt usage - Extracted complex ternary operations for better readability - Added 35+ unit tests for utilities, hooks, and API routes to exceed 80% coverage on new code
…s and API routes, and increase test coverage
…d ternaries S3358
…ngs in validation library
|


This PR implements a major performance overhaul of the CI/CD pipeline, reducing total build and deployment duration by ~80% (from 10m to under 3m).
Key Changes
Verification Results
Summary by CodeRabbit