# Project export: Persona

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 12.0
- Tagline: your professional travel journeys
- Devpost: https://devpost.com/software/personaverse-e32gbt
- GitHub: https://github.com/bingbingm2/TravelVerse
- Demo: https://github.com/bingbingm2/TravelPro
- Video: https://www.youtube.com/embed/CNfj07-8h2U?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — Bingbing Ma (4 commits)

## Devpost submission (written by the team)

### Inspiration

We wanted to reimagine travel as something more than just sightseeing as a way to explore who you could become. Many professionals dream about living or working abroad, but it’s hard to visualize what that future might actually look like. We aim to turn travel into a personalized, professional, and emotional experience where users can explore cities through the lens of their career identity and aspirations.

### What it does

PersonaVerse is an AI-driven travel and lifestyle platform where users input their professional persona (e.g., “UI/UX Designer,” “Software Engineer,” “Actor”) and a city they want to visit. The system then: Fetches persona-relevant data such as events, companies, conferences, and venues in the chosen city, including start times, locations, and contact information. Generates an AI itinerary that maximizes persona relevance, clusters activities by neighborhood, aligns chronologically, and syncs with the user’s Calendar. Stores user trip data (photos, timestamps, emotional captions, and geolocations) in Firebase for further personalization. Transforms memories into storytelling using OpenAI’s Image and Text APIs, the app creates an AI-generated narrative slideshow that visualizes the traveler’s experiences and emotions. The result is a personalized, meaningful, and imaginative travel journey where users see themselves in it.

### How we built it

For our prototype stage, we used Creao to rapidly visualize the user flow and system logic. We integrated the following components into our technical stack: iOS App built to allow travel guides or companions to record travelers’ experiences, emotions, and locations throughout the itinerary. Live web scraping data using Bright Data for persona-based datasets to simulate the itinerary and API fetching Firebase for storing user trip data, itinerary details, and photo metadata. OpenAI APIs for generating narratives, captions, and slideshow visuals. Google MCP Servers to validate scheduling data and calendar syncs. This multi-layered system bridges data, AI, and design to create a holistic travel storytelling experience.

### Challenges we ran into

Creao platform constraints — limited space and restricted code editing made implementing multi-layer logic difficult, so we had to modularize logic externally. Web scraping integration

### Accomplishments we're proud of

Established a solid architecture for future scalability and real-world API connections. Designed an iOS app to record the emotional and experiential dimensions of travel. Fully integrated Firebase, OpenAI for live data handling and AI narrative generation.

### What we learned

The importance of modular design when prototyping in limited or “black box” platforms. That a product’s value lies not only in technical completion but also in how it makes users feel and imagine their potential futures.

### What's next

Verse Expand the calendar sync and recommendation engine to support dynamic scheduling and rescheduling. Enhance the narrative slideshow into a full AI-generated travel journal with video and voice narration. Scale to a web and mobile unified platform with richer UI and community features where users can share their “future-self journeys.”

## README (from the GitHub repository)

# TravelVerse

An iOS travel planning app with itinerary management, photo uploads, and sentiment tracking. Built for a hackathon project.

## Features

### ✈️ Core Features
- **Trip Itinerary Display**
  - View travel itineraries generated via web scraping
  - Real-time location info and recommendations
  - Day-by-day detailed planning

- **Photo Upload with Location Tracking**
  - Upload photos during travel
  - Automatic GPS location recording
  - Photo-to-place association
  - Camera and album support

- **Sentiment Tagging**
  - Tag photos with emotions (Happy, Excited, Peaceful, etc.)
  - Multiple sentiment selection
  - Custom emotion input

- **Data Sharing**
  - Photos and location stored in Firestore
  - Accessible via web portal for team members
  - Used for travel video generation

## Project Structure

```
TravelVerse/
├── Models/
│   ├── Trip.swift
│   ├── Photo.swift
│   └── User.swift
├── ViewModels/
│   ├── TripViewModel.swift
│   └── PhotoViewModel.swift
├── Views/
│   ├── HomeView.swift
│   ├── ItineraryView.swift
│   └── UploadPhotoView.swift
├── Services/
│   ├── FirebaseService.swift
│   └── LocationService.swift
└── TravelVerseApp.swift
```

## Tech Stack

- **Language**: Swift
- **Framework**: SwiftUI
- **Backend**: Firebase (Firestore)
- **Architecture**: MVVM
- **Location**: CoreLocation

