# Amendments Admin CRUD — Plan & Build Record

> **Status (2026-05-17):** ✅ Implemented. Retrospective plan + reference, same format as `circulars-admin-plan.md`.
>
> **Goal:** Give the Amendments content type the same admin CRUD surface as Articles/Circulars, using the `amendements` table's own schema, and wire the previously-dead "Amendments" sidebar link.

---

## 1. Schema

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

```sql
CREATE TABLE `amendements` (
  `id`               INT(11)   NOT NULL,
  `subject_id`       INT(11)   NOT NULL,                 -- FK → circular_subject.id (shared with Circulars)
  `category`         VARCHAR(100) DEFAULT NULL,          -- free-text, public side ignores it
  `title`            VARCHAR(255) DEFAULT NULL,
  `details`          LONGTEXT     DEFAULT NULL,          -- rich-text body
  `detail_strip_tag` LONGTEXT     DEFAULT NULL,          -- ⚠ singular `detail_`, not `details_`
  `publish_date`     DATE      NOT NULL,
  `created_at`       TIMESTAMP NOT NULL DEFAULT current_timestamp()
                              ON UPDATE current_timestamp()
);
```

Schema quirks worth remembering:

| Quirk | Reality |
|---|---|
| Model name | `App\Models\Amendement` (missing the second 'n') — kept as-is to avoid breaking existing public-side code. |
| Table name | `amendements` (same misspelling) — also unchanged. |
| Strip-tag column | `detail_strip_tag` (singular). Circulars uses `details_strip_tag` (plural). Don't unify. |
| Taxonomy | `subject_id` → `circular_subject` table. **Shared lookup with Circulars.** No new taxonomy needed. |
| Date fields | One only: `publish_date` (NOT NULL). No two-date split like Circulars. |
| `category` column | Free-text. ~30 distinct values exist, mixed style (`'central'`, `'state'`, `'INDIAN PENAL CODE'`, …). `UserController@getamendements` never filters on it — search is over `subject_id`, `title`, `detail_strip_tag`, `publish_date`. |

---

## 2. Architecture Decisions

| Decision | Choice | Reason |
|---|---|---|
| CRUD shape | Page-based, six methods | Same as Articles/Circulars (rich-text body doesn't fit a modal). |
| URL / route names | `admin.amendments.*` (correctly spelled) | Matches sidebar label and existing `user.amendments` public route. The misspelled `Amendement` is contained inside the model layer only. |
| `category` field UX | Free-text input with native `<datalist>` autocomplete | Preserves the column's free-form nature, suggests existing distinct values, requires zero extra JS or schema work. Decision confirmed with user before designing. |
| `detail_strip_tag` | Always recomputed on save from `strip_tags($details)` + whitespace collapse | **Critical** — the public site's amendments search uses `MATCH(title, detail_strip_tag) AGAINST(...) IN BOOLEAN MODE`. If admins ever save rows with NULL `detail_strip_tag`, those rows become invisible to user search. |
| Subject options | Reuse `CircularSubject::orderBy('subject')` | Same lookup table as Circulars; same admin module already exists. |
| Category suggestions | `Amendement::distinct()->pluck('category')` at form load | Cheap query, no caching layer needed. Worst case it grows to a few hundred values. |
| Validation | Inline `Validator::make` in `save()` | Project convention. |

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

- Fixing `Amendement` → `Amendment` class/table rename.
- Normalising or migrating the `category` free-text into a proper lookup table.
- Other dead sidebar links (Research Articles, Samasyas, Journals).
- Changes to `UserController@amendments` / `getamendements` / export methods — untouched.
- Changes to `CircularSubject` admin (the shared subject lookup).

---

## 3. Validation Rules (in `AmendmentController::save`)

```php
'id'           => 'nullable|integer',
'subject_id'   => 'required|integer|exists:circular_subject,id',
'category'     => 'nullable|string|max:100',
'title'        => 'required|string|max:255',
'publish_date' => 'required|date',
'details'      => 'required|string|max:5000000',
```

Attribute aliases: `subject_id` → "subject", `publish_date` → "publish date", `details` → "details body".

---

## 4. Files

### Added

| Path | Purpose |
|---|---|
| `app/Http/Controllers/Admin/AmendmentController.php` | Six CRUD methods + `subjectOptions()` + `categorySuggestions()` private helpers. |
| `resources/views/admin/amendments/index.blade.php` | DataTable list page. |
| `resources/views/admin/amendments/create.blade.php` | New-amendment page wrapper. |
| `resources/views/admin/amendments/edit.blade.php` | Edit-amendment page wrapper. |
| `resources/views/admin/amendments/partials/form.blade.php` | Shared form (subject, category w/ datalist, title, publish date, details). |
| `resources/views/admin/amendments/partials/form_scripts.blade.php` | CKEditor 5 on `#amendment-details`, Flatpickr on publish date, Choices.js on subject. Category uses native `<datalist>`. |

### Modified

| Path | Change |
|---|---|
| `app/Models/Amendement.php` | Added `$fillable` (all 7 mass-assignable columns) + `$casts` (`subject_id`→int, `publish_date`→date, `created_at`→datetime). `$timestamps = false` retained. `subject()` relation kept as-is. |
| `routes/web.php` | Imported `AdminAmendmentController`; added `admin.amendments.*` route group inside the existing `auth + role:super-admin` middleware, after the circulars group. |
| `resources/views/admin/partials/sidebar.blade.php` | Added `$isAmendments = request()->routeIs('admin.amendments.*')`; included in `$contentOpen`; replaced dead `#` link with real route. |

### Routes registered

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

---

## 5. Form Layout

1. **Row 1:** Subject (6 cols, required, Choices.js) · Category (6 cols, optional, `<datalist>` autocomplete)
2. **Row 2:** Title (full width, required, max 255)
3. **Row 3:** Publish Date (3 cols, required, Flatpickr `dd-mm-Y` ↔ `Y-m-d`)
4. **Row 4:** Details (full width, CKEditor 5 Classic, min-height 500px)
5. **Footer:** "Back to list" link · "Save Amendment" / "Update Amendment" button

DataTable columns:

`S.No. | Subject | Title | Category | Publish | Action`

Searchable across `amendements.title`, `amendements.category`, `amendements.detail_strip_tag`, `circular_subject.subject`. Default sort: `amendements.id DESC`.

---

## 6. Verification

Run during build:

| Check | Result |
|---|---|
| `php -l` on `AmendmentController.php`, `Amendement.php`, `routes/web.php` | No syntax errors. |
| `php artisan route:list --name=admin.amendments` | 6 routes registered. |
| Blade compile + render harness | All views compile. The `$errors` warning during standalone render is a harness artifact (`ShareErrorsFromSession` middleware only runs during a real HTTP request); Circulars views fail the harness the same way and work in-browser. |

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

- End-to-end create / edit / delete through the admin UI
- DataTable AJAX payload on a populated table
- `<datalist>` autocomplete behaviour on the Category input
- CKEditor + Flatpickr + Choices.js init on the form page
- That existing rows display correctly (some have a populated `category`, some don't)

---

## 7. Follow-ups (not done)

- Rename `Amendement` → `Amendment` (class and table) — significant refactor across `UserController`, exports, search history `type` enum, etc. Worth a dedicated ticket.
- Promote `category` to a real lookup table if it ever becomes a filter on the public side. Right now there's no business case.
- Other dead Content sidebar links (Research Articles, Samasyas, Journals) — same pattern, separate tickets.
- No tests added — same rationale as `circulars-admin-plan.md` §7.
