GymSync  /  Platform Documentation

GymSync — Gym Management SaaS

A production-grade, multi-tenant gym management platform built with Laravel, Blade and Tailwind CSS. Single database, logical tenant isolation, light & dark mode.

Laravel 13 MySQL Tailwind CSS Vanilla JS Light + Dark Responsive
246Routes
39Models
62Controllers
76Migrations
209Blade Views
447Tests Passing

Introduction

GymSync is a SaaS platform where a platform owner (SaaS Admin) onboards client organizations and provisions gyms/branches for them. Each client pays a monthly subscription, and each gym runs day-to-day operations: members, memberships, payments, attendance, staff, inventory and reporting.

Tenant Isolation

Every tenant record carries client_id; gym operations also carry gym_id. Context is always derived from the authenticated user, never trusted from the request.

Authorization First

Policies, role middleware, Form Requests and tenant middleware combine to enforce access at every layer.

Server-Rendered UI

Pure Blade + Tailwind with Alpine.js only for small interactions. No SPA, no heavy UI frameworks.

Tech Stack

Core technologies and how they are used.

LayerTechnologyPurpose
BackendPHP 8.5 / Laravel 13Routing, Eloquent ORM, queues, scheduler, mail, validation
DatabaseMySQLSingle shared schema, logical multi-tenancy, soft deletes
FrontendBlade + Tailwind CSSServer-rendered views, responsive components, theming tokens
InteractivityVanilla JS / Alpine.jsSidebar, dropdowns, modals, theme toggle, small fetch calls
AuthLaravel Breeze-style + Spatie PermissionsSession auth, roles, email verification, password reset
PDFDomPDFReceipts, invoices, salary slips, reports
TestingPHPUnit447 feature/unit tests, RefreshDatabase, SQLite in-memory

Roles & Permissions

Seven roles span the platform owner, organization owners and gym staff.

SaaS Admin

Platform owner. Creates clients & gyms, manages plans, subscriptions, invoices, payments, reports, activity logs and support queries. Global bypass for tenant scoping.

Client Admin

Owns an organization. Manages all branches, staff, trainers, members, plans, finances, inventory, reports, settings and support queries. Cannot create new gyms.

Gym Manager

Operates assigned branch(es): members, memberships, attendance, equipment, products, leaves and support queries for that gym.

Gym Staff / Receptionist

Front-desk operations: member check-in, memberships, payments and receipts scoped to the active gym.

Accountant

Financial modules: payments, expenses, salaries and financial reports. Can force-delete financial records.

Reception Manager / Security

Additional on-site roles for managing reception flow and gate/security checks at check-in.

Multi-Tenancy & Business Hierarchy

The platform enforces a strict hierarchy so one tenant can never see another's data.

SaaS Admin Client (Organization) Gym / Branch Users & Members

client_id

Every tenant-owned table stores the owning client. Derived from the authenticated user's context.

gym_id

Gym-operational tables also store the branch. The TenantContext service resolves the active gym.

Never Trusted

IDs are never taken from hidden fields or query strings. Policies + middleware validate access on every request.

Feature Modules

Every major capability of the platform, with representative route names.

Dashboard & Analytics

Role-aware dashboards for SaaS Admin, Client Admin, Gym Manager and Staff. KPI cards, revenue trends, attendance stats and quick actions.

saas.dashboardclient.dashboardgym.dashboard

Client (Organization) Management

SaaS Admin creates client organizations, approves pending sign-ups, manages status (pending/active/suspended/inactive) and views subscription details.

saas.clients.indexsaas.clients.createsaas.clients.approvesaas.clients.show

Gym & Branch Management

Create gyms/branches with branch codes, timezone, currency. Branch switching for multi-branch clients, status control and impersonation to support a gym.

saas.gyms.indexclient.branches.indexsaas.gyms.impersonateclient.branches.switch

SaaS Plans & Subscriptions

Plans define monthly price, max gyms/users/members and extra-gym pricing. Subscriptions track base + extra gym amounts, statuses and billing cycle.

saas.plans.indexsaas.subscriptions.indexsaas.subscriptions.change-plan

SaaS Invoices & Payments

Monthly platform billing with partial payments, tax (GST/FED), discounts, adjustments, refunds and voiding. Multiple payments per invoice are supported.

