Files
FrenoCorp/Lendair/Models/CommunityEvent.swift
Michael Freno 57a460761a FRE-4665: Implement Phase 3 AI training plans and premium features
- Models: TrainingPlan, Race, FamilyPlan, BeginnerMode, CommunityEvent
- Services: 5 service layers with protocol-based architecture
- ViewModels: 5 view models with @MainActor ObservableObject pattern
- Views: 10 SwiftUI views for all Phase 3 features
- Updated README with full Phase 3 documentation

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-05-03 15:21:01 -04:00

244 lines
5.9 KiB
Swift

import Foundation
import SwiftUI
// MARK: - Community Event
struct CommunityEvent: Identifiable, Equatable, Codable {
let id: String
let title: String
let description: String
let eventType: EventType
let location: String
let latitude: Double
let longitude: Double
let startDate: Date
let endDate: Date
let distanceKm: Double?
let organizerId: String
let organizerName: String
let maxParticipants: Int?
let participantCount: Int
let imageUrl: String?
let difficulty: Difficulty?
var rsvpStatus: RSVPStatus
let createdAt: Date
enum CodingKeys: String, CodingKey {
case id, title, description, eventType, location, latitude, longitude, startDate, endDate, distanceKm, organizerId, organizerName, maxParticipants, participantCount, imageUrl, difficulty, rsvpStatus, createdAt
}
init(
id: String,
title: String,
description: String,
eventType: EventType,
location: String,
latitude: Double,
longitude: Double,
startDate: Date,
endDate: Date,
distanceKm: Double?,
organizerId: String,
organizerName: String,
maxParticipants: Int?,
participantCount: Int,
imageUrl: String?,
difficulty: Difficulty?,
rsvpStatus: RSVPStatus,
createdAt: Date
) {
self.id = id
self.title = title
self.description = description
self.eventType = eventType
self.location = location
self.latitude = latitude
self.longitude = longitude
self.startDate = startDate
self.endDate = endDate
self.distanceKm = distanceKm
self.organizerId = organizerId
self.organizerName = organizerName
self.maxParticipants = maxParticipants
self.participantCount = participantCount
self.imageUrl = imageUrl
self.difficulty = difficulty
self.rsvpStatus = rsvpStatus
self.createdAt = createdAt
}
static func == (lhs: CommunityEvent, rhs: CommunityEvent) -> Bool {
lhs.id == rhs.id && lhs.rsvpStatus == rhs.rsvpStatus
}
var isUpcoming: Bool {
startDate > Date()
}
var isOngoing: Bool {
Date() >= startDate && Date() <= endDate
}
var isPast: Bool {
endDate < Date()
}
var availableSpots: Int? {
guard let max = maxParticipants else { return nil }
return max - participantCount
}
}
// MARK: - Event Type
enum EventType: String, CaseIterable, Codable {
case groupRun
case race
case workshop
case socialGather
case charityEvent
case trainingCamp
var displayName: String {
switch self {
case .groupRun: return "Group Run"
case .race: return "Race"
case .workshop: return "Workshop"
case .socialGather: return "Social"
case .charityEvent: return "Charity"
case .trainingCamp: return "Training Camp"
}
}
var icon: String {
switch self {
case .groupRun: return "person.3.fill"
case .race: return "flag.fill"
case .workshop: return "lightbulb.fill"
case .socialGather: return "cup.and.saucer.fill"
case .charityEvent: return "heart.fill"
case .trainingCamp: return "figure.run"
}
}
var color: Color {
switch self {
case .groupRun: return .blue
case .race: return .orange
case .workshop: return .purple
case .socialGather: return .green
case .charityEvent: return .red
case .trainingCamp: return .indigo
}
}
}
// MARK: - RSVP Status
enum RSVPStatus: String, CaseIterable, Codable {
case going
case maybe
case notGoing
case pending
}
// MARK: - Event Participant
struct EventParticipant: Identifiable, Codable {
let id: String
let name: String
let avatarUrl: String?
let rsvpStatus: RSVPStatus
let joinedAt: Date
}
// MARK: - Create Event Request
struct CreateEventRequest: Encodable {
let title: String
let description: String
let eventType: EventType
let location: String
let latitude: Double
let longitude: Double
let startDate: Date
let endDate: Date
let distanceKm: Double?
let maxParticipants: Int?
let difficulty: Difficulty?
}
// MARK: - Update Event Request
struct UpdateEventRequest: Encodable {
var title: String?
var description: String?
var eventType: EventType?
var location: String?
var latitude: Double?
var longitude: Double?
var startDate: Date?
var endDate: Date?
var distanceKm: Double?
var maxParticipants: Int?
}
// MARK: - Event Filter
struct EventFilter: Encodable {
var eventType: EventType?
var startDate: Date?
var endDate: Date?
var location: String?
var radiusKm: Double?
var rsvpStatus: RSVPStatus?
var limit: Int
var offset: Int
init(
eventType: EventType? = nil,
startDate: Date? = nil,
endDate: Date? = nil,
location: String? = nil,
radiusKm: Double? = nil,
rsvpStatus: RSVPStatus? = nil,
limit: Int = 20,
offset: Int = 0
) {
self.eventType = eventType
self.startDate = startDate
self.endDate = endDate
self.location = location
self.radiusKm = radiusKm
self.rsvpStatus = rsvpStatus
self.limit = limit
self.offset = offset
}
}
// MARK: - API Response Types
struct EventListResponse: Decodable {
let events: [CommunityEvent]
let hasMore: Bool
}
struct EventDetailResponse: Decodable {
let event: CommunityEvent
let participants: [EventParticipant]
}
struct CreateEventResponse: Decodable {
let event: CommunityEvent
}
struct UpdateEventResponse: Decodable {
let event: CommunityEvent
}
struct RSVPResponse: Decodable {
let success: Bool
let eventId: String
let status: RSVPStatus
}