## Firestore Data Structure

**Trip Collection** (`trips/{tripId}`):
```json
{
  "userId": "string",
  "cityName": "string",
  "startDate": "timestamp",
  "endDate": "timestamp",
  "itinerary": [
    {
      "day": 1,
      "date": "timestamp",
      "places": [
        {
          "name": "string",
          "address": "string",
          "latitude": "double",
          "longitude": "double",
          "category": "string",
          "description": "string",
          "startTime": "string",
          "endTime": "string",
          "realTimeInfo": "string"  //web scraping data
        }
      ],
      "notes": "string"
    }
  ],
  "createdAt": "timestamp",
  "updatedAt": "timestamp"
}
```

**Photo Collection** (`photos/{photoId}`):
```json
{
  "userId": "string",
  "tripId": "string",
  "placeId": "string",
  "placeName": "string",
  "imageUrl": "string",  // Firebase Storage URL
  "caption": "string",
  "sentiment": ["string"],  // Emotion tags
  "latitude": "double",
  "longitude": "double",
  "timestamp": "timestamp",
  "cityName": "string"
}
```

## Team Integration

### For Web Scraping Team:
- Write itinerary data to Firestore `trips` collection
- Update `realTimeInfo` field with live information

### For Web Portal Team:
- Read `photos` collection from Firestore
- Use `latitude` and `longitude` to display photos on map
- Generate timeline based on `timestamp`
- Download photos via `imageUrl` for video generation

## Future Improvements

- User authentication with Firebase Auth
- Offline support with Firestore persistence
- Map view with route display
- Social media sharing
- Push notifications for itinerary updates
- Multi-language support

## License

Hackathon demo project.


## Detected evidence (automated analysis)

Indexed codebase: 20 recognized source files, 98 KB.
- Swift (language) — detected in the code
- Firebase (technology) — claimed on Devpost, not found in the code
- JavaScript (language) — claimed on Devpost, not found in the code
- Node.js (technology) — claimed on Devpost, not found in the code
- OpenAI (technology) — claimed on Devpost, not found in the code
- Python (language) — claimed on Devpost, not found in the code
- TypeScript (language) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (30 of 30)

```
.gitignore
CHECKLIST.md
FIREBASE_SETUP.md
Info.plist.example
PROJECT_SUMMARY.md
README.md
SETUP_GUIDE_CN.md
TravelVerse.xcodeproj/project.pbxproj
TravelVerse.xcodeproj/project.xcworkspace/contents.xcworkspacedata
TravelVerse.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
TravelVerse.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
TravelVerse.xcodeproj/xcuserdata/bingbingma.xcuserdatad/xcschemes/xcschememanagement.plist
TravelVerse/Assets.xcassets/AccentColor.colorset/Contents.json
TravelVerse/Assets.xcassets/AppIcon.appiconset/Contents.json
TravelVerse/Assets.xcassets/Contents.json
TravelVerse/Helpers/DateExtensions.swift
TravelVerse/Models/Photo.swift
TravelVerse/Models/Trip.swift
TravelVerse/Models/User.swift
TravelVerse/Services/FirebaseService.swift
TravelVerse/Services/LocationService.swift
TravelVerse/TravelVerseApp.swift
TravelVerse/ViewModels/PhotoViewModel.swift
TravelVerse/ViewModels/TripViewModel.swift
TravelVerse/Views/HomeView.swift
TravelVerse/Views/ItineraryView.swift
TravelVerse/Views/UploadPhotoView.swift
TravelVerseTests/TravelVerseTests.swift
TravelVerseUITests/TravelVerseUITests.swift
TravelVerseUITests/TravelVerseUITestsLaunchTests.swift
```

### Dependencies

No dependency index available.

### Recent commits (newest first)

- update readme
- update readme
- Initial commit - TravelVerse iOS app
- Initial Commit

## Key source files (fetched from GitHub, selected and truncated for size)

### CHECKLIST.md

```markdown
# TravelVerse 设置检查清单 ✅

## 开始之前
- [ ] 确保安装了最新版Xcode
- [ ] 有Google/Firebase账号
- [ ] 项目已经在Xcode中打开

## 第一步：Firebase SDK
- [ ] 在Xcode中打开 `File` → `Add Package Dependencies...`
- [ ] 添加Firebase SDK: `https://github.com/firebase/firebase-ios-sdk.git`
- [ ] 选择了以下产品：
  - [ ] FirebaseFirestore
  - [ ] FirebaseStorage
  - [ ] FirebaseCore