saas.invoices.indexsaas.payments.indexsaas.payments.refundsaas.invoices.adjustments

Members

Full member lifecycle: registration, profile, PIN & device UID, assigned trainer, status control (active/inactive/blocked) and soft-delete/restore.

client.members.indexclient.members.createclient.members.statusclient.members.show

Membership Plans

Configurable plans by category (male/female/student/family/custom), duration units (day/week/month/year) and fee with active/inactive status.

client.membership-plans.indexclient.membership-plans.create

Member Subscriptions

Assign plans, renew, change plan, freeze/resume and track price, discount, payable, paid and balance. Only one active subscription per member is enforced.

client.subscriptions.indexclient.subscriptions.renewclient.subscriptions.freezeclient.subscriptions.resume

Payments & Receipts

Record member payments (cash, bank, jazzcash, easypaisa, card, other), auto-generate PDF receipts, email receipts to members and owners.

client.payments.indexclient.receipts.indexclient.payments.show

Attendance & Biometric

PIN, manual and fingerprint check-in/out. Open-session protection, expired-member blocking, biometric device registry and an attendance simulator for testing.

client.attendance.indexclient.attendance.manualclient.biometric-devices.indexclient.biometric-simulator.index

Leads & CRM

Lead pipeline with statuses (new/contacted/trial/converted/lost), call logging, trial dates, assignment to trainers and one-click conversion to member.

client.leads.indexclient.leads.createclient.leads.convert

Trainers

Trainer profiles with specialization, rates, bank details, commission and status. Members can be assigned to trainers.

client.trainers.indexclient.trainers.createclient.trainers.status

Staff, Salaries & Leave

Staff user management with roles, salary calculation (base + bonus + commission − deductions), salary slip emailing and leave types/approvals.

client.users.indexclient.salaries.indexclient.leaves.indexclient.leave-types.index

Products, Inventory & Sales

Product categories, products with stock and low-stock thresholds, stock movements and POS-style sales that generate invoices for members or customers.

client.products.indexclient.product-categories.indexclient.sales.indexclient.products.stock.store

Equipment

Equipment categories and asset records with cost, purchase date, condition, status and maintenance tracking.

client.equipment.indexclient.equipment-categories.index

Expenses

Operational expenses by category with recurrence, payment method and approval status.

client.expenses.indexclient.expenses.create

Reports & Insights

Payment, attendance, subscription-expiry and member reports plus platform-level reports and monthly summary generation with exportable output.

client.reports.indexclient.reports.indexsaas.reports.indexclient.reports.attendance

Feedback (NPS / Reviews)

Collect feedback via a shareable public link or manually. Star ratings, sources (member/lead/public/manual), status workflow and request-by-email flow.

client.feedback.indexclient.feedback.requests.createfeedback.public

AI Assistant

Read-only natural-language assistant scoped to the active gym. Answers attendance, renewals, collections and member-lookup questions with safe, parameterised queries.

client.assistant.indexclient.assistant.ask

Support Queries NEW

Client Admin / Gym Manager raise queries, problems, issues, complaints or suggestions from the active branch. Threaded conversation with the SaaS Admin, status workflow (new/open/in_progress/resolved/closed) and email notifications both ways.

client.support.indexclient.support.createsaas.support.indexsaas.support.show

Notifications

Email/SMS/WhatsApp notification log with per-member and per-user preferences, receipt, expiry and owner-payment notification toggles.

client.settings.edit

Activity Logs & Audit

Immutable audit trail of user actions, logins/logouts and failed attempts with user, role, entity and IP metadata. Scoped by client/gym for tenant safety.

client.activity-logs.indexsaas.activity-logs.index

Gym Settings & Branding

Per-gym branding (logo, favicon), contact details, custom SMTP, receipt/invoice templates with theme colours, commission percentages and support email configuration.

saas.settings.indexclient.settings.editclient.settings.pdf-preview

Trash & Data Recovery

Soft-deleted records across modules with restore and role-gated force-delete to permanently purge data.

client.trash.indexclient.trash.restoreclient.trash.force-delete

Support Query System NEW

A two-way support desk connecting Client Admins / Gym Managers with the SaaS Admin.

Client Side

  • Submit a query from the active branch with a subject, type and message.
  • Track all queries with search, status and type filters.
  • View threaded conversation and reply while the query is open.

