# Samasyas Admin CRUD — Plan & Build Record

> **Status (2026-05-17):** ✅ Implemented. Retrospective plan + reference.
>
> **Goal:** Give the Samasyas (legal Q&A) content type the same admin CRUD as Articles/Circulars/Amendments, using the `samasyas` schema, and wire the previously-dead "Samasyas" sidebar link.

---

## 1. Schema

`samasyas` table (from `jotici (1).sql`):

```sql
CREATE TABLE `samasyas` (
  `id`                 INT(11)   NOT NULL,
  `category_id`        INT(11)   NOT NULL,                 -- FK → article_category.id (shared with Articles)
  `subcategory_id`     INT(11)       DEFAULT NULL,         -- FK → article_subcategory.id
  `samasya`            TEXT          DEFAULT NULL,         -- the question, plain text
  `samadhan`           LONGTEXT      DEFAULT NULL,         -- the solution, rich-text body
  `samadhan_strip_tag` LONGTEXT      DEFAULT NULL,
  `publish_date`       DATE          DEFAULT NULL,
  `created_at`         TIMESTAMP NOT NULL DEFAULT current_timestamp()
                                ON UPDATE current_timestamp()
);
```

Key relationships and quirks:

| Aspect | Reality |
|---|---|
| Taxonomy | **Shares `article_category` / `article_subcategory` tables with Articles.** Different rows are filtered out via the dual flag. |
| Category filter | `for_question = 1` (not `for_article = 1` like Articles). `ArticleCategoryController.php:123` documents the intent: "a category with neither flag is invisible to both dropdowns." |
| Question vs. solution | `samasya` (TEXT) is plain — public side renders it as `{{ $samasya->samasya }}` (escaped). `samadhan` (LONGTEXT) is rich-text HTML — public side renders it as `{!! $samasya->samadhan !!}`. Verified in `resources/views/users/partials/_samasya_list.blade.php`. |
| Strip-tag column | `samadhan_strip_tag`. Powers `MATCH(samasya, samadhan_strip_tag) AGAINST(...)` in `UserController@getsamasyas`. **Must stay populated on every save.** |
| `publish_date` | Nullable (unlike Circulars/Amendments where it's NOT NULL). |

---

## 2. Architecture Decisions

| Decision | Choice | Reason |
|---|---|---|
| CRUD shape | Page-based, six methods | Same as Articles/Circulars/Amendments. |
| Category dropdown source | `ArticleCategory::where('for_question', 1)` | Mirrors what the existing flag is designed for. Articles uses `for_article = 1`. |
| Subcategory cascade | Identical JS to Articles — Choices.js destroy/rebuild on category change, options filtered by `data-category` | Reuse pattern, not extract. Three uses (Articles, Samasyas, future) is below the "rule of three" threshold for a shared helper. |
| Subcategory belongs-to-category guard | `Validator::after()` block, identical to Articles | Subcategory dropdowns are client-filtered but anyone bypassing the JS could submit a mismatched pair. Same risk as Articles. |
| `samasya` field UX | Plain `<textarea>` (4 rows, maxlength 65535) | Public side escapes it — no rich text needed. |
| `samadhan` field UX | CKEditor 5 Classic | Public side renders raw HTML — needs rich text. |
| `samadhan_strip_tag` | Always recomputed on save via `strip_tags($samadhan)` + whitespace collapse | Critical for the FULLTEXT search index used by `UserController@getsamasyas`. |
| Validation | Inline `Validator::make` in `save()` | Project convention. |

**Explicitly NOT included** (minimal-scope rule):

- Public-side `UserController@samasyas` / `getsamasyas` — unchanged
- The `article_category.for_question` admin UI — already exists in `ArticleCategoryController`
- Other dead sidebar links (Research Articles, Journals)
- Extracting a shared "content CRUD" trait — premature; revisit if Journals follows the same shape

---

## 3. Validation Rules

```php
'id'             => 'nullable|integer',
'category_id'    => 'required|integer|exists:article_category,id',
'subcategory_id' => 'nullable|integer|exists:article_subcategory,id',
'samasya'        => 'required|string|max:65535',
'samadhan'       => 'required|string|max:5000000',
'publish_date'   => 'nullable|date',
```

Plus the after-hook: subcategory (if present) must reference the chosen category.

Attribute aliases for nicer error messages: `category_id` → "category", `subcategory_id` → "subcategory", `samasya` → "question", `samadhan` → "solution body".

---

## 4. Files

### Added

| Path | Purpose |
|---|---|
| `app/Http/Controllers/Admin/SamasyaController.php` | Six CRUD methods + `categoryOptions()` (filtered to `for_question=1`) + `subcategoryOptions()`. |
| `resources/views/admin/samasyas/index.blade.php` | DataTable list. |
| `resources/views/admin/samasyas/create.blade.php` / `edit.blade.php` | Page wrappers. |
| `resources/views/admin/samasyas/partials/form.blade.php` | Shared form. |
| `resources/views/admin/samasyas/partials/form_scripts.blade.php` | CKEditor on `#samadhan-input`, Flatpickr on publish date, Choices.js + cascade on category/subcategory. |

### Modified

| Path | Change |
|---|---|
| `app/Models/Samasya.php` | Added `$fillable` + `$casts`. Existing `category()` + `subcategory()` relations retained. |
| `routes/web.php` | Imported `AdminSamasyaController`; added `admin.samasyas.*` route group inside the `auth + role:super-admin` middleware, after amendments. |
| `resources/views/admin/partials/sidebar.blade.php` | Added `$isSamasyas`; included in `$contentOpen`; wired the dead `#` link. |

### Routes registered

```
GET|HEAD   admin/samasyas                  admin.samasyas.index
GET|HEAD   admin/samasyas/create           admin.samasyas.create
POST       admin/samasyas/data_table       admin.samasyas.data_table
POST       admin/samasyas/remove           admin.samasyas.remove
POST       admin/samasyas/save             admin.samasyas.save
GET|HEAD   admin/samasyas/{samasya}/edit   admin.samasyas.edit
```

---

## 5. Form Layout

1. **Row 1:** Category (5 cols, required, Choices.js) · Subcategory (4 cols, optional, cascade-filtered, no helper text under the dropdown) · Publish date (3 cols, optional, Flatpickr)
2. **Row 2:** Question / Samasya (full width, 4-row plain `<textarea>`, required)
3. **Row 3:** Solution / Samadhan (full width, CKEditor 5, required)
4. **Footer:** "Back to list" · "Save Samasya" / "Update Samasya"

> **2026-05-17 tweak:** The `<small>Options filter to match the chosen Category.</small>` helper text was removed from under the Subcategory dropdown (same removal applied to the Articles form for consistency). Cascade behaviour itself is unchanged.

DataTable columns:

`S.No. | Question | Category | Subcategory | Publish | Action`

Question column shows truncated `samasya` (first 80 chars via `Str::limit`). Searchable across `samasyas.samasya`, `samadhan_strip_tag`, category, subcategory. Default sort `samasyas.id DESC`.

---

## 6. Verification

| Check | Result |
|---|---|
| `php -l` on `SamasyaController.php`, `Samasya.php`, `routes/web.php` | No syntax errors. |
| `php artisan route:list --name=admin.samasyas` | 6 routes registered. |
| Blade compile + render harness | All views compile. (`$errors`-harness warning is expected — same as circulars/amendments.) |

**Not verified — needs browser session as `mpsjajournal@gmail.com`:**

- End-to-end create / edit / delete through admin UI
- DataTable AJAX payload on populated rows
- Category dropdown only shows `for_question=1` rows
- Subcategory cascade narrows correctly on category change
- "Subcategory must belong to chosen category" error path
- CKEditor + Flatpickr + Choices.js init cleanly

---

## 7. Follow-ups (not done)

- Journals admin CRUD — last remaining dead Content sidebar link with a known model.
- Research Articles admin CRUD — model exists (`ResearchArticle`), schema is its own thing; needs its own design pass.
- If Journals follows the same shape, consider extracting `Concerns\HandlesContentCrud` (rule of three).
- No tests added — same rationale as previous plans.