- [ ] 包已成功添加（没有错误）

## 第二步：Firebase项目
- [ ] 访问 https://console.firebase.google.com/
- [ ] 创建了新项目或选择了现有项目
- [ ] 项目名称：________________
- [ ] 添加了iOS应用
- [ ] Bundle ID已正确填写：________________

## 第三步：配置文件
- [ ] 下载了 `GoogleService-Info.plist`
- [ ] 文件已拖入Xcode项目
- [ ] ✅ 勾选了 "Copy items if needed"
- [ ] ✅ 勾选了正确的target (TravelVerse)
- [ ] 文件在项目导航器中可见

## 第四步：Firestore设置
- [ ] 在Firebase Console创建了Firestore数据库
- [ ] 选择了模式：
  - [ ] 测试模式（开发用）
  - [ ] 生产模式（需要配置规则）
- [ ] 数据库已成功创建

## 第五步：Storage设置
- [ ] 在Firebase Console启用了Storage
- [ ] 选择了测试模式（开发用）
- [ ] Storage已成功启用

## 第六步：Info.plist权限
- [ ] 打开了Info.plist文件
- [ ] 添加了以下三个权限：
  - [ ] NSLocationWhenInUseUsageDescription
  - [ ] NSCameraUsageDescription
  - [ ] NSPhotoLibraryUsageDescription
- [ ] 每个权限都有中文描述

## 第七步：构建项目
- [ ] 按 `Command + B` 构建项目
- [ ] ✅ 构建成功（没有错误）
- [ ] 如有错误，已解决：
  - [ ] Clean Build Folder (`Shift + Command + K`)
  - [ ] 重启Xcode
  - [ ] 重新构建

## 第八步：运行测试
- [ ] 选择了模拟器或真机
- [ ] 按 `Command + R` 运行app
- [ ] App成功启动
- [ ] 看到了 "我的旅行" 主页

## 第九步：功能测试

### 创建旅行
- [ ] 点击右上角 "+" 按钮
- [ ] 点击 "创建示例旅行"
- [ ] 旅行卡片出现在列表中

### 查看行程
- [ ] 点击旅行卡片
- [ ] 看到详细行程信息
- [ ] 可以切换不同天数
- [ ] 地点卡片正常显示

### 上传照片（模拟器）
- [ ] 在行程页面点击相机图标
- [ ] 模拟器设置了位置：
  - Xcode → Debug → Simulate Location → 选择城市
- [ ] 选择了照片（从相册或拍照）
- [ ] 填写了地点名称
- [ ] 看到了位置信息
- [ ] 点击上传按钮
- [ ] ✅ 上传成功

### 验证数据（Firebase Console）
- [ ] 打开Firebase Console
- [ ] 进入Firestore Database
- [ ] 看到 `trips` collection有数据
- [ ] 看到 `photos` collection有数据
- [ ] 进入Storage
- [ ] 看到上传的照片文件

## 第十步：队友集成准备
- [ ] 向队友提供Firebase项目访问权限
- [ ] 分享了数据结构文档（FIREBASE_SETUP.md）
- [ ] 提供了示例数据格式
- [ ] 确认队友可以访问Firestore

## 常见问题排查

### 如果编译失败
- [ ] 检查Firebase包是否正确添加
- [ ] 尝试Clean Build Folder
- [ ] 重启Xcode
- [ ] 检查Swift版本兼容性

### 如果App崩溃
- [ ] 检查GoogleService-Info.plist是否在项目中
- [ ] 检查文件是否在正确的target中
- [ ] 查看Xcode控制台的错误信息

### 如果位置不工作
- [ ] 检查Info.plist权限描述
- [ ] 模拟器：设置了模拟位置
- [ ] 真机：检查系统设置中的位置权限

### 如果上传失败
- [ ] 检查网络连接
- [ ] 检查Firebase Storage是否启用
- [ ] 检查Storage规则是否允许写入
- [ ] 查看错误消息

## Hackathon演示准备
- [ ] 创建了几个示例旅行
- [ ] 准备了演示用的照片
- [ ] 测试了完整流程（至少3次）
- [ ] 网络连接稳定
- [ ] 设备充满电
- [ ] 备份设备（带第二台设备）