SaaS Admin

See queries from all branches and clients with the branch name displayed. Reply, update status (new, open, in_progress, resolved, closed) and record resolution notes.

Query Types

queryproblemissuecomplaintsuggestionother

Status Workflow

NEW OPEN IN_PROGRESS RESOLVED CLOSED

Email Notifications

Client → SaaS Admin

When a new query is submitted, an email is sent to the SaaS Admin support address configured per gym (saas_admin_support_email), falling back to the mail-from address. Subject, type, branch and message are included with a link to open the query.

SaaS Admin → Client

When the SaaS Admin replies, the client's from_email receives a threaded reply email with a button to view the conversation.

Configuration — SaaS Admin → Settings → Gym

SettingColumnPurpose
Gym Support Emailsupport_emailEmail for gym-specific support queries
SaaS Admin Support Emailsaas_admin_support_emailDestination for new query notifications
Enable support notificationssupport_notifications_enabledMaster toggle for support notification emails

Key Files

app/Models/SupportConversation.php app/Models/SupportMessage.php app/Http/Controllers/Client/SupportController.php app/Http/Controllers/Saas/SupportController.php app/Policies/SupportConversationPolicy.php app/Mail/NewSupportQuery.php app/Mail/SupportQueryReply.php resources/views/client/support/* resources/views/saas/support/* resources/views/emails/support/*

Data Models

39 Eloquent models grouped by domain area.

Tenancy & Identity

ClientUserGymGymSettingGymExtraCharge

SaaS Billing

SaasPlanSaasSubscriptionSaasInvoiceSaasPaymentSaasReport

Membership

MemberMembershipPlanMemberSubscriptionMembershipFreezeMemberPaymentReceipt

Operations

AttendanceLogBiometricDeviceTrainerStaffSalaryLeaveLeaveType

CRM

LeadLeadActivityFeedbackFeedbackRequest

Inventory & Sales

ProductProductCategoryProductStockMovementSaleSaleItemEquipmentEquipmentCategory

Finance

Expense

Comms & Support

NotificationNotificationPreferenceSupportConversationSupportMessage

Audit

ActivityLog

Database Schema

Core tables (39 shown). All tenant tables carry client_id; gym records also carry gym_id.

TableDescriptionNotable Columns
clientsClient organizations / tenantsstatus, billing_cycle, next_billing_date
usersAll users across roles (saas_admin → client_admin → staff)role, status, client_id
gymsBranches belonging to a clientbranch_code, timezone, currency, status
gym_userUser ↔ gym assignment pivotrole, status
gym_settingsPer-gym configuration & brandingSMTP, receipts, support emails, commission
gym_extra_chargesExtra-gym billing chargesamount, effective dates, status
saas_plansSubscription plan catalogueprice, max gyms/users/members
saas_subscriptionsClient plan subscriptionsbase + extra amounts, status
saas_invoicesPlatform invoicestotals, tax, discount, due date, status
saas_paymentsPlatform payments (partial allowed)amount, method, status
membersGym membersmember_no, pin_hash, device_uid, status
membership_plansPlan definitionscategory, duration, fee
member_subscriptionsMember plan assignmentsdates, amounts, balance, status
member_subscription_freezesFreeze periodsfreeze dates, extended days
member_paymentsMember paymentsamount, method, status
receiptsGenerated payment receiptsreceipt_no, pdf_path, sent flags
attendance_logsCheck-in / check-out sessionsmethod (pin/manual/fingerprint)
biometric_devicesRegistered attendance devicesdevice_id, api_token, last_seen
leadsSales / enquiry pipelinestatus, source, trial_date
lead_activitiesLead interaction historytype, subject, activity_at
trainersTrainer records incl. bank detailsspecialization, rates, status
staff_salariesPayroll recordsbase, bonus, commission, net, status
leavesLeave requeststype, dates, days, status
leave_typesLeave type catalogueis_paid, deduction, max days
productsRetail products / stocksku, prices, stock qty
product_categoriesProduct categorisationparent_id, slug
product_stock_movementsStock ledgertype, qty, stock_after
salesRetail sales / POSinvoice_no, totals, status
sale_itemsSale line itemsproduct, qty, line total
equipmentGym equipment assetscondition, status, purchase info
equipment_categoriesEquipment categorisationslug, status
expensesOperational expensescategory, amount, status
feedbackFeedback / ratingsrating, source, status
feedback_requestsFeedback request emailstoken, status, sent_at
notificationsNotification delivery logtype, channel, status
notification_preferencesPer member/user prefschannels, toggles
activity_logsAudit trailaction, user, entity, ip
support_conversationsSupport queries (NEW)subject, type, status, branch, resolved_by
support_messagesSupport conversation messages (NEW)sender_type, message, attachments

Routes Reference

All 246 application routes grouped by access area.

MethodURIName
GET/activity-logsclient.activity-logs.index
GET/activity-logs/{activityLog}client.activity-logs.show
POST/assistantclient.assistant.ask
GET/assistantclient.assistant.index
GET/attendanceclient.attendance.index
POST/attendance/manualclient.attendance.manual
POST/attendance/manual/staffclient.attendance.manual.staff
POST/attendance/toggleclient.attendance.toggle
GET/biometric-devices/createclient.biometric-devices.create
DELETE/biometric-devices/{biometricDevice}client.biometric-devices.destroy
GET/biometric-devices/{biometricDevice}/editclient.biometric-devices.edit
GET/biometric-devicesclient.biometric-devices.index
POST/biometric-devicesclient.biometric-devices.store
PUT/biometric-devices/{biometricDevice}client.biometric-devices.update
GET/attendance/simulatorclient.biometric-simulator.index
POST/attendance/simulator/scanclient.biometric-simulator.scan
GET/branchesclient.branches.index
POST/branches/{gym}/switchclient.branches.switch
GET/client/dashboardclient.dashboard
GET/equipment-categories/createclient.equipment-categories.create
DELETE/equipment-categories/{category}client.equipment-categories.destroy
GET/equipment-categories/{category}/editclient.equipment-categories.edit
GET/equipment-categoriesclient.equipment-categories.index
POST/equipment-categoriesclient.equipment-categories.store
PUT/equipment-categories/{category}client.equipment-categories.update
GET/equipment/createclient.equipment.create
DELETE/equipment/{equipment}client.equipment.destroy
GET/equipment/{equipment}/editclient.equipment.edit
GET/equipmentclient.equipment.index
POST/equipmentclient.equipment.store
PUT/equipment/{equipment}client.equipment.update
GET/expenses/createclient.expenses.create
DELETE/expenses/{expense}client.expenses.destroy
GET/expenses/{expense}/editclient.expenses.edit
GET/expensesclient.expenses.index
PATCH/expenses/{expense}/statusclient.expenses.status
POST/expensesclient.expenses.store
PUT/expenses/{expense}client.expenses.update
GET/feedback/createclient.feedback.create
DELETE/feedback/{feedback}client.feedback.destroy
GET/feedbackclient.feedback.index
POST/feedback/public-linkclient.feedback.public-link
GET/feedback/requestsclient.feedback.requests.create
DELETE/feedback/requests/{feedbackRequest}client.feedback.requests.destroy
POST/feedback/requestsclient.feedback.requests.store
GET/feedback/{feedback}client.feedback.show
PATCH/feedback/{feedback}/statusclient.feedback.status
POST/feedbackclient.feedback.store
POST/leads/{lead}/convertclient.leads.convert
GET/leads/createclient.leads.create
DELETE/leads/{lead}client.leads.destroy
GET/leads/{lead}/editclient.leads.edit
GET/leadsclient.leads.index
GET/leads/{lead}client.leads.show
POST/leadsclient.leads.store
PUT/leads/{lead}client.leads.update
GET/leave-types/createclient.leave-types.create
DELETE/leave-types/{leaveType}client.leave-types.destroy
GET/leave-types/{leaveType}/editclient.leave-types.edit
GET/leave-typesclient.leave-types.index
POST/leave-typesclient.leave-types.store
PUT/leave-types/{leaveType}client.leave-types.update
POST/leaves/{leave}/approveclient.leaves.approve
GET/leaves/createclient.leaves.create
DELETE/leaves/{leave}client.leaves.destroy
GET/leavesclient.leaves.index
POST/leaves/{leave}/rejectclient.leaves.reject
POST/leavesclient.leaves.store
GET/members/createclient.members.create
DELETE/members/{member}client.members.destroy
GET/members/{member}/editclient.members.edit
GET/membersclient.members.index
GET/members/{member}client.members.show
PATCH/members/{member}/statusclient.members.status
POST/membersclient.members.store
PUT/members/{member}client.members.update
GET/membership-plans/createclient.membership-plans.create
DELETE/membership-plans/{plan}client.membership-plans.destroy
GET/membership-plans/{plan}/editclient.membership-plans.edit
GET/membership-plansclient.membership-plans.index
POST/membership-plansclient.membership-plans.store
PUT/membership-plans/{plan}client.membership-plans.update
GET/payments/createclient.payments.create
GET/paymentsclient.payments.index
GET/payments/{payment}client.payments.show
POST/paymentsclient.payments.store
GET/products/categories/createclient.product-categories.create
DELETE/products/categories/{category}client.product-categories.destroy
GET/products/categories/{category}/editclient.product-categories.edit
GET/products/categoriesclient.product-categories.index
GET/products/categories/{category}client.product-categories.show
POST/products/categoriesclient.product-categories.store
PUT/products/categories/{category}client.product-categories.update
GET/products/createclient.products.create
DELETE/products/{product}client.products.destroy
GET/products/{product}/editclient.products.edit
GET/productsclient.products.index
GET/products/{product}client.products.show
POST/products/{product}/stockclient.products.stock.store
POST/productsclient.products.store
PUT/products/{product}client.products.update
GET/receipts/{receipt}/downloadclient.receipts.download
GET/receiptsclient.receipts.index
POST/receipts/{receipt}/resendclient.receipts.resend
GET/receipts/{receipt}client.receipts.show
GET/renewalsclient.renewals.index
GET/reports/attendanceclient.reports.attendance
GET/reports/expiringclient.reports.expiring
POST/reports/expiring/{subscription}/remindclient.reports.expiring.remind
GET/reports/generateclient.reports.generate
GET/reportsclient.reports.index
GET/reports/leave-balancesclient.reports.leave-balances
GET/reports/membersclient.reports.members
GET/reports/paymentsclient.reports.payments
POST/salaries/{salary}/cancelclient.salaries.cancel
GET/salaries/createclient.salaries.create
DELETE/salaries/{salary}client.salaries.destroy
GET/salaries/{salary}/editclient.salaries.edit
GET/salariesclient.salaries.index
POST/salaries/{salary}/mark-paidclient.salaries.mark-paid
GET/salaries/previewclient.salaries.preview
POST/salariesclient.salaries.store
PUT/salaries/{salary}client.salaries.update
GET/sales/createclient.sales.create
DELETE/sales/{sale}client.sales.destroy
GET/salesclient.sales.index
GET/sales/{sale}/invoiceclient.sales.invoice
POST/sales/{sale}/resendclient.sales.resend
GET/sales/{sale}client.sales.show
POST/salesclient.sales.store
GET/settingsclient.settings.edit
GET/settings/pdf-design-previewclient.settings.pdf-design-preview
GET/settings/pdf-previewclient.settings.pdf-preview
PUT/settingsclient.settings.update
POST/subscriptions/{subscription}/change-planclient.subscriptions.change-plan
GET/subscriptions/{subscription}/change-planclient.subscriptions.change-plan.create
GET/subscriptions/createclient.subscriptions.create
POST/subscriptions/{subscription}/freezeclient.subscriptions.freeze
GET/subscriptions/{subscription}/freezeclient.subscriptions.freeze.create
GET/subscriptionsclient.subscriptions.index
POST/subscriptions/{subscription}/renewclient.subscriptions.renew
GET/subscriptions/{subscription}/renewclient.subscriptions.renew.create
POST/subscriptions/{subscription}/resumeclient.subscriptions.resume
GET/subscriptions/{subscription}client.subscriptions.show
POST/subscriptionsclient.subscriptions.store
GET/support/createclient.support.create
GET/supportclient.support.index
POST/support/{support}/replyclient.support.reply
GET/support/{support}client.support.show
POST/supportclient.support.store
GET/trainers/createclient.trainers.create
DELETE/trainers/{trainer}client.trainers.destroy
GET/trainers/{trainer}/editclient.trainers.edit
GET/trainersclient.trainers.index
GET/trainers/{trainer}client.trainers.show
PATCH/trainers/{trainer}/statusclient.trainers.status
POST/trainersclient.trainers.store
PUT/trainers/{trainer}client.trainers.update
DELETE/trash/{module}/{id}client.trash.force-delete
GET/trashclient.trash.index
POST/trash/{module}/{id}/restoreclient.trash.restore
GET/client/users/createclient.users.create
DELETE/client/users/{user}client.users.destroy
GET/client/users/{user}/editclient.users.edit
GET/client/usersclient.users.index
PATCH/client/users/{user}/statusclient.users.status
POST/client/usersclient.users.store
PUT/client/users/{user}client.users.update
GET/gym/dashboardgym.dashboard
GET/gym/selectgym.select
POST/gym/selectgym.select.store
POST/impersonation/stopimpersonation.stop
MethodURIName
GET/super-admin/activity-logssaas.activity-logs.index
GET/super-admin/activity-logs/{activityLog}saas.activity-logs.show
POST/super-admin/clients/{client}/approvesaas.clients.approve
GET/super-admin/clients/createsaas.clients.create
DELETE/super-admin/clients/{client}saas.clients.destroy
GET/super-admin/clients/{client}/editsaas.clients.edit
GET/super-admin/clientssaas.clients.index
GET/super-admin/clients/{client}saas.clients.show
POST/super-admin/clientssaas.clients.store
PUT/super-admin/clients/{client}saas.clients.update
GET/super-admin/dashboardsaas.dashboard
GET/super-admin/gyms/createsaas.gyms.create
DELETE/super-admin/gyms/{gym}saas.gyms.destroy
GET/super-admin/gyms/{gym}/editsaas.gyms.edit
POST/super-admin/gyms/{gym}/impersonatesaas.gyms.impersonate
GET/super-admin/gymssaas.gyms.index
GET/super-admin/gyms/{gym}saas.gyms.show
PATCH/super-admin/gyms/{gym}/statussaas.gyms.status
POST/super-admin/gymssaas.gyms.store
PUT/super-admin/gyms/{gym}saas.gyms.update
PATCH/super-admin/invoices/{invoice}/adjustmentssaas.invoices.adjustments
GET/super-admin/invoicessaas.invoices.index
POST/super-admin/invoices/{invoice}/paymentssaas.invoices.payments.store
GET/super-admin/invoices/{invoice}saas.invoices.show
PATCH/super-admin/invoices/{invoice}/statussaas.invoices.status
GET/super-admin/paymentssaas.payments.index
POST/super-admin/payments/{payment}/refundsaas.payments.refund
POST/super-admin/payments/{payment}/voidsaas.payments.void
GET/super-admin/plans/createsaas.plans.create
DELETE/super-admin/plans/{plan}saas.plans.destroy
GET/super-admin/plans/{plan}/editsaas.plans.edit
GET/super-admin/planssaas.plans.index
POST/super-admin/planssaas.plans.store
PUT/super-admin/plans/{plan}saas.plans.update
GET/super-admin/reports/generatesaas.reports.generate
GET/super-admin/reports/history/{report}/downloadsaas.reports.history.download
GET/super-admin/reportssaas.reports.index
GET/super-admin/settingssaas.settings.index
GET/super-admin/settings/{gym}saas.settings.show
PUT/super-admin/settings/{gym}saas.settings.update
POST/super-admin/subscriptions/{subscription}/cancelsaas.subscriptions.cancel
GET/super-admin/subscriptions/createsaas.subscriptions.create
POST/super-admin/subscriptions/{subscription}/downgradesaas.subscriptions.downgrade
GET/super-admin/subscriptions/{subscription}/editsaas.subscriptions.edit
GET/super-admin/subscriptionssaas.subscriptions.index
GET/super-admin/subscriptions/{subscription}saas.subscriptions.show
POST/super-admin/subscriptionssaas.subscriptions.store
PUT/super-admin/subscriptions/{subscription}saas.subscriptions.update
POST/super-admin/subscriptions/{subscription}/upgradesaas.subscriptions.upgrade
GET/super-admin/supportsaas.support.index
POST/super-admin/support/{support}/replysaas.support.reply
GET/super-admin/support/{support}saas.support.show
PATCH/super-admin/support/{support}/statussaas.support.status
MethodURIName
GET//—
POST/confirm-password—
GET/loginlogin
POST/login—
POST/logoutlogout
GET/confirm-passwordpassword.confirm
POST/forgot-passwordpassword.email
GET/forgot-passwordpassword.request
GET/reset-password/{token}password.reset
POST/reset-passwordpassword.store
PUT/passwordpassword.update
DELETE/profileprofile.destroy
GET/profileprofile.edit
PATCH/profileprofile.update
GET/verify-emailverification.notice
POST/email/verification-notificationverification.send
GET/verify-email/{id}/{hash}verification.verify
MethodURIName
POST/api/v1/biometric/attendanceapi.biometric.attendance
GET/api/v1/biometric/pingapi.biometric.ping
MethodURIName
GET/feedback/submit/{token}feedback.public
POST/feedback/submit/{token}feedback.public.store

Colour System

A gold-forward brand palette with semantic theme tokens for light and dark modes.

Primary — Gym Gold

primary-300
#FFE48F
primary-400
#FFC721
primary-500
#F5B800
primary-600
#D99A00
primary-700
#9A6600
primary-950
#3A2800

Supporting Palettes

Success
#12B76A
Info
#2E90FA
Purple
#7A5AF8
Accent
#F79009
Danger
#F04438
Sidebar
#0F172A

Semantic Theme Tokens (light / dark)

TokenLightDarkUsage
canvas#F1F5F9#0B1220App background
surface#F8FAFC#151F32Muted surface / inputs
card#FFFFFF#111A2BCard backgrounds
line / divider#E2E8F0#253149Borders
ink#101828#F8FAFCPrimary text
subtle#64748B#A8B3C7Secondary text
mute#94A3B8#73809ATertiary text
control#D0D5DD#344054Input & button borders

Radius scale control 10px · card 14px · panel 18px. Font: Inter.

Key Relationships

High-level Eloquent relationship map.

Client hasMany Users, Gyms, Subscriptions, Invoices, Payments, Members
Gym belongsTo Client · hasMany Members, Staff (gym_user), Equipment, Products, AttendanceLogs, Settings
Member belongsTo Gym · hasMany Subscriptions, Payments, AttendanceLogs, Receipts
MemberSubscription belongsTo Member, MembershipPlan · hasMany Payments
SupportConversation belongsTo Client, Gym, User, resolvedBy · hasMany SupportMessages

UI Components & Patterns

Consistent, reusable Blade building blocks across both portals.

Layout

Dark navy sidebar with grouped navigation, sticky topbar, responsive off-canvas drawer, and a nav that adapts per role (client vs SaaS admin).

Forms

Theme-aware inputs use the form-control class (bg-surface text-ink border-control) so fields stay legible in both light and dark mode. Validation errors render inline.

Tables & Empty States

Sortable/filterable listings with pagination, status badges, row actions and friendly empty states.

Feedback

Flash alerts, confirmation modals, status pills, stat cards, and toast-style notifications via vanilla JS.

Dark mode is class-based (data-theme / .dark) with all colours sourced from CSS custom properties — never hard-coded per view.

Services & Architecture

Reusable service classes centralise business rules.

TenantContextActivityLogServiceAttendanceService BillingServiceMemberPaymentServiceReceiptService NotificationServiceSaleServiceSaleInvoiceService ProductStockServiceSalaryCalculatorLeaveBalanceService DeviceUidService

Mailing

Transactional mailables for onboarding, billing, receipts, payroll and support.

ClientWelcomeMailGymCreatedMailSubscriptionAssignedMail SaasInvoiceMailSaasInvoiceReminderMailSaasAccountSuspendedMail SaasAccountReactivatedMailMemberWelcomeMailMembershipAssignedMail PaymentReceiptMailStaffWelcomeMailSalarySlipMail SaleInvoiceMailFeedbackRequestMailNotificationMail MonthlySummaryMailNewSupportQuerySupportQueryReply

Testing & Quality

The suite proves functionality and multi-tenant isolation.

447

Passing tests

1483

Assertions

SQLite

In-memory test DB

Run the suite with php artisan test. Coverage includes cross-tenant isolation across 18 resource types, role boundaries, branch-switch protections, and module-specific feature tests.

GymSync — Gym Management SaaS · Documentation generated for the current build.