> ## Documentation Index
> Fetch the complete documentation index at: https://theteam-13fc7f49.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Suggested Changes

> Future aspirations, planned refactors and advisory work — backend, frontend, clean-up and performance — collected from the frontend logs.

Everything below is **advisory or planned, not yet done**. Items come from the "Suggestions",
"TO BE FIXED (later)" and clean-up-plan sections of `LMS_Frontend/major_code_updates.md` and
`LMS_Frontend/clean_up.md`. Bugs that must be fixed live in
[Issues & Errors](https://theteam-13fc7f49.mintlify.site/changelog/issues-and-errors.md) instead.

✅ marks sub-items already shipped (kept here for context), ⚠️ marks items with a caveat to read
before acting, 🔥 marks the highest-value items.

Last checked against the code of `LMS_Backend` and `LMS_Frontend` (`chat+ai` branches) on
05/11/2026: every item without a ✅ is still open.

***

## 🔥 Backend — high-value changes

### ✅ Slim down the `/auth/user` login payload

✅ **Done 19/10/2026 (backend + app, Update 125).** `GET /auth/user` is now built field by field by
`AuthUserPayloadService` (about 1.5 MB → 20 KB): profile, company, role permissions, the newest
notifications and, for a student, the `my_trainings[]` / `my_exams[]` cards with a `progress` /
`attempts` summary the server computes. No exam question, option or attempt ships in it any more,
so the `QuizResource` whitelist that dropped `tip` / `quiz_question_type_id` no longer matters
(`?legacy=1` returned the old payload for one release). Kept here for context — see
[Authentication](https://theteam-13fc7f49.mintlify.site/api-reference/auth.md) and [Auth User Optimize](https://theteam-13fc7f49.mintlify.site/clean-up/auth-user-optimize.md). Only
the frontend's first-paint part (last bullet) is still open.

Original suggestion (Update 35 red flag): the student's whole "my exams" dataset — every question,
every option with `is_correct`, every attempt — shipped inside the login response and was cached
for the session. It was huge, stale by design, and its `QuizResource` whitelist silently dropped
columns.

* ✅ Strip exam `quiz_questions` (and ideally attempts) out of `/auth/user` — done 19/10/2026; only
  the attempt summary (used / allowed / best score / passed) is left.
* ✅ Consumers that need questions should fetch by id — done 19/10/2026: the exam review loads the
  attempt with its answers, then `GET /quiz/{id}`.
* ✅ The remaining `myExams` readers only need quiz **metadata** — done 19/10/2026: they read the
  cards' `quiz_questions_count` and `attempts`.
* **Don't add more data to the login payload.**
* 🔥 Frontend half (App Speed item #2, 08/10/2026) — **still open on a reload or a return visit.**
  ✅ 19/10/2026 (Update 126): a sign-in builds the dashboard from the `user` key of
  `POST /auth/login` itself (one round trip), and the theme paints from its cache. But the boot
  still waits for the whole `GET /auth/user` round trip behind `LoadingProgressWidget`
  (`app.dart`). Render the shell from the cached user (token + last user JSON in `SharedPrefs`) and
  refresh in the background; My Trainings / My Exams / Attempts then load with their own calls when
  the tab opens (they already have refresh methods).

### Harden the full-payload endpoints 🔥

Now that no student screen calls them (Update 44), make `GET /training/{id}`
(`TrainingPolicy::view` is `return true`) and `GET /quiz/{id}` (students hold `QUIZZES_VIEW` by
default) staff / owner-only — otherwise anyone who types the URL still gets lesson content and
answer keys. Tracked as a critical item in Issues & Errors.

### ✅ Fix `GET /training/{id}` chapter-quiz serialization, then delete the frontend backfill

✅ **Done 19/10/2026 (backend), together with multiple quizzes per chapter.** Every chapter
payload (`GET /training/{id}`, `GET /chapter(s)`, the login payload, the marketplace preview
outline and the chapter exports) now carries a `quizzes` array — all the chapter's quizzes, in
creation order, whether or not the chapter has lessons — plus the deprecated `quiz` (= the first
one) for older app builds. Students get the published quizzes, staff also get the drafts. A chapter
can hold any number of quizzes: creating a second one no longer turns the first into an exam (only
the training's final quiz stays one per training). Quiz and exam slugs are now generated by the
backend too. No lesson-dependent drop could be reproduced: the "missing" quiz was most likely a
**draft**, which `GET /chapter/{id}` (the app's backfill) returned and `GET /training/{id}` never
did. Kept here for context — see
[Chapters](https://theteam-13fc7f49.mintlify.site/api-reference/chapters.md#chapter-quizzes-in-the-payload) and
[Quizzes & Exams (v4)](https://theteam-13fc7f49.mintlify.site/api-reference/quizzes-and-exams-endpoints.md).

Original suggestion (Update 34): the endpoint omits a chapter's `quiz` when the chapter has no
lessons. Load `chapters.quiz.*` as a first-class sibling of `chapters.lessons`, then remove the
`GET /chapter/{id}` backfill in `TrainingQuizProvider.getTrainingWithQuizAttempts()`.

### Lightweight course-player loading

Two measured costs in the player (Update 37):

1. `GET /training/{id}` ships **every lesson's full HTML content** up front. Suggested: a structure
   endpoint (chapters/lessons without `content`) + per-lesson `GET /lesson/{id}` on demand, cached
   client-side.
2. ✅ **Done 19/10/2026 (app side, Update 125).** After "Mark as complete & continue" the app now
   updates the validated lesson ids locally and re-syncs the card's progress, so the
   `GET /user/{id}` round trip is gone; the `toggle_lesson_validation` response did not need to
   change. Original suggestion: "Mark as complete & continue" waited on a full `GET /user/{id}`
   round-trip — return the toggled lesson id/state in the `toggle_lesson_validation` response and
   update `currentUser.lessons` locally.

Item 1 is still open: `GET /training/{id}` still loads `chapters.lessons` with their content.

### One archive mode for the staff lists

Accept `archive_status=active|archived|all` (or `with_archived=1`) on the list endpoints and return
`archived_at` in every row. The frontend's "All records" view (Update 85) currently merges two
paginated lists at 2–3 requests per page — it would then send one request and drop the merge
(known issue 31).

### Gamification: weekly window + levels

`GET /progress/leaderboard` / `/progress/score` are all-time only and carry no level. Add a weekly
score (for example `?period=week`) and a `level` / tier per entry so the leaderboard card can render
the template's "This week" pill and "Level 42 – Diamond" lines (known issue 13).

### Lesson validation timestamps

A validated lesson carries no completion date, so the "Trainings in progress" card cannot show
"· 2 touched this week". Add the pivot `created_at` to the user's lessons (known issue 16).

### Marketplace payload finishing touches

* Add `total_quizzes_count` to `/preview-trainings` so the catalog cards can show the quizzes stat
  (known issue 3) — `Training.quizzesCount` already reads it.
* Remove `total_active_sales` from `MarketplacePreviewService::trainingCard` / `examCard` if sales
  figures should not reach students' network tabs at all (Update 47).

### Certifications: defense in depth

* ✅ DB-level unique constraints on `(student_id, training_id)` and `(student_id, quiz_id)`
  (Update 3's recommendation) — **already in the backend, checked 05/11/2026**: the migration
  `2026_04_17_000001_enforce_unique_certifications_per_target` deletes the duplicate rows, then adds
  the `certifications_student_training_unique` and `certifications_student_quiz_unique` indexes, so
  concurrent requests can no longer create a second certificate.
* Server-side ownership + completion checks in `CertificationsController::store` — this one is a
  **critical issue**, tracked in Issues & Errors (#1).
* Arabic certificate share images: bundle an Arabic font and shape the text (e.g. the
  `ar-php` glyph helper) or render that case with a headless browser (known issue 30).

### Notification e-mails: an archive copy (BCC) — Update 94 question

E-mails sent from the admin Reminders page never appear in a "Sent" folder: the backend queues one
`NotificationEmail` per recipient (`NotificationService`, `Mail::to(...)->queue`) through the
platform's SMTP account, not the admin's mailbox, and SMTP keeps no copies — the platform's own
record is the Recent log. Without code, an admin can tick their own account (or a team account that
is a platform user) as an extra recipient: each recipient gets a separate mail, so learners never
see that address; it counts toward the 10-recipient e-mail limit and adds an in-app notification
for that account. If wanted on the backend: a **BCC** archive address — for example a
`NOTIFICATIONS_ARCHIVE_BCC` setting added with `->bcc(...)` in `NotificationEmail::build()`, or a
per-company field next to the SMTP settings. BCC, never CC (CC shows the address to every learner).
⚠️ The "Platform access" template carries a password, so any archive mailbox would hold it too.

### AI assistant: move the per-training toggle to the backend

Update 38 shipped it with SharedPreferences persistence **by design**. The migration plan:
`trainings.ai_assistant_enabled` boolean column (default true), `nullable|boolean` validation with a
`request()->has()` guard, field in `$fillable` + resources; frontend then reads the payload field,
drops `ai_assistant_local_settings.dart`, and the field gets documented in
`api-reference/trainings.mdx`.

***

## 🔥 Frontend — aspirations & deferred features

### Boot-failure retry button (`app.dart`)

On a boot-time network failure, `authResultNotifier` never leaves `null` and the app sits on a
spinner / blank screen forever. Distinguish "loading" from "failed" and render an error view with a
**Retry** button that re-runs `_fetchAuthData()` (keys `noInternetError` / `anErrorHasOccurred`
already exist in all three locales).

### Real device push notifications (FCM) — investigated, deferred by owner

In-app notifications are pull-based today; nothing reaches a closed device. The full turn-key
checklist is written (Update 35-era suggestion block):

**1. External setup (owner)**

Create a Firebase project (Android + iOS apps, `flutterfire configure`); for iOS a paid Apple
Developer account + APNs Auth Key (.p8) uploaded to Firebase.

**2. Flutter app**

`firebase_core` + `firebase_messaging`, a `PushNotificationService` (token → backend after
login, cleared on logout), the three message states wired (foreground via the existing
`flutter_local_notifications`, tap deep-links via go\_router, background handler).

**3. Native shells**

Android google-services plugin; iOS Push capability + Background Modes + AppDelegate bridge.

**4. Backend**

A register-device-token endpoint and an FCM fan-out wherever a notification is created — without
this send-side, no push ever arrives.

⚠️ The bundle ID is still the placeholder `com.example.strived`; pick the real reverse-domain ID
**before** registering Firebase. ⚠️ Don't ship the app side half-wired and assume users get pushes.

### Shrink `main.dart.js` with deferred loading 🔥

Measured 08/10/2026 on the release build (Flutter 3.44.6, CanvasKit): `main.dart.js` is **12.2 MB
(3.2 MB gzipped)** and `deferred as` is used **0** times, so every visitor — a student included —
downloads the admin / super-admin / instructor / content-manager pages (Syncfusion DataGrid,
`fl_chart`, Quill / Fleather, `html_editor_enhanced`, the PDF viewer, the image cropper). Import
each role shell `deferred as` and `await loadLibrary()` in its GoRouter redirect / branch (Flutter
web emits one `.part.js` per deferred library); do the same for the single-screen heavy libraries.
The 08/10 dead-code deletion did **not** change the bundle size — dead code was already
tree-shaken; only code splitting shrinks it. Item #1 of the App Speed table below.

### App speed suggestions — 16 measured items (08/10/2026)

The full table, with the `grep` counts, the suggestion and the expected gain of each item, is on
the [Frontend Clean-Up](https://theteam-13fc7f49.mintlify.site/clean-up/frontend.md#app-speed-suggestions) page. The quick wins:

* **Trivial:** silence `debugPrint` in release (598 calls, 487 in providers — one line in `main()`,
  plus dropping `localStorage.debug = '*'` from `index.html`); ~~Sentry `tracesSampleRate`
  1.0 → 0.1~~ (✅ moot since 19/10/2026: Sentry was removed from the app, Update 122);
  the 15 `Opacity` widgets → tinted colors / `FadeTransition`; `RepaintBoundary` around the painted
  banners and the DataGrid (3 in the whole app today); nginx `Cache-Control` headers (`no-cache`
  for `index.html` / `main.dart.js`, a week for `canvaskit/*` and fonts).
* **Low:** drop the 59 `UniqueKey()` on routed pages (`student_statefulshell.dart` recreates a page
  on every shell rebuild — locale, theme or provider tick); `memCacheWidth` / `cacheWidth` on the
  104 network and asset images (none set today); a `ValueNotifier` clock for the six 1-second timers
  that rebuild whole exam screens; `FittedBox` / ellipsis instead of the 78 `AutoSizeText`.
* **Medium:** code splitting by role (above); first paint from the cached user (see the `/auth/user`
  item); `ListView.builder` for the 48 `shrinkWrap` lists; finer rebuilds (582 `notifyListeners()`,
  92 `context.watch`, `basic_info_screen.dart` watching one provider 12 times); `heroicons` (1,288
  bundled SVGs, 5.1 MB, 71 used) → Material icons or 71 local SVGs; fixed card heights per
  breakpoint instead of `IntrinsicHeight` rows in `GlassCardGrid`; ~~self-hosting
  `cropper.min.js`~~ (✅ gone: loaded on demand since 19/10/2026, Update 126, then removed with
  `image_cropper` on 28/10/2026, Update 166) and preloading Poppins.

✅ Applied so far: the review screens' `FlipCard` became a static swap (`ExplanationSwap`) and the
exam attempt review page lays out from 960px with the `StudentWebLayout.review*` sizes. Next: the
training review twins (`training_quiz_review_*`) onto the same tokens.

✅ Also applied since: on 19/10/2026 (Update 126) a sign-in builds the dashboard in one round trip,
the theme paints from its cache, Poppins ships as 4 Latin-subset faces (about 26 KB each instead of
155 KB) and Cropper.js left `index.html`; on 31/10/2026 (Update 183) the web app loads 150 KB less
font before its first frame. Still at 0: `deferred as` imports.

### Optional screenshot blocking on sensitive screens

Audited: screen capture is allowed everywhere (no `FLAG_SECURE`, no blocking package). If ever
wanted, decide app-wide vs per-screen first, then `FLAG_SECURE` on Android + a secure overlay on
iOS, or a package like `no_screenshot`.

### ✅ Marketplace tab badges: prefetch the non-active catalog

✅ **Done 21/10/2026 (Updates 138–139), without an extra request.** Page 1 of
`/preview-trainings`, `/preview-exams` and `/instructors` carries the backend's cached
`catalog_counts`, so every Marketplace tab badge shows on first landing, whichever tab is open
(known issue 44, resolved). Kept here for context.

Original suggestion (Update 9): on first landing in Marketplace, fetch the other catalog's first
page (or a lightweight count) so both tab badges show immediately — trigger from
`addPostFrameCallback`, only when the total is still null.

### Voucher success → proper in-app refresh

The "Go to My Content" button after applying a voucher does a **full page reload** on web, because
the earlier in-place refresh surfaced unowned trainings and was removed. The real fix:
`EnrolledTrainings` / `EnrolledQuizzes` freeze their lists in `initState` — a genuine refresh needs
the `Selector3` to rebuild with fresh references while My Content is mounted, after the
"shows unowned trainings" bug is understood. ⚠️ The mobile path still needs its own verify pass.

### Make the Swift Package Manager "off" switch repo-enforced

SPM off is a **machine-local override** (`~/.config/flutter/settings`) while Flutter 3.44 defaults
it on; a fresh machine or `flutter config --clear-features` reintroduces the Update 28 Sentry hang.
Options, in value order: (1) a preflight check that hard-errors when the flag is not `false`,
(2) document the command as mandatory per-machine setup, (3) re-test whether a newer
`sentry_flutter` removes the hang entirely. ⚠️ `~/.flutter` is the analytics file, not the feature
flags; `flutter_keyboard_visibility` is transitive and can't be bumped from `pubspec.yaml`.

Status 05/11/2026: ✅ option (2) is done — the README's iOS setup lists
`flutter config --no-enable-swift-package-manager` next to `brew install cocoapods`. ✅ The cause
behind option (3) is gone: `sentry_flutter` left the app on 19/10/2026 (Update 122) and its pods
are out of `ios/Podfile.lock`, so the Sentry SPM download that hung no longer happens. Nobody has
tested turning SPM back on yet, and `flutter_inappwebview_ios` (through `cloudflare_turnstile`) still
has no SPM support (known issue 60). Option (1), the preflight check, is still open.

### Smaller polish items

* Narrow the lesson column from 960px toward the template's \~720–800px for better line length —
  weigh against shrinking lesson images/PDFs (Update 40).
* Web course-player header: the "Off" pill reuses the `off` key whose fr/ar strings are discount
  wording — switch to a dedicated key such as `disabled` (Update 51 note).
* Dashboard enrolled-card meta row overflows a few px on narrow cards — shrink the avatar below a
  card-width threshold; don't hide it with `FittedBox`. ⚠️ The parity test deliberately drains these
  exceptions today; remove the drains when fixing.
* Below 600px a desktop browser shows undersized cards: branch the width on
  `ResponsiveWidget.isSmallScreen` (viewport) rather than `isMobileDevice` (device).
* Stale-package replacements, in the suggested order (clean-up table G.4, 08/10/2026): `tuple` →
  Dart 3 records (40 files, mechanical, touches the hottest `Selector`s), `auto_size_text` →
  `FittedBox` / `Text` ellipsis in the quiz screens, then `html_editor_enhanced` → Fleather's
  `ParchmentHtmlCodec` (the one that unlocks the `win32` 5 chain and five held packages), then
  `expandable`, `modal_side_sheet`, `fluid_dialog`, `styled_divider` whenever their screen is next
  touched. `qr_flutter` stays.
  * ✅ `styled_divider` removed 12/10/2026 (Update 100, the app's own dashed divider).
  * ✅ `html_editor_enhanced` removed 14/10/2026 (Update 113): replaced by `flutter_quill` rather
    than Fleather, which ended the `win32` 5 chain; the web editor is SunEditor since 25/10/2026
    (Update 153).
  * ✅ `modal_side_sheet` removed 28/10/2026 (Update 167, the app's own `BodyWithSideSheet`).
  * Still to do: `tuple`, `auto_size_text`, `expandable`, `fluid_dialog`.
* ✅ **Done 01/11/2026 (Update 188).** The Exam Catalog and the Instructors tab shared one Filters
  side-sheet flag (`ExamProvider.showSideSheetExam`, Update 90 note). The flag is gone: each
  Marketplace page opens its own filter drawer over the catalog, and since Update 190 (same day) the
  Instructors tab has only a search bar.

***

## 🧹 Repository clean-up & package upgrades → the Clean Up tab

The full clean-up plans now live in their own tab, with every table and the complete
installed-vs-latest package versions:

> [card] [Frontend Clean-Up (Flutter)](https://theteam-13fc7f49.mintlify.site/clean-up/frontend.md)
>
> graphify-out untracking (\~47 MB, still pending); the dead code (56 files + 11 assets), the 13
> unused packages and the Flutter 3.44.6 package bump — all ✅ done 08/10/2026, plus 27 more
> orphaned files ✅ deleted 23/10/2026; the full
> installed-vs-latest table with why 21 packages are held, the G.4 stale-package replacement
> plan, the asset audit and the 16 App Speed Suggestions.

> [card] [Backend Clean-Up (Laravel)](https://theteam-13fc7f49.mintlify.site/clean-up/backend.md)
>
> graphify-out untracking (\~12 MB), the duplicated abandoned CORS packages (✅ removed
> 19/10/2026), unused jwt-auth / geoip, the composer version table (✅ Laravel 12 since
> 25/10/2026, was Laravel 10 EOL 🔴), ✅ one PDF engine (wkhtmltopdf / Snappy removed
> 24/10/2026), Pint debt, and the backend optimization backlog.

Quick status (09/10/2026, D.2 updated 23/10/2026, re-checked 05/11/2026): frontend Sections
**B / C / D / D.2 / E / G.1 / G.2** are ✅ done (D.2, the 10 newly orphaned files, on 23/10/2026);
frontend **G.4** is 🔶 half done (`flip_card` 08/10, `styled_divider` 12/10,
`html_editor_enhanced` 14/10 and `modal_side_sheet` 28/10/2026 replaced; `tuple`,
`auto_size_text`, `expandable` and `fluid_dialog` left); frontend **A** (graphify-out,
`.DS_Store`s, stray files) and **F** are ⬜ pending. Backend: ✅ the duplicated CORS packages
removed 19/10/2026 (Update 126), ✅ one PDF engine (dompdf) 24/10/2026, ✅ Laravel 10 → 12.70.1
25/10/2026 (Sanctum 4, PHPUnit 11); the rest of the backend plan (graphify-out, jwt-auth / geoip,
images, Pint, lang folders, the Telescope tables) is ⬜ pending.

***

## ⚡ Web performance — the nginx fix (compression live, caching not yet applied) 🔶

Update 33's cold-load diagnosis: `canvaskit.wasm` ships **7.2 MB uncompressed** and nothing carries
a `Cache-Control` header, so every repeat visit re-validates \~10 MB. The fix is Plesk → nginx
directives on each vhost (no rebuild needed): enable gzip/brotli for `application/wasm` (and json /
fonts), mark the content-hashed assets `public, max-age=31536000, immutable`, and keep
`index.html` + `flutter_service_worker.js` at `no-cache`.

Status 05/11/2026 (checked on `elearning.theteam.com.tn` with the command below):
✅ compression is live — `canvaskit.wasm`, `main.dart.js` and `index.html` come back with
`content-encoding: gzip`. Still open: none of them carries a `Cache-Control` header, so repeat
visits still re-validate everything. (The service-worker line no longer applies: the web app has no
service worker since Update 127.) Verify with:

```bash theme={null}
curl -sI -H "Accept-Encoding: br, gzip" \
  https://elearning.theteam.com.tn/canvaskit/canvaskit.wasm \
  | grep -iE "content-encoding|content-length|cache-control"
```

✅ Already done on the code side: Poppins trimmed from 18 faces to 6 (\~2.1 MB freed), then to 4
Latin-subset faces on 19/10/2026 (Update 126), and 150 KB less font before the first frame on
31/10/2026 (Update 183). The deferred loading item (above) is step 3, after measuring.

***

## 📌 Reference links kept from the clean-up notes

(Also listed on the [Frontend Clean-Up](https://theteam-13fc7f49.mintlify.site/clean-up/frontend.md) page.)

* [shadcn\_flutter](https://pub.dev/packages/shadcn_flutter)
* [flutter\_quill](https://pub.dev/packages/flutter_quill)
* [quill\_native\_bridge](https://pub.dev/packages/quill_native_bridge)
* [shadcnstudio.com/illustrations](https://shadcnstudio.com/illustrations)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.