## 文档清单
- [ ] README.md - 完整说明
- [ ] SETUP_GUIDE_CN.md - 快速设置
- [ ] FIREBASE_SETUP.md - Firebase配置
- [ ] PROJECT_SUMMARY.md - 项目总结
- [ ] CHECKLIST.md - 本清单

## 最终检查
- [ ] 所有功能都测试通过
- [ ] Firebase数据正确存储
- [ ] 队友可以访问和读取数据
- [ ] UI显示正常
- [ ] 没有明显bug
- [ ] 准备好演示脚本
- [ ] 团队所有成员都了解各自负责部分

---

## ✅ 完成状态

**设置完成日期**: _______________

**测试人员**: _______________

**准备状态**: 
- [ ] 已完成所有设置
- [ ] 已测试所有功能
- [ ] 准备好Hackathon演示

**备注**:
_______________________________________________
_______________________________________________
_______________________________________________

---

**祝你
[truncated — 14 more characters]
```

### SETUP_GUIDE_CN.md

```markdown
# TravelVerse 快速设置指南 🚀

## 第一步：安装依赖

### 方法1：使用Swift Package Manager（推荐）

1. 打开Xcode，打开你的 `TravelVerse.xcodeproj`
2. 在顶部菜单选择：`File` → `Add Package Dependencies...`
3. 在搜索框输入：
   ```
   https://github.com/firebase/firebase-ios-sdk.git
   ```
4. 版本选择：`Up to Next Major Version` 输入 `10.0.0`
5. 点击 `Add Package`
6. 在产品列表中勾选：
   - ✅ FirebaseFirestore
   - ✅ FirebaseStorage
   - ✅ FirebaseCore
7. 点击 `Add Package`

## 第二步：配置Firebase

### 1. 创建Firebase项目

1. 访问 https://console.firebase.google.com/
2. 点击 `添加项目`
3. 输入项目名称：`TravelVerse` （或任意名称）
4. 可以禁用Google Analytics（不需要）
5. 点击 `创建项目`

### 2. 添加iOS应用

1. 在Firebase项目主页，点击iOS图标
2. **iOS Bundle ID**: 
   - 在Xcode中找到：选择项目 → Target → General → Bundle Identifier
   - 复制这个ID（例如：`com.yourname.TravelVerse`）
   - 粘贴到Firebase的Bundle ID输入框
3. **App昵称**：输入 `TravelVerse`
4. 点击 `注册应用`

### 3. 下载配置文件

1. 点击 `下载 GoogleService-Info.plist`
2. **重要**：将这个文件拖入Xcode项目：
   - 直接拖到项目导航器中的 `TravelVerse` 文件夹（蓝色图标）
   - 确保勾选 ✅ `Copy items if needed`
   - 确保勾选 ✅ `TravelVerse` target
3. 点击 `下一步`，然后 `继续到控制台`

## 第三步：设置Firestore数据库

1. 在Firebase控制台左侧菜单，点击 `Firestore Database`
2. 点击 `创建数据库`
3. 选择 `以测试模式启动`（用于开发）
4. 选择位置：`us-central` 或离你最近的位置
5. 点击 `启用`

## 第四步：设置Firebase Storage

1. 在Firebase控制台左侧菜单，点击 `Storage`
2. 点击 `开始使用`
3. 选择 `以测试模式启动`
4. 使用默认位置
5. 点击 `完成`

## 第五步：配置App权限

### 在Xcode中设置Info.plist

**方法A：使用可视化编辑器**
1. 在Xcode项目导航器中找到 `Info.plist`
2. 右键点击 → `Open As` → `Property List`
3. 点击任意一行，然后点击 `+` 号添加新行
4. 添加以下三个权限：

| Key | Type | Value |
|-----|------|-------|
| Privacy - Location When In Use Usage Description | String | 我们需要您的位置信息来记录照片拍摄地点 |
| Privacy - Camera Usage Description | String | 我们需要访问相机来拍摄旅行照片 |
| Privacy - Photo Library Usage Description | String | 我们需要访问相册来选择照片 |

**方法B：直接编辑源代码**
1. 右键点击 `Info.plist` → `Open As` → `Source Code`
2. 在 `<dict>` 标签内添加：

```xml
<key>NSLocationWhenInUseUsageDescription</key>
<string>我们需要您的位置信息来记录照片拍摄地点</string>
<key>NSCameraUsageDescription</key>
<string>我们需要访问相机来拍摄旅行照片</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>我们需要访问相册来选择照片</string>
```

## 第六步：构建并运行

