first commit
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
/** Mirrors the tenant-scoped response from GET client/access-devices. */
|
||||
export interface AccessDevice {
|
||||
deviceId: string
|
||||
deviceName: string
|
||||
isActive: boolean
|
||||
lastSeenAt?: string | null
|
||||
isOnline: boolean
|
||||
presenceExpiresAt?: string | null
|
||||
isClaimed: boolean
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export interface AccessAgent {
|
||||
agentId: string
|
||||
name: string
|
||||
isActive: boolean
|
||||
lastSeenAt?: string | null
|
||||
isOnline: boolean
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export interface ClientAccessDevices {
|
||||
agents: AccessAgent[]
|
||||
devices: AccessDevice[]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface ApiResponse<T> {
|
||||
statusCode: number
|
||||
message: string
|
||||
data: T
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/** Attendance entity rows from GET client/attendance. */
|
||||
export interface AttendanceRecord {
|
||||
attendanceId: string
|
||||
fkCustomerId?: string
|
||||
fkUserId?: string
|
||||
fkAccessProfileId?: number
|
||||
deviceUserId: number
|
||||
eventTime: string
|
||||
eventType: string
|
||||
customer?: { cusId: string; cusName: string } | null
|
||||
user?: { userId: string; userName?: string | null } | null
|
||||
accessProfile?: {
|
||||
accessProfileId: number
|
||||
displayName: string
|
||||
personType: string
|
||||
} | null
|
||||
}
|
||||
|
||||
/** Mirrors TodayAttendanceDTO. */
|
||||
export interface TodayAttendance {
|
||||
customerName: string
|
||||
eventTime: string
|
||||
eventType?: string | null
|
||||
}
|
||||
|
||||
/** Mirrors AttendanceCountDTO. */
|
||||
export interface AttendanceCount {
|
||||
customerId: string
|
||||
customerName: string
|
||||
attendanceCount: number
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
export interface LoginRequest {
|
||||
identifier: string
|
||||
password: string
|
||||
userTypeId?: string
|
||||
deviceName?: string
|
||||
rememberMe?: boolean
|
||||
}
|
||||
|
||||
export interface LoginResult {
|
||||
accessToken: string | null
|
||||
refreshToken: string | null
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/** Mirrors BookingDto. */
|
||||
export interface Booking {
|
||||
bookingId: string
|
||||
facilitySessionId: string
|
||||
facilityName?: string | null
|
||||
sessionStartTime?: string | null
|
||||
sessionEndTime?: string | null
|
||||
|
||||
isGuest: boolean
|
||||
customerId?: string | null
|
||||
guestName?: string | null
|
||||
guestPhone?: string | null
|
||||
|
||||
/** Only populated right after booking creation — the guest's receipt. */
|
||||
otpCode?: string | null
|
||||
otpVerified: boolean
|
||||
|
||||
paymentStatus: string
|
||||
amountCharged: number
|
||||
status: string
|
||||
bookedAt: string
|
||||
checkedInAt?: string | null
|
||||
|
||||
/** Public, unauthenticated link to this booking's confirmation page. */
|
||||
shareUrl?: string | null
|
||||
}
|
||||
|
||||
export interface CreateGuestBookingInput {
|
||||
facilitySessionId: string
|
||||
guestName: string
|
||||
guestPhone: string
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
import type { GymPlan } from "@/lib/models/gym-plan"
|
||||
import type { PaymentStatus, SubscriptionSummary } from "@/lib/models/subscription"
|
||||
|
||||
/** One weekly weigh-in (CusWeightLossProgress). */
|
||||
export interface WeightProgressPoint {
|
||||
weeklyProgressId: string
|
||||
weight?: number | null
|
||||
recordedAt: string
|
||||
}
|
||||
|
||||
/** A member's goal/measurements (CustomerTarget) with their weight history. */
|
||||
export interface CustomerTarget {
|
||||
customerTargetId: string
|
||||
currentWeight?: number | null
|
||||
currentHeight?: number | null
|
||||
targetWeight?: number | null
|
||||
goal?: string | null
|
||||
timePeriod?: string | null
|
||||
weeklyProgresses?: WeightProgressPoint[] | null
|
||||
}
|
||||
|
||||
/** Customer entity as serialized by the backend (camelCase of the EF model). */
|
||||
export interface MemberProfile {
|
||||
cusId: string
|
||||
cusName?: string | null
|
||||
gender?: string | null
|
||||
/** Date of birth, "yyyy-MM-dd". Backend property is BoD. */
|
||||
boD?: string | null
|
||||
primaryPH?: string | null
|
||||
secondaryPH?: string | null
|
||||
email?: string | null
|
||||
nic?: string | null
|
||||
address?: string | null
|
||||
fkUserId?: string
|
||||
isActive: boolean
|
||||
createdAt: string
|
||||
isLocked?: boolean
|
||||
lockReason?: string | null
|
||||
lockedAt?: string | null
|
||||
customerTargets?: CustomerTarget[] | null
|
||||
user?: {
|
||||
userId: string
|
||||
userName?: string | null
|
||||
email?: string | null
|
||||
phoneNumber?: string | null
|
||||
} | null
|
||||
}
|
||||
|
||||
/** A workout/meal plan the member is assigned to. */
|
||||
export interface FollowedPlan {
|
||||
planId: string
|
||||
name?: string | null
|
||||
isFollowing: boolean
|
||||
completedDate?: string | null
|
||||
}
|
||||
|
||||
/** GET client/customers/{id}/full — the "More Info" aggregate. */
|
||||
export interface MemberFullDetail {
|
||||
customer: MemberProfile
|
||||
subscriptions?: SubscriptionSummary[]
|
||||
workoutPlans: FollowedPlan[]
|
||||
mealPlans: FollowedPlan[]
|
||||
}
|
||||
|
||||
/** POST client/customers/{id}/medical (password-gated). */
|
||||
export interface MedicalDetails {
|
||||
bloodGroup?: string | null
|
||||
disabilities?: string | null
|
||||
}
|
||||
|
||||
/**
|
||||
* Row shape of GET client/customers (MemberListRowDto): one row per member.
|
||||
* Group members each get a row pointing at their shared subscription.
|
||||
*/
|
||||
export interface MemberSubscription {
|
||||
subscriptionId: string
|
||||
fkCustomerPlanId: string
|
||||
fkCustomerId: string
|
||||
onboardDate: string
|
||||
/** Frozen at creation, never changes again -- use renewalDate for current status/listing. */
|
||||
expiryDate?: string | null
|
||||
renewalDate?: string | null
|
||||
isActive: boolean
|
||||
createdAt: string
|
||||
customer?: MemberProfile | null
|
||||
isPrimary: boolean
|
||||
/** False when unassigned from the plan but still a member of the gym — re-assignable. */
|
||||
isPlanAssigned?: boolean
|
||||
planName?: string | null
|
||||
/** "None" means this member was created without a plan yet — assign one to make them active. */
|
||||
planScope?: "Individual" | "Group" | "None"
|
||||
memberCount?: number
|
||||
memberLimit?: number
|
||||
invoiceAmount?: number
|
||||
paidAmount?: number
|
||||
balance?: number
|
||||
paymentStatus?: PaymentStatus
|
||||
allowSplitPayments?: boolean
|
||||
fingerprintId?: number | null
|
||||
fingerprintName?: string | null
|
||||
accessProfileId?: number | null
|
||||
accessNumber?: string | null
|
||||
accessProvisionStatus?: "NotConfigured" | "NoDevice" | "Pending" | "Synced" | "Failed"
|
||||
actualAccessState?: "Unknown" | "Enabled" | "Disabled" | "Mixed"
|
||||
desiredAccessState?: "Enabled" | "Disabled"
|
||||
fingerprintCount?: number
|
||||
accessLastError?: string | null
|
||||
isLocked?: boolean
|
||||
lockReason?: string | null
|
||||
/** Face-photo enrolment on the gym's access terminals. */
|
||||
hasFacePhoto?: boolean
|
||||
facePhotoStatus?: "None" | "Pending" | "Synced" | "Failed"
|
||||
facePhotoError?: string | null
|
||||
}
|
||||
|
||||
/** GET client/customers/unlinked-fingerprints — a device enrollment not yet linked to a Customer. */
|
||||
export interface UnlinkedFingerprint {
|
||||
id: string
|
||||
deviceUserId: number
|
||||
deviceEmployeeNo: string
|
||||
deviceReportedName?: string | null
|
||||
deviceUserType?: string | null
|
||||
fingerprintCount: number
|
||||
deviceId: string
|
||||
deviceName?: string | null
|
||||
discoveredAt: string
|
||||
}
|
||||
|
||||
export interface AccessCommandStatus {
|
||||
accessProfileId: number
|
||||
accessNumber: string
|
||||
personType: "Customer" | "Staff"
|
||||
desiredAccessState: "Enabled" | "Disabled"
|
||||
provisionStatus: "NoDevice" | "Pending" | "Synced" | "Failed"
|
||||
actualAccessState: "Unknown" | "Enabled" | "Disabled" | "Mixed"
|
||||
fingerprintCount: number
|
||||
lastError?: string | null
|
||||
lastSyncedAt?: string | null
|
||||
deviceCount: number
|
||||
syncedDeviceCount: number
|
||||
}
|
||||
|
||||
/** GET client/customers/{id} response. */
|
||||
export interface MemberDetail {
|
||||
customer: MemberProfile
|
||||
plans: GymPlan[]
|
||||
subscriptions?: SubscriptionSummary[]
|
||||
}
|
||||
|
||||
export interface MemberInput {
|
||||
fullname: string
|
||||
userName: string
|
||||
email: string
|
||||
mobileNumber: string
|
||||
gender: string
|
||||
/** "yyyy-MM-dd" */
|
||||
birthday: string
|
||||
nic?: string
|
||||
customerPlanId?: string
|
||||
workoutPlanIds?: string[]
|
||||
mealPlanIds?: string[]
|
||||
/** ISO datetime. Only meaningful alongside customerPlanId. */
|
||||
onboardDate?: string
|
||||
/** false bills the plan at Rs. 0.00 and skips the automatic payment link. */
|
||||
chargePayment?: boolean
|
||||
discountType?: "Percentage" | "Flat"
|
||||
discountValue?: number
|
||||
/** Set true only on the resubmit after staff has confirmed linking an existing platform
|
||||
* customer to this gym (see CreateMemberResult.requiresConfirmation). */
|
||||
confirmLink?: boolean
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
export type FacilityType = "Gym" | "Pool" | "Court" | "Class"
|
||||
|
||||
/** Mirrors FacilityDto. */
|
||||
export interface Facility {
|
||||
facilityId: string
|
||||
name: string
|
||||
facilityType: FacilityType
|
||||
description?: string | null
|
||||
defaultCapacity: number
|
||||
isActive: boolean
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export interface FacilityInput {
|
||||
name: string
|
||||
facilityType: FacilityType
|
||||
description?: string
|
||||
defaultCapacity: number
|
||||
}
|
||||
|
||||
/** Mirrors FacilitySessionDto. */
|
||||
export interface FacilitySession {
|
||||
facilitySessionId: string
|
||||
facilityId: string
|
||||
facilityName?: string | null
|
||||
startTime: string
|
||||
endTime: string
|
||||
capacity: number
|
||||
bookedCount: number
|
||||
isCancelled: boolean
|
||||
}
|
||||
|
||||
export interface FacilitySessionInput {
|
||||
facilityId: string
|
||||
startTime: string
|
||||
endTime: string
|
||||
/** Overrides the facility's default capacity for this specific session. */
|
||||
capacity?: number
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/** Item shape returned by GET client/feed (ShowFeedByClient). */
|
||||
export interface FeedPost {
|
||||
feedPostId: string
|
||||
caption?: string | null
|
||||
description?: string | null
|
||||
userId: string
|
||||
docId?: number | null
|
||||
isActive: boolean
|
||||
createdAt: string
|
||||
likeCount: number
|
||||
document?: {
|
||||
fileName?: string | null
|
||||
fileType?: string | null
|
||||
documentUrl?: string | null
|
||||
documentType?: string | null
|
||||
/** Base64 file content. */
|
||||
data?: string | null
|
||||
} | null
|
||||
}
|
||||
|
||||
/** Inner wrapper FeedService puts around the list. */
|
||||
export interface FeedListResult {
|
||||
success: boolean
|
||||
message: string
|
||||
data: FeedPost[]
|
||||
}
|
||||
|
||||
export interface FeedDocumentInput {
|
||||
FileName: string
|
||||
/** Base64 file content (DocumentService reads the `Base64` key). FeedService injects FileType. */
|
||||
Base64: string
|
||||
}
|
||||
|
||||
export interface CreateFeedInput {
|
||||
caption?: string
|
||||
description?: string
|
||||
document: FeedDocumentInput[]
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
export type PlanScope = "Individual" | "Group"
|
||||
export type DurationMode = "Rolling" | "FixedRange"
|
||||
|
||||
/** Mirrors CustomerPlanDto (gym membership plans). */
|
||||
export interface GymPlan {
|
||||
customerPlanId: string
|
||||
fkClientId: string
|
||||
clientName?: string | null
|
||||
planName: string
|
||||
planDescription?: string | null
|
||||
planPrice: number
|
||||
hasOnboardingFee: boolean
|
||||
onboardingFeeAmount: number
|
||||
durationInDays: number
|
||||
planScope: PlanScope
|
||||
memberLimit: number
|
||||
durationMode: DurationMode
|
||||
validFrom?: string | null
|
||||
validTo?: string | null
|
||||
allowSplitPayments: boolean
|
||||
installmentCount: number
|
||||
isActive: boolean
|
||||
createdAt: string
|
||||
/** Places this plan grants access to. Empty = unrestricted (every active device). */
|
||||
placeIds: string[]
|
||||
placeNames: string[]
|
||||
}
|
||||
|
||||
export interface GymPlanInput {
|
||||
planName: string
|
||||
planDescription?: string
|
||||
planPrice: number
|
||||
hasOnboardingFee?: boolean
|
||||
onboardingFeeAmount?: number
|
||||
/** Required for Rolling plans; omit for FixedRange. */
|
||||
durationInDays?: number
|
||||
planScope?: PlanScope
|
||||
/** Required (>= 2) for Group plans. */
|
||||
memberLimit?: number
|
||||
durationMode?: DurationMode
|
||||
/** ISO dates; required for FixedRange plans. */
|
||||
validFrom?: string
|
||||
validTo?: string
|
||||
allowSplitPayments?: boolean
|
||||
/** Number of mostly-equal installments when split payments are enabled. */
|
||||
installmentCount?: number
|
||||
/** Replaces the plan's place set when provided; omit to leave unchanged, [] to clear. */
|
||||
placeIds?: string[]
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/** Mirrors GymRoleDto. */
|
||||
export interface GymRole {
|
||||
gymRoleId: string
|
||||
name: string
|
||||
isActive: boolean
|
||||
navKeys: string[]
|
||||
}
|
||||
|
||||
export interface CreateGymRoleInput {
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface UpdateGymRoleInput {
|
||||
name?: string
|
||||
isActive?: boolean
|
||||
}
|
||||
|
||||
export interface SetRolePermissionsInput {
|
||||
navKeys: string[]
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import type { MemberProfile } from "@/lib/models/customer"
|
||||
import type { GymPlan } from "@/lib/models/gym-plan"
|
||||
|
||||
/** CustomerInvoice entity from GET client/member-invoices (member billing). */
|
||||
export interface MemberInvoice {
|
||||
customerInvoiceId: string
|
||||
fkCustomerSubscriptionId: string
|
||||
fkClientId: string
|
||||
amount: number
|
||||
/** The portion of `amount` carried forward from a prior cycle's unpaid balance (renewal
|
||||
* approval flow), rather than this cycle's own charge. 0/undefined when there's none --
|
||||
* only populated by the renewal-approvals list and the single-invoice detail endpoint. */
|
||||
carriedForwardAmount?: number
|
||||
status: string
|
||||
/** Approval gate for the renewal-approval workflow: false means this invoice was
|
||||
* auto-generated by the daily renewal sweep and is awaiting approve/change-plan/reject.
|
||||
* Independent of `status`. */
|
||||
isActive: boolean
|
||||
paidAmount?: number
|
||||
balance?: number
|
||||
customerInvoiceDate: string
|
||||
/** Flattened from CustomerSubscription.TenantCustomerLink -- use this for display, not
|
||||
* customerSubscription.customer, which the backend never actually populates. */
|
||||
customerName?: string | null
|
||||
customerPhone?: string | null
|
||||
/** TenantCustomerLink id -- used for "view previous invoices" and latest-per-customer grouping. */
|
||||
fkCustomerId?: string | null
|
||||
customerSubscription?: {
|
||||
subscriptionId: string
|
||||
onboardDate: string
|
||||
expiryDate?: string | null
|
||||
renewalDate?: string | null
|
||||
customer?: MemberProfile | null
|
||||
customerPlan?: GymPlan | null
|
||||
} | null
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
export type LedgerEntryType = "Income" | "Expense"
|
||||
|
||||
/** Mirrors LedgerEntryDto — one row in the P&L breakdown (derived or manual). */
|
||||
export interface LedgerEntry {
|
||||
id?: string | null
|
||||
entryType: LedgerEntryType
|
||||
category: string
|
||||
description?: string | null
|
||||
amount: number
|
||||
entryDate: string
|
||||
isManual: boolean
|
||||
}
|
||||
|
||||
export interface CategoryTotal {
|
||||
category: string
|
||||
amount: number
|
||||
}
|
||||
|
||||
/** GET client/reports/profit-loss. */
|
||||
export interface ProfitLoss {
|
||||
from: string
|
||||
to: string
|
||||
totalIncome: number
|
||||
totalExpense: number
|
||||
netProfit: number
|
||||
incomeByCategory: CategoryTotal[]
|
||||
expenseByCategory: CategoryTotal[]
|
||||
entries: LedgerEntry[]
|
||||
}
|
||||
|
||||
export interface AddLedgerEntryInput {
|
||||
entryType: LedgerEntryType
|
||||
category: string
|
||||
description?: string
|
||||
amount: number
|
||||
entryDate?: string
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface LockPolicy {
|
||||
lockAfterDaysOverdue: number | null
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
export interface NavItemApi {
|
||||
key: string
|
||||
label: string
|
||||
shortLabel?: string
|
||||
href: string
|
||||
icon: string
|
||||
}
|
||||
|
||||
export interface NavResponse {
|
||||
isOwner: boolean
|
||||
items: NavItemApi[]
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/** GET client/pay/{token} — public pay-by-link invoice summary. */
|
||||
export interface PayByLinkInvoice {
|
||||
memberName?: string | null
|
||||
gymName?: string | null
|
||||
planName?: string | null
|
||||
amount: number
|
||||
balance: number
|
||||
currency: string
|
||||
}
|
||||
|
||||
export interface PayByLinkResult {
|
||||
success: boolean
|
||||
invoiceId: string
|
||||
paidAmount: number
|
||||
balance: number
|
||||
status: string
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/** Mirrors PlaceDto. A physical access zone (e.g. "Pool Area", "Indoor Gym") with one
|
||||
* fingerprint/face terminal for now. */
|
||||
export interface Place {
|
||||
placeId: string
|
||||
name: string
|
||||
description?: string | null
|
||||
isActive: boolean
|
||||
createdAt: string
|
||||
deviceId?: string | null
|
||||
deviceName?: string | null
|
||||
}
|
||||
|
||||
export interface PlaceInput {
|
||||
name: string
|
||||
description?: string
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/** Shared shapes for workout & meal plans (mirrors WorkoutPlanDto / MealPlanDto). */
|
||||
|
||||
export interface AssignedCustomer {
|
||||
/** customerWorkoutId or customerMealId — the id UnassignWorkout/UnassignMeal expects. */
|
||||
assignmentId: string
|
||||
customerId: string
|
||||
customerName?: string | null
|
||||
isActive: boolean
|
||||
assignedDate: string
|
||||
}
|
||||
|
||||
export interface TrainingPlan {
|
||||
/** workoutPlanId or mealPlanId, normalized to `planId` at the API layer. */
|
||||
planId: string
|
||||
name: string
|
||||
description?: string | null
|
||||
isTemplate: boolean
|
||||
categoryId: string
|
||||
categoryName?: string | null
|
||||
clientId?: string | null
|
||||
clientName?: string | null
|
||||
isActive: boolean
|
||||
createDate: string
|
||||
assignedCustomers: AssignedCustomer[]
|
||||
}
|
||||
|
||||
export interface PlanCategory {
|
||||
categoryId: string
|
||||
categoryName: string
|
||||
description?: string | null
|
||||
}
|
||||
|
||||
export interface PlanItemRow {
|
||||
/** Day label for workouts (e.g. "Monday"); free text for meals ("Breakfast"). */
|
||||
label: string
|
||||
detail: string
|
||||
}
|
||||
|
||||
/** One structured exercise within a workout day (mirrors WorkoutExerciseDto). */
|
||||
export interface WorkoutExerciseView {
|
||||
id: string
|
||||
name: string
|
||||
muscleGroup?: string | null
|
||||
sets?: number | null
|
||||
reps?: string | null
|
||||
description?: string | null
|
||||
breathIn?: string | null
|
||||
breathOut?: string | null
|
||||
videoUrl?: string | null
|
||||
}
|
||||
|
||||
/** A saved plan item, normalized for the detail panel (one card per item). */
|
||||
export interface PlanItemView {
|
||||
id: string
|
||||
/** exerciseDay (workout) or meal name (meal). */
|
||||
title: string
|
||||
/** exercises list (workout) or description (meal). */
|
||||
detail: string
|
||||
/** Structured exercises (workout only) — empty for meal items. */
|
||||
exercises: WorkoutExerciseView[]
|
||||
}
|
||||
|
||||
export interface CreatePlanInput {
|
||||
name: string
|
||||
description?: string
|
||||
categoryId: string
|
||||
isTemplate?: boolean
|
||||
customerIds?: string[]
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/** One equipment type + how many the gym has (e.g. Treadmill × 10). */
|
||||
export interface GymEquipment {
|
||||
name: string
|
||||
count: number
|
||||
}
|
||||
|
||||
/** Gym business record attached to a client (from GET client/me). */
|
||||
export interface ClientBusiness {
|
||||
bid: string
|
||||
businessName?: string | null
|
||||
address?: string | null
|
||||
city?: string | null
|
||||
description?: string | null
|
||||
businessLogoId?: string | null
|
||||
/** JSON blob: { hasAc, equipment: [{ name, count }] } — parse with parseFacilities(). */
|
||||
facilities?: string | null
|
||||
isActive: boolean
|
||||
}
|
||||
|
||||
/** Mirrors the Client entity returned by GET client/me. */
|
||||
export interface ClientProfile {
|
||||
clientId: string
|
||||
clientName?: string | null
|
||||
clientPhone?: string | null
|
||||
clientEmail?: string | null
|
||||
clientBusinesses?: ClientBusiness[] | null
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/** Mirrors StaffDto. */
|
||||
export interface StaffMember {
|
||||
staffId: string
|
||||
userId: string
|
||||
userName?: string | null
|
||||
email?: string | null
|
||||
phoneNumber?: string | null
|
||||
clientId: string
|
||||
clientName?: string | null
|
||||
gymRoleId?: string | null
|
||||
gymRoleName?: string | null
|
||||
staffPosition?: string | null
|
||||
department?: string | null
|
||||
hireDate: string
|
||||
isActive: boolean
|
||||
createdAt: string
|
||||
fingerprintId?: number | null
|
||||
fingerprintName?: string | null
|
||||
accessProfileId?: number | null
|
||||
accessNumber?: string | null
|
||||
accessProvisionStatus?: "NotConfigured" | "NoDevice" | "Pending" | "Synced" | "Failed"
|
||||
actualAccessState?: "Unknown" | "Enabled" | "Disabled" | "Mixed"
|
||||
desiredAccessState?: "Enabled" | "Disabled"
|
||||
fingerprintCount?: number
|
||||
accessLastError?: string | null
|
||||
}
|
||||
|
||||
export interface StaffInput {
|
||||
userName: string
|
||||
email: string
|
||||
phoneNumber: string
|
||||
staffPosition?: string
|
||||
department?: string
|
||||
/** "yyyy-MM-dd" — create only. */
|
||||
hireDate?: string
|
||||
/** Create only. Left unset, the new staff member has no dashboard access until a role
|
||||
* is assigned later via Roles & Permissions. */
|
||||
gymRoleId?: string
|
||||
}
|
||||
|
||||
export type StaffRateType = "Monthly" | "Hourly"
|
||||
|
||||
/** Mirrors StaffPayRateDto. */
|
||||
export interface StaffPayRate {
|
||||
id: string
|
||||
rateType: StaffRateType
|
||||
amount: number
|
||||
effectiveFrom: string
|
||||
}
|
||||
|
||||
export interface UpsertStaffPayRateInput {
|
||||
rateType: StaffRateType
|
||||
amount: number
|
||||
}
|
||||
|
||||
/** Mirrors StaffPaymentDto. */
|
||||
export interface StaffPayment {
|
||||
id: string
|
||||
amount: number
|
||||
note?: string | null
|
||||
paidAt: string
|
||||
}
|
||||
|
||||
export interface RecordStaffPaymentInput {
|
||||
amount: number
|
||||
note?: string
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/** Mirrors ClientStatSummaryDto (camelCased by the backend). */
|
||||
export interface ClientStatSummary {
|
||||
totalCustomerCount: number
|
||||
thisMonthCustomerCount: number
|
||||
lastMonthCustomerCount: number
|
||||
customerGrowthPercentage: number
|
||||
/** These four are zeroed server-side for non-owner callers — revenue is owner-only. */
|
||||
thisMonthRevenue: number
|
||||
lastMonthRevenue: number
|
||||
revenueGrowthPercentage: number
|
||||
totalRevenue: number
|
||||
overdueCount: number
|
||||
expiringWithin7DaysCount: number
|
||||
/** Always "today" (UTC date), independent of any reporting-period filter elsewhere on this
|
||||
* object -- visible to every staff member, not owner-gated like revenue above. */
|
||||
todayPaymentsCount: number
|
||||
todayPaymentsTotal: number
|
||||
/** Muscle Beast-only pilot -- gated server-side, so the UI never needs to know any gym's
|
||||
* ClientId to decide whether to show the Today's Payments card/filter. */
|
||||
showTodayPayments: boolean
|
||||
}
|
||||
|
||||
/** Mirrors MonthlyRevenueStat. */
|
||||
export interface MonthlyRevenueStat {
|
||||
year: number
|
||||
month: number
|
||||
monthLabel: string
|
||||
/** Zeroed server-side for non-owner callers — revenue is owner-only. */
|
||||
revenue: number
|
||||
newMemberCount: number
|
||||
}
|
||||
|
||||
interface CustomerRef {
|
||||
cusId: string
|
||||
cusName: string
|
||||
}
|
||||
|
||||
/** PaymentTransaction entity rows from GET client/stats/recent-payments. */
|
||||
export interface RecentPayment {
|
||||
attemptId: string
|
||||
payDate: string | null
|
||||
payStatus: string
|
||||
payAmount: number
|
||||
payNote?: string | null
|
||||
customer?: CustomerRef | null
|
||||
}
|
||||
|
||||
/** Attendance entity rows from GET client/stats/recent-attendance. */
|
||||
export interface RecentAttendance {
|
||||
attendanceId: string
|
||||
eventTime: string
|
||||
eventType: string
|
||||
deviceUserId: number
|
||||
customer?: CustomerRef | null
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
export type PaymentStatus = "Pending" | "Partial" | "Paid"
|
||||
|
||||
export interface SubscriptionMemberInfo {
|
||||
subscriptionMemberId: string
|
||||
customerId: string
|
||||
cusName?: string | null
|
||||
email?: string | null
|
||||
primaryPH?: string | null
|
||||
isPrimary: boolean
|
||||
joinedAt: string
|
||||
isActive: boolean
|
||||
/** False when unassigned from the plan but still a member of the gym. */
|
||||
isPlanAssigned?: boolean
|
||||
}
|
||||
|
||||
export interface SubscriptionInstallment {
|
||||
subscriptionInstallmentId: string
|
||||
installmentNo: number
|
||||
dueDate: string
|
||||
amount: number
|
||||
paidAmount: number
|
||||
status: PaymentStatus
|
||||
}
|
||||
|
||||
export interface AutoChargeCard {
|
||||
cardId: string
|
||||
cardLast4?: string | null
|
||||
cardType?: string | null
|
||||
}
|
||||
|
||||
export interface MemberCard {
|
||||
jobId: string
|
||||
cardId?: string | null
|
||||
cardNumber?: string | null
|
||||
cardType?: string | null
|
||||
nickName?: string | null
|
||||
isPrimary: boolean
|
||||
consentGiven: boolean
|
||||
}
|
||||
|
||||
/** GET client/subscriptions/{id}. */
|
||||
export interface SubscriptionSummary {
|
||||
subscriptionId: string
|
||||
customerPlanId: string
|
||||
planName?: string | null
|
||||
planScope: "Individual" | "Group"
|
||||
memberLimit: number
|
||||
durationMode: "Rolling" | "FixedRange"
|
||||
allowSplitPayments: boolean
|
||||
planPrice: number
|
||||
onboardDate: string
|
||||
/** Frozen at creation, never changes again -- see renewalDate for the current cycle end. */
|
||||
expiryDate?: string | null
|
||||
renewalDate?: string | null
|
||||
isActive: boolean
|
||||
customerInvoiceId?: string | null
|
||||
invoiceAmount: number
|
||||
paidAmount: number
|
||||
balance: number
|
||||
paymentStatus: PaymentStatus
|
||||
members: SubscriptionMemberInfo[]
|
||||
installments: SubscriptionInstallment[]
|
||||
autoChargeEnabled: boolean
|
||||
autoChargeCard?: AutoChargeCard | null
|
||||
}
|
||||
|
||||
export interface SubscriptionPayment {
|
||||
attemptId: string
|
||||
payDate?: string | null
|
||||
payStatus: string
|
||||
payAmount: number
|
||||
payNote?: string | null
|
||||
refType?: string | null
|
||||
paymentMethod?: "Cash" | "Bank" | null
|
||||
bankRefNo?: string | null
|
||||
isApproved?: boolean | null
|
||||
}
|
||||
|
||||
/** GET client/subscriptions/{id}/payments. */
|
||||
export interface SubscriptionPaymentsResult {
|
||||
invoiceId?: string | null
|
||||
invoiceAmount: number
|
||||
paidAmount: number
|
||||
balance: number
|
||||
status: PaymentStatus
|
||||
installments: SubscriptionInstallment[]
|
||||
payments: SubscriptionPayment[]
|
||||
}
|
||||
|
||||
export interface RecordPaymentInput {
|
||||
amount: number
|
||||
note?: string
|
||||
paymentMethod: "Cash" | "Bank"
|
||||
/** Reference/slip number — only meaningful (and sent) when paymentMethod is "Bank". */
|
||||
bankRefNo?: string
|
||||
}
|
||||
|
||||
export type ExtraFeeFrequency = "OneTime" | "Recurring"
|
||||
|
||||
/** Mirrors CustomerSubItemDto — an optional extra fee on a subscription (e.g. coaching fee). */
|
||||
export interface SubscriptionExtraFee {
|
||||
customerSubItemId: string
|
||||
subItemName?: string | null
|
||||
subItemDescription?: string | null
|
||||
subItemPrice: number
|
||||
frequency: ExtraFeeFrequency
|
||||
isActive: boolean
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export interface AddExtraFeeInput {
|
||||
name: string
|
||||
description?: string
|
||||
amount: number
|
||||
frequency: ExtraFeeFrequency
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/** TicketStatusEnum: 1=Pending, 2=Open, 3=Closed (serialized as int). */
|
||||
export type TicketStatus = 1 | 2 | 3
|
||||
|
||||
export const TICKET_STATUS_LABEL: Record<TicketStatus, string> = {
|
||||
1: "Pending",
|
||||
2: "Open",
|
||||
3: "Closed",
|
||||
}
|
||||
|
||||
/** Mirrors TicketDto (list rows). */
|
||||
export interface Ticket {
|
||||
ticketId: string
|
||||
mainReason: string
|
||||
createDate: string
|
||||
userId: string
|
||||
userName?: string | null
|
||||
userEmail?: string | null
|
||||
adminId?: string | null
|
||||
adminName?: string | null
|
||||
ticketCategoryId?: string | null
|
||||
ticketCategoryName?: string | null
|
||||
userRating?: number | null
|
||||
ticketStatus: TicketStatus
|
||||
}
|
||||
|
||||
/** Mirrors TicketMessageDto. */
|
||||
export interface TicketMessage {
|
||||
messageId: string
|
||||
isSentByAdmin: boolean
|
||||
message: string
|
||||
attachmentURL?: string | null
|
||||
createDate: string
|
||||
}
|
||||
|
||||
/** Mirrors TicketDetailDto. */
|
||||
export interface TicketDetail extends Ticket {
|
||||
userPhoneNumber?: string | null
|
||||
userFeedback?: string | null
|
||||
feedbackAt?: string | null
|
||||
messages: TicketMessage[]
|
||||
}
|
||||
|
||||
export interface TicketCategory {
|
||||
ticketCategoryId: string
|
||||
ticketCategoryName: string
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/** Mirrors TimeBankCardDto. */
|
||||
export interface TimeBankCard {
|
||||
timeBankCardId: string
|
||||
rfidTagId: string
|
||||
customerId?: string | null
|
||||
totalMinutesPurchased: number
|
||||
minutesRemaining: number
|
||||
amountPaid: number
|
||||
purchasedAt: string
|
||||
expiresAt?: string | null
|
||||
isActive: boolean
|
||||
|
||||
/** True right after a tap-in swipe (session open, clock running). */
|
||||
sessionOpen: boolean
|
||||
sessionStartedAt?: string | null
|
||||
sessionFacilityName?: string | null
|
||||
|
||||
/** Only populated in the response to a tap-out swipe. */
|
||||
minutesDeductedThisSwipe?: number | null
|
||||
}
|
||||
|
||||
export interface IssueTimeBankCardInput {
|
||||
rfidTagId: string
|
||||
totalMinutesPurchased: number
|
||||
amountPaid: number
|
||||
/** Ties the card to a member's own account; omit for a walk-in-only card. */
|
||||
customerId?: string
|
||||
expiresAt?: string
|
||||
}
|
||||
|
||||
export interface SwipeTimeBankCardInput {
|
||||
rfidTagId: string
|
||||
facilityId: string
|
||||
}
|
||||
Reference in New Issue
Block a user