Markdown Converter
Agent skill for markdown-converter
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Sign in to like and favorite skills
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Start the application:
docker-compose up -d
Stop the application:
docker-compose down
Rebuild after code changes:
docker-compose up -d --build
Access points:
Default test credentials (PIN: 123456):
/ ├── config.php # Central config & utilities - loaded by ALL files ├── src/ # Business logic (NOT web-accessible) │ ├── auth.php # Session-based authentication │ └── timekeeping.php # Core check-in/out logic ├── public/ # Web root (Apache DocumentRoot) │ ├── index.php # Parent interface │ ├── admin.php # Staff dashboard (largest file: 1505 lines) │ └── reports.php # Reporting interface └── data/ # JSON data storage (persisted via Docker volume) ├── families.json # Family & children (includes base64 photos) ├── users.json # Staff credentials ├── settings.json # Configurable business rules └── timekeeping/ # Monthly partitioned attendance records └── YYYY-MM.json # One file per month
Key Architectural Decision: The
src/ directory contains business logic that should NEVER be directly web-accessible. Apache's DocumentRoot points to public/ only.
Check-In/Out Process:
auth.php validates against hashed PIN in families.jsonindex.php displays children from families.jsontimekeeping.php::checkInChild() creates record in current month's JSONFile-Based Transactions:
loadJsonFile())saveJsonFile())All business rules are stored in
data/settings.json and loaded at runtime in config.php:
// These constants are loaded from settings.json: MAX_HOURS_PER_DAY // Default: 8.5 hours OVERAGE_RATE_PER_MINUTE // Default: $1.00 DAYCARE_CLOSING_TIME // Default: 16:30:00 (4:30 PM) LATE_PICKUP_RATE_PER_MINUTE // Default: $1.00
Staff can edit these via Admin → Settings tab. Changes take effect immediately on page refresh.
The application calculates TWO separate fee types:
Overtime Fee: Applied when total duration exceeds
MAX_HOURS_PER_DAY
Late Pickup Fee: Applied when checkout time is after
DAYCARE_CLOSING_TIME
Both can apply to the same checkout. Records store both separately:
{ "overage_minutes": 30, "overage_charge": 30.00, "late_pickup_minutes": 30, "late_pickup_charge": 30.00 }
data/settings.json with default valueconfig.php defaults array (lines 35-42)admin.php Settings tab (lines 1244-1348)admin.php (lines 405-434)All fee calculations happen in
src/timekeeping.php:
checkOutChild() function (lines 142-214) - calculates fees at checkoutedit_times handler in admin.php (lines 357-418) - recalculates when editingadd_record handler in admin.php (lines 489-531) - calculates for manually added recordsImportant: When changing fee logic, update ALL three locations to maintain consistency.
Helper Functions (all in
):config.php
loadJsonFile($filepath) // Read JSON → PHP array saveJsonFile($filepath, $data) // Write PHP array → JSON loadSettings() // Get settings array saveSettings($settings) // Update settings.json
Data Schema Reference:
Family structure in
families.json:
[ 'id' => 'fam_timestamp_random', 'name' => 'Family Name', 'pin_hash' => '$2y$10$...', // bcrypt hash 'contact_phone' => '555-0123', 'contact_email' => '[email protected]', 'children' => [ [ 'id' => 'child_timestamp_random', 'first_name' => 'John', 'last_name' => 'Doe', 'birth_date' => 'YYYY-MM-DD', 'photo' => 'data:image/png;base64,...', // Base64 encoded 'notes' => 'Allergies, etc.' ] ] ]
Timekeeping record structure:
[ 'id' => 'rec_timestamp_random', 'child_id' => 'child_...', 'date' => 'YYYY-MM-DD', 'check_in_time' => 'HH:MM:SS', 'check_out_time' => 'HH:MM:SS', 'duration_hours' => 8.5, 'overage_minutes' => 30, 'overage_charge' => 30.00, 'late_pickup_minutes' => 0, 'late_pickup_charge' => 0.00, 'checked_in_by' => 'parent', 'checked_out_by' => 'parent', 'notes' => '' ]
Two separate authentication systems:
Family Authentication:
password_hash())$_SESSION['family_id']loginParent(), isParentLoggedIn(), getCurrentFamily()Staff Authentication:
$_SESSION['staff_id']loginStaff(), isStaffLoggedIn(), requireStaffLogin(), getCurrentStaff()All auth functions in
src/auth.php.
admin.php uses JavaScript tabs without page reloads:
<div> containers<a href="/reports.php"> linkshowTab(tabName) JavaScript function?date=2025-11-13 shows Edit Times tab)When adding features to admin dashboard:
id="tab-yourname" and class="tab-content hidden"Child photos are stored as base64-encoded data URIs directly in
families.json. This explains why the file can be 2MB+. Photos are displayed inline:
<img src="data:image/png;base64,iVBORw0KGgoAAAANS..." />
Timekeeping records are split into monthly files to prevent a single massive JSON file:
data/timekeeping/YYYY-MM.json.htaccess in public/ sets security headers:
X-Frame-Options: SAMEORIGIN X-Content-Type-Options: nosniff X-XSS-Protection: 1; mode=block
All IDs use
timestamp_randomnumber format:
generateId('child_') → 'child_1699999999_4567'
This ensures uniqueness without auto-increment counters.
The application intentionally has:
This makes deployment extremely simple but means you implement everything from scratch.
This codebase is intentionally educational with:
When making changes, maintain this beginner-friendly style: