- 30 Aug, 2026 5 commits
-
-
Claude authored
The system predates branches, and adoption of the branch switcher was partial: 44 of 186 Livewire components used UsesBranchScope, and several that imported it never actually called it. The dashboard was the worst case — half its widgets were branch-aware and half silently reported academy-wide totals next to them, so the numbers on one screen were not comparable with each other. OC-Sport runs 7 active branches, so every unscoped widget was showing six other branches' data. Dashboard: scoped trainers-present, pending payslips, pending documents, low stock and expiring medical certificates, which were academy-wide. All six dashboard widgets (revenue, product revenue, subscription revenue, enrolment trends, overdue renewals, trainer dues) now filter by branch, including the raw-SQL CTEs in the revenue breakdowns. Lists and reports: events, evaluations, base prices, pricing rules, promotions, stock counts, kits, document approvals, trainers, trainer advances, payroll, essential deliveries and the financial report. Pickers: participant, group, program, facility, warehouse, product and employee selectors now offer only the active branch's records, so a transfer or invoice cannot silently reference another branch. POS and InvoiceShow used auth()->user()->branch_id directly, ignoring the switcher entirely — a user who switched branch still transacted against their home branch. Both now read the active branch. Deliberately left unscoped: parent- and guardian-facing screens, which are scoped to their own children and have no branch switcher, and single-record detail screens, which are already scoped by the record and would hide legitimately related history for participants who moved between branches. Co-Authored-By:Claude Opus 5 <noreply@anthropic.com>
-
Claude authored
Postgres '=' is case-sensitive, so a user stored as 'Km...@gmail.com' could not log in from a phone keyboard that lowercases the email field. The lookup in AuthService returned null before Hash::check ever ran, so this presented as "wrong password" and was invisible in login_history — that table is only written once a user has been found. On OC-Sport this affected 8 of 26 accounts, and had already produced one duplicate registration: a user who could not get in simply signed up again with the same address in lowercase. - CredentialNormalizer: one canonical shape for emails and phones - AuthService: case-insensitive email lookup, deterministically ordered so a pre-existing case-duplicate pair resolves to the account actually in use rather than an arbitrary row; phone lookup matches local and +20 forms - User: set-mutators so new rows are stored canonical - Migration: normalises existing rows, skipping and logging any that would collide, since those are duplicate accounts needing a human merge rather than a guess Co-Authored-By:Claude Opus 5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
"The schedule" is not one document. Different people print it for different reasons and a single layout serves none of them, so this ships three sheets sized for their actual use scene: - Facility day board (A4/A3 landscape, auto-picked by segment count) — time down the side, the facility's physical grid segments across the top, every booking in its own cell. This is the one that carries the grid, and the one that did not exist. Built to be pinned at the court entrance and read from a few metres away. - Facility week board (A3 landscape) — seven days x time, segments as a badge per booking. The notice-board overview. - Trainer day cards (A4 portrait, two-up, cut lines) — one pocket card per trainer: when, where, which segment, how many players. A trainer does not want an A3 off the wall. Rendered as branded HTML and printed from the browser rather than through a PDF library: Arabic shaping survives intact, Cairo and the academy's brand colour render exactly, and nothing queues on the server. print-color-adjust is set explicitly, without which browsers strip every fill and the whole colour-coded board arrives as blank boxes. Group colours move to a shared GroupColor palette used by both the builder and every sheet, so a coach who learns "our group is the teal one" on the wall sees the same teal in the app. The hues stay distinguishable in greyscale, because plenty of academies print on a mono laser. Bookings read as a filled, tinted cell rather than a thin coloured stripe — a 3px rail is invisible at the distance these are actually read from. Co-Authored-By:Claude Opus 5 (1M context) <noreply@anthropic.com>
-
Mahmoud Aglan authored
Icons - No emoji anywhere in the UI. Extracted the sidebar's inline SVG map into a single <x-ui.icon name="..."> component and added the icons the pricing work needed, so there is one source instead of a per-view copy. Discount recipes now carry icon NAMES, not glyphs. Schedule builder - Facilities are scoped to the selected branch. The screen listed every branch's facilities, which is how someone books the wrong building. A ?facility_id= carried over from another branch (bookmark, back button) is now dropped instead of silently overriding the branch scope. - Groups are scoped to the facility's branch AND to the sports that facility hosts, so a football court no longer offers swimming groups. That link did not exist, so this adds a facility_activities pivot. A facility that declares no activities still hosts anything, so nothing breaks for academies that have not filled it in. Facility grid - Removed the arbitrary ceilings (rows/columns capped at 10, lanes at 20). Physical space is not limited to a number we picked. - New facilities never got a layout, which is why the grid silently failed to appear on them. FacilityService::create now seeds one, the migration backfills every existing facility that has none, and the default is a 1x1 grid — "one whole space, not subdivided yet" — rather than inventing a subdivision nobody asked for. - Grid size is editable straight from facility settings, with a live preview of the cells being described. Shrinking onto a segment that holds a confirmed future reservation is refused rather than silently dropping someone's booking. - Sports and starting grid are both settable at creation time too. Co-Authored-By:Claude Opus 5 (1M context) <noreply@anthropic.com>
-
Mahmoud Aglan authored
Two bugs meant no pricing rule has ever applied correctly: 1. Condition keys never matched. The engine reads min/max/values; the wizard wrote min_age/min_children/target_gender and the form blade wrote a third set. Ranges saw null bounds and list rules saw an empty allow-list, both of which passed, so every rule applied to every participant. 2. Percentages were 100x too small. applyAdjustment divides by 10000 (basis points) but both screens stored a plain percent, so "20%" discounted 0.2%. They masked each other, which is why the symptom looked like a broken engine rather than two bugs — and why everyone moved to the untyped super-admin price override instead. Engine - ConditionSchema is now the single owner of the conditions vocabulary; builder, engine, simulator and migration all read keys from it. - Percent handles all basis-point conversion; nothing else touches the raw column. - evaluateInList fails closed instead of treating an empty allow-list as "match everyone". - custom rules no longer auto-apply; they are picker-only. - enrollment_timing honours days_before_start (fails closed without a program start date instead of silently passing). - Global discount cap reads system_settings rather than a hardcoded constant with a TODO. - New: explain(), audience(), wouldApply(), and role-capped manual discounts. Per-branch - pricing_rule_branches pivot so one rule targets many branches, instead of one near-identical row per branch that drifts apart. Stacking - is_stackable now defaults to false; best-of-one is the normal case and stacking is an explicit opt-in. Authoring - The five-step column editor becomes a recipe gallery plus an Arabic sentence, with a live simulator on a real participant and an audience count that warns when a rule would hit everyone. Saving a conditionless rule is refused. Checkout - ManagesDiscounts trait plus <x-pricing.discount-picker>: branch-scoped, searchable, pinned favourites, replace-vs-stack inline, blocked rows show why. Wired into CollectPaymentWizard renewals; discount names are frozen onto invoice.metadata so receipts survive later rule changes. - NewRegistrationWizard now prices through the engine using a provisional context built from the form, since the participant row does not exist yet. The step-4 guard still checks the base price, so a 100% discount is not mistaken for an unpriced program. Migration - Rewrites conditions onto the canonical keys and scales percentages to basis points. Rules whose conditions cannot be mapped confidently are deactivated rather than guessed, with the old JSON kept in metadata.legacy_conditions. Also fixes list and coupon views that rendered the raw column (a 20% rule would have displayed as 2000%), and adds the [x-cloak] CSS rule that was missing app-wide. Co-Authored-By:Claude Opus 5 (1M context) <noreply@anthropic.com>
-
- 27 Aug, 2026 2 commits
-
-
Mahmoud Aglan authored
Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
- Switch streamCsv from chunk() to cursor() to fix truncated export - Mirror Livewire query in ExportController (search by membership_id/national_id) - Apply PermissionService::applyScope() to export query - Fix branch scoping fallback to match UsesBranchScope trait - Add membership_type and is_free filters to list and export - Pass sort params to export so CSV respects current sort order - Whitelist sort columns to prevent SQL injection - Output enum values and is_free column in CSV Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
- 24 Aug, 2026 1 commit
-
-
Mahmoud Aglan authored
Rebuilt the transfer wizard to support selecting multiple participants from a source group and moving them all to a destination group at once. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
- 23 Aug, 2026 7 commits
-
-
Mahmoud Aglan authored
When a training schedule is created or updated, immediately generate sessions for the next 7 days so trainers see today's sessions without waiting for the nightly cron. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Trainers now see sessions for groups where they are head_trainer_id in addition to explicit assignments, ensuring no sessions are missed. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Client-side Alpine.js search that filters enrollments by name (Arabic/English) or phone number instantly without server round-trips. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Business rule: non-payment or absence = debt, not removal. Participants stay enrolled and appear in attendance regardless of payment/attendance. - Remove frozen/suspended filter from attendance generation - DeactivateExpiredEnrollments now only logs (no status change) - EnforceAttendanceThresholds only warns (no auto-suspend) - SuspendOnThreshold listener disabled (log only) - BackfillAttendanceRecords no longer skips frozen/suspended Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
SessionCreated event had no listener to generate attendance records for existing enrollments. Added GenerateSessionAttendance listener, fixed rescheduleSession to dispatch SessionCreated, added attendance generation on payment-activated enrollments, and added daily backfill command as safety net. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Qualify status and training_group_id with table name to avoid ambiguity when joining participants/people tables for name sorting. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
- Add WithSorting trait to POSHistory, ActivityLog, MessageLog - Migrate GroupList and ProgramList to use WithSorting trait (remove duplicate) - Add sortable columns (name, date, payment) to GroupShow enrollments subtable - Add sort-header components to POS, ActivityLog, and MessageLog blade views - Fix FK violation on group/program delete: nullify enrollments.transferred_to_id - Fix Arabic name confirmation: normalize ي/ى, ة/ه, hamza variants before compare Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
- 22 Aug, 2026 3 commits
-
-
Mahmoud Aglan authored
- ParticipantList: default sort by name_ar ASC, added sort headers for activity and membership columns - EnrollmentList: added sort by participant name (join), group name, and payment_status columns - GroupList: default sort by name_ar ASC instead of created_at Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
UserForm was setting role_id on the users table but not syncing to the role_user pivot table. Permission checks use the pivot (roles() relation), so users appeared to have the correct role in the UI but had zero permissions in practice. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
The group view was marking players as "unpaid" if they had ANY unpaid invoice (products, etc.), even if their program subscription was fully paid. Now the logic only checks: 1. Enrollment payment_status (paid/waived = done) 2. Enrollment's linked invoice status 3. Recent subscription invoice with matching program name Also fixed ActivateEnrollmentOnPayment listener to properly set payment_status='paid' on enrollments when subscription invoice is paid (was only activating status, not updating payment_status). Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
- 20 Aug, 2026 1 commit
-
-
Mahmoud Aglan authored
- Group view: added KPI cards for paid/unpaid/free player counts - Group view: added essential product purchase stats with progress bars - Group view: added installment info column per player in enrollments tab - Fixed PHP 8.2+ trait property conflict: removed $sortBy/$sortDir redeclarations from 13 Livewire components that use WithSorting trait, moved custom defaults to mount() instead Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
- 19 Aug, 2026 3 commits
-
-
Mahmoud Aglan authored
- Sort header component: blue active arrow, hover-to-reveal for inactive, cursor-pointer, group-hover animation - Participant list: name column now sortable (joins people table) - Trainer list: name column sortable (joins employees→people) Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
- Created WithSorting trait (shared across all 35 list components) - Created <x-ui.sort-header> Blade component with arrow indicators - All table lists now support click-to-sort on key columns (persisted via URL) - Trainer edit form: added salary amount + frequency fields from Employee model - Trainer edit form: person data (name, national_id, phone) already included Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
- Free player: is_free column, toggle button on participant show, badge component across all views, skip invoice on enrollment/renewal for free players - Trainer delete: full cascade (cancel compensations/advances, remove future attendance, cancel assignments, nullify trainer refs, deactivate user, archive employee/person, free national_id for reimport) - Trainer edit: person data fields (name, national_id, phone) editable from trainer form Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
- 16 Aug, 2026 2 commits
-
-
Mahmoud Aglan authored
This was the THIRD auto-suspend mechanism (alongside send-alerts and the real-time listener). It directly wrote status='suspended' without going through the event/listener. Now all three paths respect the same opt-in setting. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Both the scheduled command and the real-time listener now check 'auto_suspend_on_threshold' setting (default: false). Auto-suspend will only fire if explicitly enabled in system_settings. This prevents participants from being repeatedly suspended after reactivation due to old attendance records in the lookback window. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
- 15 Aug, 2026 1 commit
-
-
Mahmoud Aglan authored
-
- 13 Aug, 2026 6 commits
-
-
Mahmoud Aglan authored
- New migration: attachment_path + attachment_name columns on expenses - ExpenseForm uses WithFileUploads trait, accepts jpg/png/webp/pdf up to 5MB - Drag-drop upload zone in the form with preview of attached file name - Files stored in storage/app/public/expenses/ Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
When a participant goes from suspended/frozen/inactive → active, the listener now regenerates expected attendance records for all their active enrollments' future sessions. Previously, reactivated participants were invisible in attendance because records were only generated at session creation time. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Payment Collection (CollectPaymentWizard): - Remove branch filter from participant search (POS sells cross-branch) - Include Draft status in outstanding invoice query - Add invoice search mode (search by invoice number/contact name) - Allow walk-in invoices to be found and paid directly - Accept inactive/frozen/registered participants (not just active) Retroactive Enrollment Wizard: - Fix "paid outside system" to use PaymentService (creates proper Payment + Transaction records for financial reports) - Add server-side validation in confirm() before DB transaction - Add national_id duplicate check to prevent duplicate participants - Add price=0 guard (show error if no base price and no override) - Fix rounding loss: remainder goes to last month's invoice - Pass skip_auto_invoice to prevent double invoice creation POS Partial Payment: - Enable allows_partial_payment on all existing products (migration) so the deposit/partial payment UI appears at checkout Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
All financial-related pages are now reachable from the Financial Overview via quick-action buttons with proper permission gates. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Shows all POS receipts (RCP-*) containing this product with: receipt number, customer, total, payment status, method, cashier, date. Each receipt has a print link and invoice link for further actions. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
- Prominent button with colored border (amber pulse when unpinned, green when pinned) - Dropdown with branch list, selected indicator, and pin status - Pin icon shows when branch is locked - Selecting a branch auto-pins it (persists across logout/login) - Unpin button available to release the lock - Visual hint text explains pin behavior Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
- 10 Aug, 2026 2 commits
-
-
Mahmoud Aglan authored
Trainers section (purple header) shows first with max 2-3 records. Players section (blue header) shows below with full table. Access control: - Trainer account: can only mark their own attendance (others disabled) - Supervisor/head_trainer (role level >= 60): can mark all trainers Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Branch: saves preferred_branch_id to users table on switch, restores on next login/session so you stay on the branch you last picked. Expenses: adds status column (active/cancelled) with cancel button that creates a reversing double-entry transaction. Cancelled expenses remain visible but excluded from totals. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
- 09 Aug, 2026 7 commits
-
-
Mahmoud Aglan authored
Shows when the invoice was last paid instead of when it was created. List is already sorted by paid_at DESC. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Shows the method (cash/card/wallet/etc) from the invoice's most recent payment, making it easy to see how each invoice was settled at a glance. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
The print receipt was always showing only the total amount, even when the transaction was a partial payment (deposit). Now shows paid amount, remaining balance, and a "مدفوع جزئياً" badge when the linked invoice has outstanding balance. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
The payment summary panel was only counting inbound payments, ignoring outbound refunds entirely. Now shows net amounts (inbound - refunds) per method, and displays a refund line when refunds exist in the selected period. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Shows most recently paid invoices at the top so the user sees the latest actual money collected, not just the latest generated. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
CollectPaymentWizard and ReconciliationWizard were creating payments without payer_type/payer_id, making them invisible on participant profiles. Now PaymentService inherits payer from the invoice's billable when callers omit it. Also auto-generates reference if empty. Fixed 113 orphaned payments in production. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Auto-generated renewal invoices are created by the system, but collected_by records the real person who collected the money from the participant. Set on every payment collection via updatePaidAmount. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-