403Webshell
Server IP : 10.200.247.200  /  Your IP : 216.73.217.19
Web Server : Apache
System : Linux synergy-usa-sites 6.8.0-138-generic #138-Ubuntu SMP PREEMPT_DYNAMIC Fri Jul 31 22:41:49 UTC 2026 x86_64
User : jeremy ( 1001)
PHP Version : 8.4.25
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : OFF
Directory :  /var/www/development/callings.lvsaints.com/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/development/callings.lvsaints.com/PLAN.md
# Village Calling Board — Implementation Plan (Hybrid)

Goal: a **functional, data-driven** calling board (working buttons, org switching,
LCR import, per-ward data) **plus** safe design levers so the boss keeps the
styling control he wants — without letting hand-edited HTML break the app.

Stack: **PHP 8.4 + MariaDB** (server-rendered) with a light JavaScript layer
(vanilla / Alpine + SortableJS). No build step.

---

## Guiding principle: separate the three layers

| Layer | Who owns it | How it's changed |
|---|---|---|
| **Data** (members, callings, planning) | LCR + bishopric | LCR import + working buttons |
| **Structure** (templates, logic) | Developer | Code |
| **Theme** (CSS, colors, spacing, ordering) | Boss | "Design" tab (editable CSS per ward) |

The old "upload a full HTML file" feature is **kept but rebranded as
"Design Proposals"** — a sandbox for mocking up new directions, which then get
folded into the real app. It is no longer the live site.

---

## Data model (new tables, added alongside wards / users / page_versions)

- **organizations** — left-menu items (Bishopric, EQ, RS, YM, YW, Primary, …),
  with `sort_order`. Global reference list.
- **calling_areas** — the *cards* on the board (e.g. "Primary Presidency",
  "Valiant 9", "Nursery", "Activity Days – Jr Boys"). Fields: `ward_id`,
  `organization_id`, `section` (group header), `name`, `sort_order`.
- **members** — the ward roster from LCR. `ward_id`, `name`, `category`
  (member / student / mission), `notes`.
- **current_callings** — LCR truth. `ward_id`, `calling_area_id`,
  `calling_name` (role), `member_id` (NULL = vacant), `sustained_date`,
  `imported_at`.
- **planning_items** — board truth (per README). `ward_id`, `member_id`,
  `organization_id`, `calling_area_id`, `calling_name`,
  `status` (idea | proposed | extend | hold), `notes`,
  `created_by_user_id`, `created_at`, `updated_at`, `archived_at`.
- **import_history** — `ward_id`, `type` (callings | members), `raw_text`,
  `summary`, `created_by`, `created_at`.
- **ward_settings** — `ward_id` (PK), `theme_css` (LONGTEXT), `layout_json`.

`page_versions` stays as the Design-Proposals store.

Derived, not stored: **Needs Calling** = members with no current calling
MINUS anyone already in Ideas / Proposed / Extend (per README).

---

## Backend architecture

- `index.php` → **app shell**: reads `?org=primary&ward=…`, renders the board
  and planning sidebar from the DB, and injects the ward's `theme_css`.
- `render/board.php`, `render/sidebar.php` → shared templates (the README's
  "reuse the same components for every org").
- `api/` → small JSON endpoints, each enforcing login + ward scope + CSRF:
  - `api/planning.php` — add / remove / move / update planning items.
  - `api/import.php` — parse pasted LCR reports.
  - `api/theme.php` — save theme CSS / layout.
- `assets/app.js` → button clicks & drag/drop call the API and update the DOM.

Roles already built: **super_admin** (all wards), **admin** (own ward),
**viewer** (read-only). All API calls reuse `can_edit_ward()` / `can_view_ward()`.

---

## Phases (each is independently demo-able)

### Phase 1 — Data model + read-only board + org switching
- Create the new tables; seed `organizations`.
- One-time seed: convert the current static Primary content into rows so the
  board looks identical to today, but rendered from the DB.
- Refactor `index.php` into the app shell; left menu switches org via `?org=`.
- **Outcome:** clicking "Young Men" loads Young Men; nothing is hard-coded.

### Phase 2 — Working planning buttons (CRUD)
- `+ Idea`, remove idea, Hold, Extend, "Archive / Clear Current Ideas".
- Planning sidebar generated live (Needs Calling / Ideas-Proposed / Extend /
  Hold / Mission / YSA).
- **Outcome:** the buttons do real things and persist.

### Phase 3 — LCR import
- Parse "Callings by Organization" and "Members Without Callings".
- Update `current_callings` + `members`; **preserve** planning items.
- Auto-resolve: someone in Proposed/Extend who shows up sustained in LCR is
  removed from planning and shown as current.
- Record each import in `import_history`.
- **Outcome:** the paste-from-LCR workflow works end to end.

### Phase 4 — Drag & drop
- SortableJS: drag a member from "Needs Calling" onto a card → creates a
  planning item; removing it returns them to Needs Calling (if still uncalled).
- **Outcome:** the interaction described in the README.

### Phase 5 — Design tab + Proposals rebrand
- Admin "Design" tab: edit `theme_css` per ward with a live preview iframe;
  saved CSS is injected into the board so the boss can restyle/reorder safely.
- Rebrand the current upload feature as "Design Proposals" (mockups, not live).
- **Outcome:** boss gets more design control than today, logic stays intact.

### Phase 6 — Polish (later)
- Member detail panel + notes, sustained dates on hover, per-ward URL slugs
  (`/Village`, `/Southgate`), scenarios.

---

## Security notes
- Every API endpoint: `require_login()` + ward-scope check + CSRF + prepared
  statements.
- `theme_css` is injected into a `<style>` tag. Only admins can set it (same
  trust level as today's HTML upload), but strip `</style>`/`<` breakouts on
  save so a stray tag can't inject script. Consider a CSP header too.
- Member data stays behind login (already enforced).

---

## Open design questions to settle during Phase 1
1. **Card mapping:** LCR gives a flat calling list; the board groups them into
   cards ("Valiant 9", "Nursery"). Do we define `calling_areas` by hand per
   ward and map LCR calling names into them, or auto-derive from LCR text?
   (Recommend: hand-defined areas + a mapping table, refined over time.)
2. **JS approach:** vanilla + fetch vs. Alpine.js. (Recommend Alpine for the
   drag/reactive bits; still no build step.)

Youez - 2016 - github.com/yon3zu
LinuXploit