1. 选择一个模拟器或连接真机
2. 按 `Command + B` 构建项目
3. 按 `Command + R` 运行

## 测试App

### 1. 创建示例旅行
- 打开app
- 点击右上角的 `+` 按钮
- 点击 `创建示例旅行`

### 2. 查看行程
- 点击旅行卡片
- 查看详细行程信息
- 切换不同的天数

### 3. 上传照片（模拟器）
- 在行程页面点击右上角的相机图标
- 点击 `从相册选择`
- 选择一张照片
- 填写地点名称
- **设置模拟器位置**：
  - Xcode菜单 → `Debug` → `Simulate Location` → 选择一个城市
- 点击上传

## 常见问题解决

### ❌ 编译错误：Cannot find 'FirebaseCore' in scope

**解决方法**：
1. 确认Firebase包已经添加
2. Clean Build Folder：`Shift + Command + K`
3. 关闭Xcode并重新打开
4. 重新构建项目

### ❌ App崩溃：Firebase app not configured

**解决方法**：
1. 检查 `GoogleService-Info.plist` 是否在项目中
2. 确认文件在正确的target中（查看File Inspector）
3. Clean并重新构建

### ❌ 位置服务不工作

**解决方法（模拟器）**：
1. Xcode菜单 → `Debug` → `Simulate Location` → 选择位置
2. 或在模拟器中：`Features` → `Location` → `Custom Location`

**解决方法（真机）**：
1. 设置 → 隐私与安全性 → 定位服务 → TravelVerse → 使用App期间

### ❌ 上传照片失败

**解决方法**：
1. 检查Firebase Storage是否已启用
2. 检查网络连接
3. 在Firebase Console查看Storage规则是否允许写入

## Firestore数据结构（给队友）

如果你的队友
[truncated — 1169 more characters]
```

### TravelVerse/TravelVerseApp.swift

```swift
//
//  TravelVerseApp.swift
//  TravelVerse
//
//  Created by Bingbing Ma on 10/25/25.
//

import SwiftUI
import FirebaseCore

@main
struct TravelVerseApp: App {
    
    // 初始化Firebase
    init() {
        FirebaseApp.configure()
    }
    
    var body: some Scene {
        WindowGroup {
            HomeView()
        }
    }
}

```

### TravelVerseUITests/TravelVerseUITestsLaunchTests.swift

```swift
//
//  TravelVerseUITestsLaunchTests.swift
//  TravelVerseUITests
//
//  Created by Bingbing Ma on 10/25/25.
//

import XCTest

final class TravelVerseUITestsLaunchTests: XCTestCase {

    override class var runsForEachTargetApplicationUIConfiguration: Bool {
        true
    }

    override func setUpWithError() throws {
        continueAfterFailure = false
    }

    @MainActor
    func testLaunch() throws {
        let app = XCUIApplication()
        app.launch()

        // Insert steps here to perform after app launch but before taking a screenshot,
        // such as logging into a test account or navigating somewhere in the app

        let attachment = XCTAttachment(screenshot: app.screenshot())
        attachment.name = "Launch Screen"
        attachment.lifetime = .keepAlways
        add(attachment)
    }
}

```

### TravelVerseTests/TravelVerseTests.swift

```swift
//
//  TravelVerseTests.swift
//  TravelVerseTests
//
//  Created by Bingbing Ma on 10/25/25.
//

import XCTest

final class TravelVerseTests: XCTestCase {

    override func setUpWithError() throws {
        // Put setup code here. This method is called before the invocation of each test method in the class.
    }

    override func tearDownWithError() throws {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
    }

    func testExample() throws {
        // This is an example of a functional test case.
        // Use XCTAssert and related functions to verify your tests produce the correct results.
        // Any test you write for XCTest can be annotated as throws and async.
        // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
        // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
    }

    func testPerformanceExample() throws {
        // This is an example of a performance test case.
        measure {
            // Put the code you want to measure the time of here.
        }
    }

}

```

### TravelVerseUITests/TravelVerseUITests.swift

```swift
//
//  TravelVerseUITests.swift
//  TravelVerseUITests
//
//  Created by Bingbing Ma on 10/25/25.
//

import XCTest

final class TravelVerseUITests: XCTestCase {

    override func setUpWithError() throws {
        // Put setup code here. This method is called before the invocation of each test method in the class.

        // In UI tests it is usually best to stop immediately when a failure occurs.
        continueAfterFailure = false

        // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
    }

