Commit fd6f8339 authored by AGLANPC\aglan's avatar AGLANPC\aglan

smsing

parent d11165e9
.git
.gitignore
*.md
code.sql
prompts/
storage/logs/*
storage/uploads/*
core/App_112.php
core/Database_110.php
core/View_111.php
core/helpers_109.php
# ── App ──────────────────────────────────────────────────────────────────────
APP_DEBUG=false
APP_URL=https://club.al-arcade.com
# ── Database ─────────────────────────────────────────────────────────────────
DB_HOST=srv-captain--club-db # CapRover internal service name
DB_PORT=3306
DB_NAME=club_management
DB_USER=club_user
DB_PASS=CHANGE_ME_STRONG_PASSWORD
FROM php:8.2-apache
# ── System deps ──────────────────────────────────────────────────────────────
RUN apt-get update && apt-get install -y \
libpng-dev libjpeg-dev libfreetype6-dev \
libzip-dev libonig-dev libxml2-dev \
zip unzip curl git \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install pdo pdo_mysql mbstring zip gd xml opcache \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# ── Apache: point DocumentRoot at public/ and enable mod_rewrite ─────────────
ENV APACHE_DOCUMENT_ROOT=/var/www/html/public
RUN sed -i 's|DocumentRoot /var/www/html|DocumentRoot /var/www/html/public|g' \
/etc/apache2/sites-available/000-default.conf \
&& sed -i 's|<Directory /var/www/html>|<Directory /var/www/html/public>|g' \
/etc/apache2/apache2.conf \
&& echo '<Directory /var/www/html/public>\n AllowOverride All\n Require all granted\n</Directory>' \
>> /etc/apache2/apache2.conf \
&& a2enmod rewrite
# ── PHP production settings ───────────────────────────────────────────────────
RUN echo "display_errors = Off\n\
error_reporting = E_ALL\n\
log_errors = On\n\
error_log = /var/log/php_errors.log\n\
upload_max_filesize = 20M\n\
post_max_size = 22M\n\
memory_limit = 256M\n\
max_execution_time = 60\n\
opcache.enable = 1\n\
opcache.memory_consumption = 128\n\
opcache.validate_timestamps = 0" > /usr/local/etc/php/conf.d/production.ini
# ── Copy application files ────────────────────────────────────────────────────
WORKDIR /var/www/html
COPY . .
# ── Create required directories & set permissions ────────────────────────────
RUN mkdir -p storage/logs storage/uploads \
&& chown -R www-data:www-data /var/www/html \
&& chmod -R 775 storage
EXPOSE 80
{
"schemaVersion": 2,
"dockerfilePath": "./Dockerfile"
}
Now the dashboard widgets:
\ No newline at end of file
### Pagination (core/Pagination.php)
\ No newline at end of file
### File Upload (core/FileUpload.php)
\ No newline at end of file
### Sequence Generator (core/SequenceGenerator.php)
\ No newline at end of file
### Audit Log (core/AuditLog.php)
\ No newline at end of file
### Lang (core/Lang.php) — Bilingual Support
\ No newline at end of file
### Config (core/Config.php)
\ No newline at end of file
### Export (core/Export.php)
\ No newline at end of file
## GLOBAL HELPER FUNCTIONS (available everywhere)
\ No newline at end of file
## FRONTEND CONVENTIONS
### CSS Framework: Bootstrap 5.3 (loaded via CDN in layout)
### Icons: Font Awesome 6 (loaded via CDN in layout)
### DataTables: DataTables 1.13+ with Bootstrap 5 styling (loaded via CDN)
### Date Picker: Flatpickr (loaded via CDN)
### Select Dropdowns: Select2 with Bootstrap 5 theme (loaded via CDN)
### Alerts: SweetAlert2 (loaded via CDN)
### Charts: Chart.js 4 (loaded via CDN)
All CDN libraries are included in Phase 0's layout. You do NOT need to include them.
### DataTable Pattern (Server-Side)
In your view:
\ No newline at end of file
In your module JS:
\ No newline at end of file
Now the CSS:
\ No newline at end of file
In your API controller, return:
\ No newline at end of file
### Form Submit Pattern (AJAX)
\ No newline at end of file
Phase 0's `form-helper.js` auto-intercepts forms with `data-ajax="true"`:
- Shows loading spinner on submit
- Sends via AJAX
- On success (`{"success": true, "message": "...", "redirect": "/members/123"}`): shows toast, redirects
- On validation error (`{"success": false, "errors": {...}}`): shows field-level errors
- On server error: shows SweetAlert
### Modal Pattern
\ No newline at end of file
Phase 0's `modal-helper.js` auto-loads content from `data-ajax-url` when modal opens.
### Status Badge Convention
\ No newline at end of file
### Card Layout Pattern for Detail Pages
\ No newline at end of file
## SECURITY REQUIREMENTS
1. ALL DB queries use prepared statements (parameterized). NO string concatenation for SQL.
2. ALL output is escaped with `e()` unless explicitly rendering trusted HTML.
3. CSRF token on every POST form.
4. Permission checked on every route (via routes.php permission column).
5. Audit log for every create/update/delete/status-change.
6. File uploads validated for type and size.
7. Session-based auth with timeout.
8. Rate limiting on login attempts (handled by Phase 0).
## CODING STANDARDS
1. PHP files: `<?php` opening tag, NO closing `?>` tag on pure PHP files.
2. Indentation: 4 spaces.
3. Controller methods: camelCase (`index`, `create`, `store`, `show`, `edit`, `update`, `destroy`).
4. View files: snake_case (`member_form.php`, `index.php`).
5. Database columns: snake_case (already defined in schema).
6. CSS classes: kebab-case following Bootstrap conventions.
7. JS: camelCase functions, `const`/`let` only, no `var`.
8. Comments: PHPDoc on classes and public methods.
9. Max line length: 120 chars (soft limit).
10. Every controller action that modifies data MUST call `$this->audit()`.
## HOW MODULES REFERENCE DATA FROM OTHER MODULES
Modules DO NOT import each other's classes. The database IS the integration contract.
To show a member name in the Finance module:
\ No newline at end of file
To call a stored procedure:
\ No newline at end of file
## DASHBOARD WIDGETS
Each module can register dashboard widgets in `module.json`. The widgets partial file receives no parameters — it must fetch its own data. The partial is a PHP file that outputs HTML.
\ No newline at end of file
## WHAT YOU MUST DELIVER FOR YOUR PHASE
1. **`module.json`** — Module manifest (exactly per schema above)
2. **`routes.php`** — All routes (exactly per format above)
3. **All Controller files** — In `Controllers/` directory
4. **All View files** — In `views/` directory, valid PHP/HTML
5. **Module CSS file** — If needed, in `css/`
6. **Module JS file** — If needed, in `js/`
7. **Language files** — `lang/en.php` and `lang/ar.php` with all strings
8. **Dashboard widget partials** — If any, in `views/widgets/`
9. Complete, working, production-ready code. NO placeholders, NO TODOs, NO "implement this".
## THE DATABASE SCHEMA
The complete database schema is attached. Study it carefully — understand every table, column, relationship, trigger, stored procedure, and view that is relevant to your phase. The DB is already created and seeded with lookup data.
\ No newline at end of file
### 3. `/.htaccess` — Root Rewrite
\ No newline at end of file
And finally, the JavaScript:
\ No newline at end of file
### 4. `/config/database.php`
\ No newline at end of file
### 5. `/config/app.php`
\ No newline at end of file
### 6. ALL `/core/` files as specified in the Master Prompt
Build EVERY class with EVERY method documented in the Master Prompt's "Core Class Public APIs" section.
The following is additional specification:
#### `core/Bootstrap.php`
- Set timezone from config
- Register autoloader: Classes in `core/` resolve by name. Classes in `modules/<mod>/Controllers/` resolve by module.
- Start session via `Session::start()`
- Load `helpers.php`
#### `core/App.php`
- Constructor: load config, init Database, init Router, init ModuleRegistry
- `run()`: Process request through middleware pipeline → Router → Controller dispatch
- Register core routes (login, logout, dashboard, change-password, profile)
- Auto-register all module routes via ModuleRegistry
#### `core/Router.php`
- Support GET, POST, PUT, DELETE methods
- URI pattern matching with `{param}` extraction
- Each route has: method, pattern, handler, required permission, middleware array
- `dispatch()`: Find matching route, check auth middleware, check permission, call controller method
- 404 for no match, 403 for permission denied
#### `core/Database.php`
- PDO singleton with persistent connection
- Prepared statements everywhere
- `fetchAll()`, `fetchOne()`, `fetchColumn()`, `insert()`, `update()`, `delete()`, `execute()`
- Query builder: `table()` returns a QueryBuilder instance with `where()`, `orderBy()`, `limit()`, `offset()`, `get()`, `first()`, `count()`
- Transaction support: `beginTransaction()`, `commit()`, `rollBack()`
- Query logging in debug mode
- Automatic reconnection on lost connection
#### `core/Model.php`
\ No newline at end of file
#### `core/Auth.php`
- `login($username, $password)`: Verify against `auth_users` table using `password_verify()`. Check `is_active`, `is_deleted`, `is_locked`. Update `last_login_at`, `last_login_ip`. Record in `auth_login_attempts` and `login_log`. Create `auth_sessions` record. Return bool.
- `logout()`: Destroy session, mark `auth_sessions` as inactive.
- `user()`: Return current authenticated user array from session cache (load from DB on first call per request, then cache in static property).
- `check()`: Return bool — is user logged in?
- `id()`: Return user ID.
- Lock account after N failed attempts (from `sys_config.max_login_attempts`). Unlock after `lockout_duration_minutes`.
- Load user permissions (via `auth_user_roles` → `auth_role_permissions` → `auth_permissions`) and cache in session.
#### `core/Gate.php`
- `can($permissionCode)`: Check if current user has permission. Super admin (role_code='super_admin') can do everything.
- `canAny(array $permissions)`: Any of the permissions.
- `canAll(array $permissions)`: All of the permissions.
- `authorize($permission)`: Can or abort 403.
#### `core/Session.php`
- Secure session config: httponly, secure (if HTTPS), samesite=Lax
- `get()`, `set()`, `has()`, `remove()`, `flash()`, `getFlash()`, `regenerate()`, `destroy()`
- Session timeout check against config
#### `core/CSRF.php`
- Generate per-session token
- `token()`: Get current token
- `field()`: Return hidden input HTML
- `validate($token)`: Check submitted token matches session token
#### `core/View.php`
- Render views with layout wrapping
- Module views resolve from `modules/<module>/views/<view>.php`
- Core views resolve from `views/<view>.php`
- Layout files in `views/layouts/<layout>.php`
- Variable extraction into view scope
- Push/endPush stack system for styles/scripts sections
- `partial($path, $data)` for including sub-views
- Section/yield not needed — just use the push/endPush for CSS/JS and the main content is yielded automatically
#### `core/Validator.php`
- Implement ALL validation rules listed in Master Prompt
- Returns object with `fails()`, `errors()` (associative array field => [messages]), `validated()`
- `exists:table,column` rule checks DB
- `unique:table,column,except_id` rule checks DB
#### `core/FileUpload.php`
As documented. Save with unique filename (UUID or hash-based). Preserve extension.
#### `core/Pagination.php`
- Generates Bootstrap 5 pagination HTML
- Supports query string preservation
- Shows: First, Prev, page numbers (with ellipsis), Next, Last
#### `core/Export.php`
- CSV: Direct download with proper headers
- Excel: HTML table with `Content-Type: application/vnd.ms-excel`
- PDF: HTML rendered with print-friendly CSS, use `window.print()` approach. Include a simple print layout.
#### `core/Lang.php`
- Load core translations from `views/lang/en.php` and `views/lang/ar.php`
- Load module translations from `modules/<mod>/lang/<code>.php`
- `get($key, $replacements = [])`: Dot notation lookup. Key format: `module.key` or `common.key`
- Language stored in session, switchable via URL param `?lang=en` or `?lang=ar`
#### `core/Config.php`
- Load all from `sys_config` table on first access, cache in static property
- `get($key, $default)`: Lookup by `config_key`
#### `core/Logger.php`
- Write to `storage/logs/app-YYYY-MM-DD.log`
- Levels: debug, info, warning, error, critical
- Format: `[YYYY-MM-DD HH:MM:SS] [LEVEL] [IP] [USER:id] Message`
#### `core/AuditLog.php`
- Insert into `audit_log` table
- Capture: user_id, action_type, module, entity_type, entity_id, description, ip, old_values, new_values
#### `core/SequenceGenerator.php`
- Call `sp_generate_next_sequence` stored procedure
- Return the generated value
- Thread-safe (the SP uses `FOR UPDATE`)
#### `core/ModuleRegistry.php`
- Scan `/modules/*/module.json` on init
- Parse and cache module manifests
- Provide: `getModules()`, `getModule($name)`, `getMenuItems()`, `getRoutes()`, `getWidgets()`, `getCssFiles()`, `getJsFiles()`
- Filter menu items by user permissions at render time (in MenuBuilder)
#### `core/MenuBuilder.php`
- Build sidebar menu HTML from all module manifests
- Filter items by current user's permissions
- Highlight active menu item based on current URI
- Support nested children (one level deep)
- Support collapsible groups
#### `core/helpers.php`
Implement ALL helper functions listed in the Master Prompt. Plus:
- `status_badge($status)`: Returns Bootstrap badge HTML
- `active_class($path)`: Returns 'active' if current URI starts with $path
- `money($amount)`: Alias for format_money
- `selected($value, $current)`: Returns 'selected' attribute for <option>
- `checked($value, $current)`: Returns 'checked' attribute for checkbox/radio
#### `core/Middleware/AuthMiddleware.php`
- Check `Auth::check()`. If not authenticated, redirect to `/login`.
#### `core/Middleware/GuestMiddleware.php`
- If authenticated, redirect to `/dashboard`.
#### `core/Middleware/PermissionMiddleware.php`
- Check route's required permission against `Gate::can()`. Abort 403 if denied.
### 7. ALL `/views/` files
#### `views/layouts/app.php` — Main Layout
Full HTML5 document with:
- DOCTYPE, html tag with `dir` and `lang` attributes (RTL support)
- Meta viewport, charset
- Title tag: `<?= $title ?? 'Club Management' ?> | <?= Config::get('club_name_en') ?>`
- CDN includes: Bootstrap 5.3 CSS, Font Awesome 6, DataTables CSS, Select2 CSS, Flatpickr CSS, SweetAlert2 CSS
- RTL: Include Bootstrap RTL variant if `is_rtl()`
- Custom `app.css` include
- Yielded `styles` stack
- Body with sidebar layout:
- Sidebar (include `partials/sidebar.php`)
- Main content area:
- Header (include `partials/header.php`)
- Breadcrumb (include `partials/breadcrumb.php`)
- Flash messages (include `partials/alerts.php`)
- `<?= $content ?>` — The view content
- Footer (include `partials/footer.php`)
- CDN includes: jQuery 3.7, Bootstrap 5.3 JS, DataTables JS, Select2 JS, Flatpickr JS, SweetAlert2 JS, Chart.js
- Custom JS: `app.js`, `datatable-helper.js`, `form-helper.js`, `modal-helper.js`
- Module CSS/JS files from ModuleRegistry
- Yielded `scripts` stack
#### `views/layouts/auth.php` — Login Layout
Simple centered card layout, no sidebar. Bootstrap included.
#### `views/layouts/print.php` — Print Layout
Minimal: just content with print-friendly CSS, no sidebar/header.
#### `views/partials/header.php`
Top navbar with:
- Sidebar toggle button (hamburger)
- Language switcher (EN/AR)
- User dropdown: Profile, Change Password, Logout
- Notifications bell (placeholder — module fills later)
#### `views/partials/sidebar.php`
- Club logo and name at top
- Menu items from `MenuBuilder::render()`
- Collapsible sidebar on mobile
- Active state highlighting
- Permission-filtered items
#### `views/partials/footer.php`
- Copyright: `© {year} {club_name}. All rights reserved.`
- System version
#### `views/partials/breadcrumb.php`
- Renders `$breadcrumbs` array: `[['label' => 'Home', 'url' => '/'], ['label' => 'Members']]`
- Bootstrap breadcrumb component
#### `views/partials/alerts.php`
- Render flash messages from session (success, error, warning, info)
- Auto-dismissible Bootstrap alerts
#### `views/auth/login.php`
- Login form: username, password, remember me checkbox
- CSRF token
- Show validation errors
- Club logo and name
- Responsive centered card
#### `views/auth/change_password.php`
- Current password, new password, confirm new password
- Password strength requirements shown
- CSRF token
#### `views/dashboard/index.php`
- Welcome message with user name and current date/time
- Widget grid from all modules' dashboard_widgets
- Each widget rendered via its partial file
- Responsive grid layout (Bootstrap cols)
- Phase 0 includes 1 default widget: "Quick Stats" that shows basic system info
#### `views/errors/403.php`, `views/errors/404.php`, `views/errors/500.php`
- Clean error pages with appropriate messages
- Link back to dashboard
- Use the auth layout (no sidebar for logged-out users, app layout for logged-in)
#### `views/lang/en.php` and `views/lang/ar.php`
Core translations including:
- common.save, common.cancel, common.delete, common.edit, common.view, common.create, common.search
- common.actions, common.status, common.yes, common.no, common.confirm, common.back
- common.export_csv, common.export_excel, common.export_pdf, common.print
- common.loading, common.no_data, common.showing, common.of, common.entries
- common.are_you_sure, common.cannot_undo, common.success, common.error
- auth.login, auth.logout, auth.username, auth.password, auth.remember_me
- auth.change_password, auth.current_password, auth.new_password, auth.confirm_password
- dashboard.title, dashboard.welcome
- (Arabic translations for ALL of the above)
### 8. `/public/assets/css/app.css`
Custom styles:
- Sidebar: 260px width, dark theme (dark blue/navy), collapsible
- Content area: margin-left matching sidebar
- Card styling enhancements
- DataTable custom styling
- Form label styling
- Status badge colors
- RTL adjustments reference
- Print styles
- Responsive breakpoints
- Loading overlay styles
- Toast positioning
- Scrollbar styling for sidebar
- Active menu item highlighting
- Breadcrumb spacing
- Mobile sidebar overlay
- Minimum 500 lines of thoughtful, production-quality CSS
### 9. `/public/assets/css/rtl.css`
RTL overrides:
- Sidebar on right
- Content margin-right instead of margin-left
- Text alignment adjustments
- Float reversals
- Padding/margin direction swaps
### 10. `/public/assets/js/app.js`
Core JavaScript:
- Sidebar toggle functionality
- CSRF token auto-injection into AJAX headers:
\ No newline at end of file
- Global AJAX error handler (401 → redirect login, 403 → alert, 500 → alert)
- Tooltip initialization
- Popover initialization
- Auto-format money inputs
- Confirm delete handler: elements with `data-confirm="true"` show SweetAlert before form submit
- Language-aware number formatting
- Sidebar active state management
- Mobile menu handling
- Auto-dismiss alerts after 5 seconds
### 11. `/public/assets/js/datatable-helper.js`
\ No newline at end of file
### 12. `/public/assets/js/form-helper.js`
\ No newline at end of file
### 13. `/public/assets/js/modal-helper.js`
\ No newline at end of file
### 14. Core Routes (registered in App.php)
\ No newline at end of file
### 15. Core Controllers (in `/core/` since they're part of Phase 0)
- `AuthController`: Login/Logout with brute-force protection
- `DashboardController`: Render dashboard with widgets from all modules
- `ProfileController`: View/edit profile, change password
- `LangController`: Switch language and redirect back
## DELIVERABLES CHECKLIST
You MUST deliver EVERY file listed above, complete and production-ready.
Total estimated files: ~40-45 files.
Total estimated lines: 5000-8000 lines.
DO NOT skip any file. DO NOT leave placeholder implementations.
DO NOT use any external PHP library — pure PHP only.
The system must be installable by simply:
1. Importing the SQL dump
2. Copying the files
3. Configuring `config/database.php`
4. Accessing the URL
Default login: admin / admin (the password hash in the DB is for 'admin' — or generate a new one with `password_hash('admin', PASSWORD_BCRYPT)` and document it).
## ARCHITECTURE NOTES
- No Composer. No vendor autoload. Your custom autoloader in Bootstrap.php handles everything.
- Session-based auth only. No JWT. No OAuth. Simple and bulletproof.
- The sidebar must be perfect. It's the navigation for the ENTIRE system. Every module depends on it.
- The DataTable helper must be FLAWLESS. Every module uses it for listings.
- The form helper must handle ALL edge cases: file uploads, nested fields, arrays.
- The Module Registry must correctly scan, parse, and cache all module manifests.
- Test with: 0 modules installed (empty sidebar except dashboard), then with module.json files present.
\ No newline at end of file
### Controller (core/Controller.php) — Your Controllers Extend This
\ No newline at end of file
### Request (core/Request.php)
\ No newline at end of file
### Validator (core/Validator.php)
\ No newline at end of file
Available validation rules:
`required`, `nullable`, `string`, `integer`, `decimal`, `email`, `date`, `datetime`, `boolean`,
`min:N`, `max:N`, `between:min,max`, `in:val1,val2,...`, `not_in:val1,val2,...`,
`exists:table,column`, `unique:table,column,except_id`, `file`, `mimes:ext1,ext2`, `max_file:KB`,
`regex:pattern`, `confirmed` (requires field_name_confirmation), `same:other_field`,
`after:date`, `before:date`, `array`, `json`
### View System (core/View.php)
In your controller:
\ No newline at end of file
In your view file (`modules/<module>/views/index.php`):
\ No newline at end of file
### Auth & Permissions (core/Auth.php, core/Gate.php)
\ No newline at end of file
<?php <?php
return [ return [
'host' => getenv('DB_HOST') ?: 'localhost', 'host' => getenv('DB_HOST') ?: 'srv-captain--mysql-db',
'port' => getenv('DB_PORT') ?: 3306, 'port' => getenv('DB_PORT') ?: 3306,
'database' => getenv('DB_NAME') ?: 'club_management', 'database' => getenv('DB_NAME') ?: 'club_management',
'username' => getenv('DB_USER') ?: 'root', 'username' => getenv('DB_USER') ?: 'root',
'password' => getenv('DB_PASS') ?: '', 'password' => getenv('DB_PASS') ?: 'Alarcade123#',
'charset' => 'utf8mb4', 'charset' => 'utf8mb4',
'collation'=> 'utf8mb4_unicode_ci', 'collation' => 'utf8mb4_unicode_ci',
]; ];
\ No newline at end of file
<div class="row justify-content-center"><div class="col-lg-8"><div class="card"> <div class="row g-4">
<div class="card-header d-flex justify-content-between"><h6 class="mb-0"><?= e($dep['deposit_number']) ?></h6><div><?= status_badge($dep['status']) ?></div></div> <div class="col-lg-5">
<div class="card-body"> <div class="card">
<table class="table table-sm mb-4"> <div class="card-header d-flex justify-content-between align-items-center">
<tr><th>Bank</th><td><?= e($dep['bank_name_en']) ?></td></tr> <h6 class="mb-0"><i class="fas fa-university me-2"></i>
<tr><th>Date</th><td><?= format_date($dep['deposit_date']) ?></td></tr> <?= e($dep['deposit_number']) ?>
<tr><th>Total</th><td class="fw-bold"><?= format_money($dep['total_amount']) ?></td></tr> </h6>
<tr><th>Cash</th><td><?= format_money($dep['cash_amount']) ?></td></tr> <?= status_badge($dep['status']) ?>
<tr><th>Checks</th><td><?= format_money($dep['check_amount']) ?> (<?= $dep['check_count'] ?> checks)</td></tr> </div>
<?php if($dep['bank_reference']): ?><tr><th>Bank Ref</th><td><?= e($dep['bank_reference']) ?></td></tr><?php endif; ?> <div class="card-body">
</table> <table class="table table-sm mb-0">
<h6>Items</h6> <tr>
<table class="table table-sm table-bordered"><thead><tr><th>Type</th><th>Amount</th><th>Details</th></tr></thead><tbody> <th width="40%">
<?php foreach($items as $it): ?> <?= lang('adv_finance.bank_name') ?>
<tr><td><?= ucfirst($it['item_type']) ?></td><td><?= format_money($it['amount']) ?></td><td><?= $it['check_number'] ? 'Check #' . e($it['check_number']) . ' — ' . e($it['drawer_name']) : '—' ?></td></tr> </th>
<?php endforeach; ?> <td>
</tbody></table> <?= e($dep['bank_name_en']) ?>
<?php if(in_array($dep['status'], ['prepared','deposited']) && auth_can('gl.manage')): ?> </td>
<form method="POST" action="/advanced-finance/deposits/<?= $dep['id'] ?>/confirm" data-ajax="true" class="mt-3"> </tr>
<?= csrf_field() ?> <tr>
<div class="row g-2 align-items-end"> <th>
<div class="col-md-6"><label class="form-label">Bank Reference</label><input type="text" name="bank_reference" class="form-control"></div> <?= lang('adv_finance.deposit_date') ?>
<div class="col-md-3"><button type="submit </th>
<td>
<?= format_date($dep['deposit_date']) ?>
</td>
</tr>
<tr>
<th>
<?= lang('adv_finance.total_amount') ?>
</th>
<td class="fw-bold fs-5">
<?= format_money($dep['total_amount']) ?>
</td>
</tr>
<tr>
<th>
<?= lang('adv_finance.cash_amount') ?>
</th>
<td>
<?= format_money($dep['cash_amount']) ?>
</td>
</tr>
<tr>
<th>
<?= lang('adv_finance.check_amount') ?>
</th>
<td>
<?= format_money($dep['check_amount']) ?> <span class="text-muted">(
<?= (int) $dep['check_count'] ?> checks)
</span>
</td>
</tr>
<tr>
<th>
<?= lang('common.status') ?>
</th>
<td>
<?= status_badge($dep['status']) ?>
</td>
</tr>
<?php if ($dep['bank_reference']): ?>
<tr>
<th>
<?= lang('adv_finance.bank_reference') ?>
</th>
<td><code><?= e($dep['bank_reference']) ?></code></td>
</tr>
<?php endif; ?>
<?php if ($dep['notes']): ?>
<tr>
<th>Notes</th>
<td>
<?= e($dep['notes']) ?>
</td>
</tr>
<?php endif; ?>
<?php if ($dep['confirmed_at']): ?>
<tr>
<th>Confirmed</th>
<td>
<?= format_datetime($dep['confirmed_at']) ?>
</td>
</tr>
<?php endif; ?>
</table>
</div>
</div>
--- <?php if (in_array($dep['status'], ['prepared', 'deposited']) && auth_can('gl.manage')): ?>
<div class="card mt-3">
<div class="card-header">
<h6 class="mb-0"><i class="fas fa-check-circle me-2"></i>
<?= lang('adv_finance.confirm_deposit') ?>
</h6>
</div>
<div class="card-body">
<form method="POST" action="/advanced-finance/deposits/<?= $dep['id'] ?>/confirm" data-ajax="true">
<?= csrf_field() ?>
<div class="mb-3">
<label class="form-label">
<?= lang('adv_finance.bank_reference') ?>
</label>
<input type="text" name="bank_reference" class="form-control"
placeholder="Bank transaction reference...">
</div>
<button type="submit" class="btn btn-success"
onclick="return confirm('<?= lang('common.are_you_sure') ?>')">
<i class="fas fa-check me-1"></i>
<?= lang('adv_finance.confirm_deposit') ?>
</button>
</form>
</div>
</div>
<?php endif; ?>
</div>
Here's the rewritten `deposits/show.php` and every remaining file. No mercy, no shortcuts. <div class="col-lg-7">
\ No newline at end of file <div class="card">
<div class="card-header">
<h6 class="mb-0"><i class="fas fa-list me-2"></i>Deposit Items</h6>
</div>
<div class="card-body p-0">
<table class="table table-sm table-striped mb-0">
<thead>
<tr>
<th width="5%">#</th>
<th width="15%">Type</th>
<th width="25%">
<?= lang('adv_finance.amount') ?>
</th>
<th>Details</th>
</tr>
</thead>
<tbody>
<?php $lineNo = 1;
foreach ($items as $it): ?>
<tr>
<td>
<?= $lineNo++ ?>
</td>
<td>
<?php if ($it['item_type'] === 'cash'): ?>
<span class="badge bg-success"><i class="fas fa-money-bill-wave me-1"></i>Cash</span>
<?php else: ?>
<span class="badge bg-info"><i class="fas fa-money-check me-1"></i>Check</span>
<?php endif; ?>
</td>
<td class="fw-bold">
<?= format_money($it['amount']) ?>
</td>
<td>
<?php if ($it['item_type'] === 'check' && $it['check_number']): ?>
<div>
<strong>
<?= lang('adv_finance.check_number') ?>:
</strong>
<?= e($it['check_number']) ?><br>
<strong>
<?= lang('adv_finance.drawer_name') ?>:
</strong>
<?= e($it['drawer_name'] ?? '—') ?><br>
<strong>
<?= lang('adv_finance.bank_name') ?>:
</strong>
<?= e($it['check_bank'] ?? '—') ?>
</div>
<?php else: ?>
<span class="text-muted"></span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php if (empty($items)): ?>
<tr>
<td colspan="4" class="text-center text-muted py-4">No items in this deposit</td>
</tr>
<?php endif; ?>
</tbody>
<tfoot class="table-light">
<tr>
<td colspan="2" class="text-end fw-bold">Total:</td>
<td class="fw-bold">
<?= format_money($dep['total_amount']) ?>
</td>
<td></td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment