# Business rules — calendar-module

Evidence date: 2026-10-19. Revision: `master` `88c9d8d`.

The repository does not state business policy. `README.md` does not mention events, dates, or who may write them. Unless a rule is marked **Documented**, it is **Inferred** from the React and PHP at HEAD, or from the SQL dump. Inferred rules describe what the code does. They are not a confirmed business policy.

The connection file's password is an empty string. It is mentioned because the emptiness is the rule, not because a secret is being copied.

## Who may use the calendar

**BR-001 — Anyone who can open the page can add an event.**  
`Calendar.js` and `Modal.js` do not read a token, cookie, or role. **Inferred.**

**BR-002 — Anyone who can request the PHP scripts can list every event and insert an event.**  
`fetchEvent.php` runs `SELECT * FROM event`. `addEvent.php` inserts the JSON body. Neither script calls `session_start` or checks a header. `header.php` sets `Access-Control-Allow-Origin: *`. **Inferred.**

**BR-003 — Events are not owned.**  
The `event` table has no user column. The list query has no `WHERE`. **Inferred** from the schema and `fetchEvent.php`. **Requires Confirmation** whether a shared calendar was the intent.

**BR-004 — The user helpers do not grant access.**  
`functions.php` can insert into `user`, update `user`, and write `loguser`, and `redirectToLoginPage` points at `../index.php`. No calendar script calls them. No login page is in the tree. **Implemented** as unused code. They are not an access policy for this module.

## What counts as an event

**BR-005 — A client event needs a non-empty title.**  
`Modal.js` calls `addEvent` only when `title` is truthy. An empty string is refused. A string of spaces is accepted. **Inferred** from the `if (title)` check. There is no written rule.

**BR-006 — The server does not repeat that title rule.**  
`addEvent.php` trims `title` and inserts it, including `""`. The dump contains rows whose `eventTitle` is empty. **Implemented** in PHP. **Documented** as data in `php/fullcalender.sql`. **Requires Confirmation** whether those empty rows were mistakes.

**BR-007 — Title length on the server is the column width.**  
`eventTitle` is `varchar(255) NOT NULL`. The browser does not enforce 255. **Implemented** in the dump. What MySQL does with a longer string depends on SQL mode, which is not in the repository.

**BR-008 — Start and end are strings copied from FullCalendar.**  
The client sends `startStr` and `endStr`. The columns `dtDateStart` and `dtDateEnd` are `varchar(255)`, not `DATE` or `DATETIME`. **Implemented.** No rule rejects an end before a start.

**BR-009 — The `time` column is not the event's clock time.**  
The modal has no time input. `addEvent.php` inserts `""` for `time`. Every dumped row stores `00:00:00`. Week and day views, if they show a duration, get it from `start` / `end`, which the fetch script returns and which the insert copies from FullCalendar. **Inferred.**

**BR-010 — Description is never collected.**  
`eventDescription` is inserted as `""`. The form does not ask. The fetch script does not return the column. **Implemented.**

**BR-011 — The database assigns the only durable id.**  
`eventID` is an auto-increment primary key. The id created in `Modal.js` (`createEventId`) is passed to FullCalendar and is not included in the POST body. `fetchEvent.php` does not return `eventID`. **Implemented.** After a reload, the client cannot match a grid event to a row. **Inferred** consequence.

## How a save behaves

**BR-012 — Adding on the grid is what triggers the save.**  
The POST runs from FullCalendar's `eventAdd`, not from the form's submit directly. Submit calls `calendarApi.addEvent`, and that callback posts. **Implemented.**

**BR-013 — A failed save is not shown to the person.**  
Axios errors and the PHP body are `console.log` only. PHP does not return a success flag. `Insertdata` echoes `Error: ` plus the MySQL error text into the HTTP body when the query fails. **Implemented.**

**BR-014 — The insert is not parameterised.**  
`Insertdata` glues field names and values into one SQL string. A title containing a quote changes the statement. **Implemented.** This is a defect in the code, not a business policy. **Requires Confirmation** if anyone still exposes the script.

**BR-015 — The save response is debug text.**  
`addEvent.php` `var_dump`s the decoded JSON. `Insertdata` `print_r`s the SQL. **Implemented.**

**BR-016 — Dragging an event does not change the database.**  
`editable` is true. No handler posts the new start or end. **Implemented** UI flag. **Inferred** that a refresh shows the stored range again, if fetch ever paints events.

## How a load behaves

**BR-017 — The list is the whole table.**  
No date window is applied, even though FullCalendar passes a visible range into an event-source function. `fetchEvent` ignores its arguments. **Implemented.**

**BR-018 — Only title, start, and end are offered to the calendar.**  
`eventID`, `time`, and `eventDescription` are dropped in `fetchEvent.php`. **Implemented.**

**BR-019 — The browser function does not deliver the list the way FullCalendar 5 documents.**  
`fetchEvent` returns the Axios data and does not call a success callback. **Implemented** in `Calendar.js`. **Inferred** that the grid does not show stored rows. **Requires Confirmation** against `@fullcalendar/react` 5.3.1 at runtime.

## Dates and the dialog

**BR-020 — The dialog opens from a date click, and the stored range comes from a select.**  
`selectedDate` only controls whether `Modal` is mounted. The start and end posted later are `eventClick.startStr` and `eventClick.endStr`. **Implemented.** If those two gestures have not both happened, submit has no select info. **Inferred** failure. **Requires Confirmation** of FullCalendar's click order.

**BR-021 — The dialog stays up after Add.**  
Submit does not call `close`. The overlay click clears `selectedDate`. **Implemented.**

**BR-022 — There is no rule for all-day versus timed.**  
`allDay` is copied onto the client event. The POST body does not include `allDay`. The table has no all-day flag. **Implemented.**

## Data that shipped inside the dump

**BR-023 — The dump is a snapshot, not a seed with a stated policy.**  
`fullcalender.sql` is a phpMyAdmin dump of database name `fullcalender` (comment only; no `USE`). It inserts 13 rows. Ids 1, 3, and 5 are absent. `AUTO_INCREMENT` is 17. Several titles are blank. **Documented** by the file. Why rows were deleted or left blank is **Unknown**.

**BR-024 — The connection target and the dump name disagree.**  
`db_connection.php` uses database `login`. The dump comment says `fullcalender`. **Implemented** texts. Which one a running stack used is **Requires Confirmation**.

## Conflicts

No written business rule conflicts with the code, because no written rule exists. The conflicts that matter are inside the implementation:

| Topic | One side | Other side |
|---|---|---|
| Empty title | Modal blocks a falsy title | PHP and the dump accept `''` |
| Connection | Scripts include `dbconnect.php` | Git tracks `db_connection.php` and nothing includes it |
| Database name | `login` | Dump comment `fullcalender` |
| PHP URL | `http://localhost/php-react/*.php` | Files live in `php/` |
| Fetch contract | Function returns JSON | FullCalendar 5 asks for a success callback |
| Editable grid | Drag is enabled | Nothing saves the drag |

These are recorded in the PRD as behaviour, not as approved policy.