    override func tearDownWithError() throws {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
    }

    @MainActor
    func testExample() throws {
        // UI tests must launch the application that they test.
        let app = XCUIApplication()
        app.launch()

        // Use XCTAssert and related functions to verify your tests produce the correct results.
    }

    @MainActor
    func testLaunchPerformance() throws {
        // This measures how long it takes to launch your application.
        measure(metrics: [XCTApplicationLaunchMetric()]) {
            XCUIApplication().launch()
        }
    }
}

```

### TravelVerse/Models/User.swift

```swift
//
//  User.swift
//  TravelVerse
//
//  Created by Bingbing Ma on 10/25/25.
//

import Foundation
import FirebaseFirestore

/// 代表用户信息
struct User: Identifiable, Codable {
    @DocumentID var id: String?
    var name: String
    var email: String
    var profileImageUrl: String?
    var createdAt: Date
    
    enum CodingKeys: String, CodingKey {
        case id
        case name
        case email
        case profileImageUrl
        case createdAt
    }
}

```

### TravelVerse/Helpers/DateExtensions.swift

```swift
//
//  DateExtensions.swift
//  TravelVerse
//
//  Created by Bingbing Ma on 10/25/25.
//

import Foundation

extension Date {
    /// 格式化日期为字符串
    func formatted(style: String = "yyyy-MM-dd") -> String {
        let formatter = DateFormatter()
        formatter.dateFormat = style
        return formatter.string(from: self)
    }
    
    /// 获取日期范围字符串
    static func dateRangeString(from startDate: Date, to endDate: Date) -> String {
        let formatter = DateFormatter()
        formatter.dateFormat = "MM/dd"
        let start = formatter.string(from: startDate)
        let end = formatter.string(from: endDate)
        return "\(start) - \(end)"
    }
}

```

### TravelVerse/Models/Photo.swift

```swift
//
//  Photo.swift
//  TravelVerse
//
//  Created by Bingbing Ma on 10/25/25.
//

import Foundation
import FirebaseFirestore

/// 代表用户上传的照片
struct Photo: Identifiable, Codable {
    @DocumentID var id: String?
    var userId: String
    var tripId: String
    var day: Int? // 第几天
    var placeId: String? // 关联到具体地点
    var placeName: String
    var imageBase64: String // Base64编码的图片数据
    var caption: String?
    var sentiment: [String]? // 情绪标签数组，如 ["Happy", "Excited", "Peaceful"]
    var latitude: Double
    var longitude: Double
    var timestamp: Date
    var cityName: String
    
    enum CodingKeys: String, CodingKey {
        case id
        case userId
        case tripId
        case day
        case placeId
        case placeName
        case imageBase64
        case caption
        case sentiment
        case latitude
        case longitude
        case timestamp
        case cityName
    }
}

```

### TravelVerse/Models/Trip.swift

```swift
//
//  Trip.swift
//  TravelVerse
//
//  Created by Bingbing Ma on 10/25/25.
//

import Foundation
import FirebaseFirestore

/// 代表一个完整的旅行
struct Trip: Identifiable, Codable {
    @DocumentID var id: String?
    var userId: String
    var cityName: String
    var startDate: Date
    var endDate: Date
    var itinerary: [DayItinerary]
    var createdAt: Date
    var updatedAt: Date
    
    enum CodingKeys: String, CodingKey {
        case id
        case userId
        case cityName
        case startDate
        case endDate
        case itinerary
        case createdAt
        case updatedAt
    }
}

/// 代表一天的行程
struct DayItinerary: Identifiable, Codable {
    var id: String = UUID().uuidString
    var day: Int
    var date: Date
    var places: [Place]
    var notes: String?
    
    enum CodingKeys: String, CodingKey {
        case id
        case day
        case date
        case places
        case notes
    }
}

/// 代表一个地点
struct Place: Identifiable, Codable {
    var id: String = UUID().uuidString
    var name: String
    var address: String
    var latitude: Double
    var longitude: Double
    var category: String // 例如: "restaurant", "attraction", "hotel"
    var description: String?
    var startTime: String? // 例如: "09:00"
    var endTime: String? // 例如: "11:00"
    var realTimeInfo: String? // 队友web scraping获取的实时信息
    
    enum CodingKeys: String, CodingKey {
        case id
        case name
        case address
        case latitude
        case longitude
        case category
        case description
        case startTime
        case endTime
        case realTimeInfo
    }
}

```

[9 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]