Sprint 2 - Moviles-G23-20262/General-Wiki GitHub Wiki
Ethics video
Implemented Business Questions (BQs)
Summary
| BQ | Question | Author | Type | Endpoint |
|---|---|---|---|---|
| 2 | How many listings does a buyer open before contacting a seller, and how many before completing an exchange? | Angie Gutiérrez | 2 | GET /analytics/buyer-journey-funnel/ |
| 4 | How many messages do a buyer and a seller exchange in the app chat before they agree on a meeting point, and how long does this conversation take? | Sabrina Noguera | 2 | GET /analytics/chat-to-meeting-point/ |
| 5 | At what time of the day and which days of the week do students publish and browse the most? | Daniel Diab | 2 | GET /analytics/activity-times/ |
| 6 | Which categories of items generate the most listings and the most completed exchanges? | Andres Botero Ruiz | 3 | GET /analytics/categories/ |
| 7 | How many buyers save items in the wishlist, and how many of those saved items end in a purchase after a Smart Matching notification? | Santiago Bernal | 3 | GET /analytics/wishlist-conversion/ |
| 12 | Which specific campus locations are most frequently used for item exchanges, and how does this density shift during different hours of the day? | Carla González Mina | 3 | GET /analytics/meeting-points/ |
Detailed Breakdown
BQ 2. How many listings does a buyer open before contacting a seller, and how many before completing an exchange?
Author: Angie Gutiérrez
Type: 2
Rationale: Evaluates friction during the buyer discovery journey and assesses whether search and comparison screens provide sufficient upfront information. The metric measures distinct items (material_id) viewed via LISTING_VIEW events before a user's first CONTACT_SELLER event (cutoff 1) and before an Exchange.completedAt transaction timestamp (cutoff 2). The endpoint processes event streams via a decoupled pure function (calculate_buyer_journey_metrics), computing aggregated statistics (mean, median, p75, total users, and total analyzed events) without locking analytical reads, as verified by a comprehensive unit test suite in test_buyer_journey.py.
BQ 4. How many messages do a buyer and a seller exchange in the app chat before they agree on a meeting point, and how long does this conversation take?
Author: Sabrina Noguera
Type: 2
Rationale: The Sprint 1 feasibility report marked this as Fully Possible because we have access to the buyer-seller conversations. Message data comes from the Messaging Service messages models, and the agreement moment comes from the Meeting Coordination Service with the meetingPoint confirmation timestamp. The endpoint groups messages by conversation and keeps only messages sent up to the meeting confirmation. For each conversation, it computes the message count and the time to agreement. It then returns the distribution of both metrics so they can be plotted directly as a histogram of messages per thread and a box plot of time to agreement. Conversations that never reach a confirmed meeting point are excluded from the metrics and reported separately as a count.
BQ 5. At what time of the day and which days of the week do students publish and browse the most?
Author: Daniel Diab
Type: 2
Rationale: The Sprint 1 feasibility report marked this as Fully Possible: publishing activity comes straight from Material.createdAt, and browsing activity comes from AnalyticsEvent.occurredAt filtered to LISTING_VIEW and SEARCH events. The endpoint extracts the day of week and hour from both timestamps with Django's ExtractWeekDay/ExtractHour and groups by (day_of_week, hour), returning two counted series (publishing_activity, browsing_activity) that can be plotted directly as a heatmap of app usage over the week.
BQ 6. Which categories of items generate the most listings and the most completed exchanges?
Author: Andres Botero Ruiz
Type: 3
Rationale: Marked Fully Possible since Material.category is a native enum field. The endpoint groups Material by category and annotates both the total listing count and the count of linked Exchange records per category, ranking categories by completed exchanges first and total listings second — directly answering which categories move the most inventory versus which just get listed.
BQ 7. How many buyers save items in the wishlist, and how many of those saved items end in a purchase after a Smart Matching notification?
Author: Santiago Bernal
Type: 3
Rationale: Marked Fully Possible because WishlistItem, Notification (filtered to type = SMART_MATCH) and Exchange all key off the same (user, material) pair. The endpoint builds a lookup of the first Smart Match notification per pair and the purchase timestamp per pair, then classifies every wishlist item into "received a Smart Match", "purchased after that Smart Match", or "purchased without one," returning a conversion rate (purchased_after_smart_match / items_with_smart_match) that measures whether Smart Match notifications actually drive purchases.
BQ 12. Which specific campus locations are most frequently used for item exchanges, and how does this density shift during different hours of the day?
Author: Carla González Mina
Type: 3
Rationale: Originally marked Not Possible in Sprint 1 because no location data existed on Exchange. This was resolved by extending the schema with a new MeetingPoint model (name, lat/lng, monitored flag) and a meetingPointId foreign key on Exchange, mirroring the same change on the upstream Prisma/NestJS backend. The endpoint groups completed exchanges by meeting point and by hour of day (converted from UTC to America/Bogota via a custom CampusHour SQL function), with an optional ?hour= filter, giving the counts needed to build the requested heat map and see how usage shifts across the day. It degrades gracefully (available: false) if the MeetingPoint migration hasn't been applied yet in a given environment.
Analytics Pipeline
In case the Teaching Assistant has astigmatism, here you go
Architectural Design
Class Diagram
Click here: Online Class Diagram
The CampusSwap domain model represents the structural foundation of a closed-loop academic marketplace, enforcing strong transactional integrity, communication boundaries, and decoupled telemetry collection:
- Identity & Catalog Management:
UserEntityserves as the root aggregate for academic profiles and authentication within the campus boundary. A one-to-many relationship linksUserEntitytoMaterialEntity(1to1..N), ensuring that every published listing has an explicit, accountable student seller (sellerId). - Transactional Exclusivity: An item listed in
MaterialEntitycan transition toExchangeEntitythrough a strict one-to-zero/one relationship (1to0..1). This multiplicity guarantees that physical academic goods cannot be double-sold. Furthermore,ExchangeEntityacts as an immutable transactional ledger capturing bothbuyerIdandsellerIdas foreign keys toUserEntity, locking in the agreed price, transaction completion timestamp, and physical GPS coordinates. - Campus Safety & Meeting Points:
MeetingPointEntitymodels verified, monitored physical locations on campus. Its relationship toExchangeEntity(0..1to1..N) is strictly optional, ensuring that transaction records remain historically valid even if specific campus safety zones are reconfigured or archived. - Communication Subsystem: Direct negotiations are encapsulated in
ChatRoomEntity, joining a prospective buyer, the verified seller, and the specific listing under inquiry (MaterialEntity). The relationship betweenChatRoomEntityandMessageEntityis modeled as a strict UML composition (1to1..N, represented by the solid diamond), dictating that individual messages cannot exist outside the lifecycle of their parent chat room and are automatically removed upon conversation termination. - Wishlists & Targeted Alerts:
WishlistItemmaterializes a many-to-many relationship between users and listed items, allowing buyers to monitor item availability.NotificationEntitymaintains a mandatory recipient link (UserEntity) and an optional reference toMaterialEntity(0..1), supporting both administrative broadcasts and contextual Smart Matching price drop notifications. - Decoupled Analytical Telemetry:
AnalyticsEventcaptures high-volume client telemetry streams (LISTING_VIEW,CONTACT_SELLER,SEARCH). BothuserIdandmaterialIdare modeled as optional foreign keys (0..1), intentionally preventing referential database locks during peak campus usage and allowing privacy-preserving anonymous search tracking.
Context Diagram
The context diagram shows CampusSwap from the perspective of its stakeholders and external dependencies. The three actors are Students, The Kotlin team, and Flutter team: students publish, find, buy, and rate academic materials, while both teams build independent mobile clients that consume the same backeend. Having two clients share one CampusSwap API avoids duplicating business riles and keeps data consistent across platforms.
Inside the system are the components that run on the phone: the App Cre (Jetpack Compose in Kotlin, Flutter in Dart), the device hardware (GPS for exchange confirmation and context-aware meeting points, the camera for listing photos), a local event queue that keeps analytics events on disk until they can be sent, and an in-memory cache of the last data loaded from the server. They belong to the system because they work without connectivity.
Outside the system is the team's backend on Vercel: teh CampusSwap API (NestJS + Prisma) is the only writer to PostgresSQL, and the Analytics API (Django) reads the same database to answer the business questions. Google Play Services, the maps service, and Firebase Crashlytics are third-part providers with their own lifecycle. The dashboard is used only by the development teams to create ans inspect data, so it appears as a separate external tool.
Deployent Diagram
The diagram shows two mobile devices, separate NestJS and Django application runtimes, a shared PostgreSQL database, and external services. The dashed boundary represents the intended backend hosting environment; the cloud provider and whether each service runs in a container or as a serverless function must be confirmed from the deployment configuration.
Device execution environments. The Kotlin client runs on Android with Jetpack Compose, GPS access and a local analytics event queue. Its configured minimum is Android API 26. The Flutter client runs on a smartphone with GPS and camera access. These figures are planning targets, not measured or enforced minimum requirements.
Backend nodes. The CampusSwap API runs NestJS on Node.js and uses Prisma for marketplace operations. The Analytics API runs Django on Python and queries the same PostgreSQL database. Django's ReadOnlyRouter prevents writes and migrations at the application layer; Prisma owns database migrations. A dedicated read-only database credential for Django is recommended but must be verified in the deployed environment.
| Connection | Intended production protocol | Local development | Purpose |
|---|---|---|---|
| Mobile clients → CampusSwap API | HTTPS/REST over TLS, TCP 443 | Android emulator → 10.0.2.2:3000 over HTTP |
Listings, chat messages, exchanges and meeting points |
| Mobile clients → Analytics API | HTTPS/REST over TLS, TCP 443 | Android emulator → 10.0.2.2:8000 over HTTP |
Business-question results, including meeting-point usage by hour |
| NestJS → PostgreSQL | PostgreSQL over TCP 5432, secured with TLS | Local Docker PostgreSQL exposed on host port 5433 | Transactional reads and writes |
| Django → PostgreSQL | PostgreSQL over TCP 5432, secured with TLS | Local Docker PostgreSQL exposed on host port 5433 | Analytical reads |
| Development dashboard → CampusSwap API | HTTPS/REST over TLS, TCP 443 | VITE_BACK_URL defaults to http://localhost:3000 |
Administration and SQL console; omitted from the drawing for clarity |
Implementation status. Chat currently uses HTTP endpoints; WebSockets should be labelled as planned unless a deployed gateway is verified. The drawing also includes POST /analytics-events/batch, external maps and Firebase Crashlytics as intended integrations. In the checked Kotlin and NestJS code, the analytics controller exposes POST /analytics-events, the Kotlin analytics layer still defaults to a local file sink, the campus map is drawn inside the app, and crash reporting uses a local reporter. These diagram labels should be updated when the integrations are completed. The Flutter implementation requires separate verification.
Security and scalability. Public API traffic should use TLS, while production database connections should require TLS and restrict access to the application services. Secrets belong in environment variables. NestJS and Django can be scaled independently because they run as separate services, but they share PostgreSQL as a capacity limit. Connection pooling, indexes and monitoring must be verified before claiming production-scale performance.
Architectural Styles
-
Layered Architecture: The overall solution employs it on both the mobile client and backend systems to enforce clean separation of responsibilities. On the Mobile Client, the application is divided into a UI Layer for rendering screens and capturing user input, a State Layer for managing screen state and reactivity, a Domain Layer for encapsulating core business rules and use cases, and a Service Layer for infrastructure and data communication. On the Backend, the architecture follows a classic four-tier structure comprising a Controller Layer handling incoming HTTP endpoints, a Service Layer executing core marketplace logic, a Data Access Layer, and the Data Layer with PostgreSQL persistence. This decoupling ensures that presentation UI components do not directly manipulate business logic or data access routines, facilitating independent testing and maintainability.
-
Client-Server Architecture: The system uses this architecture to centralize domain data management while maintaining a lightweight consumer on mobile devices. The mobile client interacts with distinct backend services, specifically the primary CampusSwap API for core marketplace transactions and the Analytics API for event telemetry. Both API components interface directly with a shared PostgreSQL database. Additionally, the client integrates directly with external third-party services, including a Maps service for campus location visualizations and Firebase Crashlytics for remote crash and performance telemetry. Decoupling operational endpoints from core transaction endpoints ensures that heavy analytics ingestion does not degrade core user search and listing interactions.
-
Offline-First Architecture: To ensure high resilience under variable university network conditions such as elevators, campus basements, or congested Wi-Fi zones in the university, the mobile client incorporates an Offline-First Architecture. Unsubmitted form data and media assets are preserved locally in Local drafts and caches to prevent user data loss. System requests and telemetry events are queued inside a client-side Offline queue. Once a network connection is re-established, the offline queue automatically synchronizes pending payloads with the NestJS API, which then safely persists them into the PostgreSQL database. This pattern provides an uninterrupted user experience regardless of transient network failures.
-
Event-Driven Architecture: The tracking of analytics and user behavior is designed around an Event-Driven Architecture. This architecture is needed so when users perform actions in the mobile application, the UI Emits Events via a local
logEvent()is able to call to the client-side Analytics Service. The Analytics Service batches these events and sends them asynchronously over a secure HTTPS/TLS connection to the NestJS Ingest endpoint. The backend ingests the events and executes SQL Aggregation views inside PostgreSQL, transforming raw UI interaction streams into structured metrics for dashboards and business questions. Asynchronous event publishing ensures that behavioral analytics collection executes without blocking the main UI thread or delaying network responses.
Design Pattern: Strategy Pattern (Listing Sorting & Ranking)
- Responsible Member: Angie Gutiérrez
- Pattern Type: Behavioral (GoF Strategy Pattern)
- Location in Code:
lib/Domain/Strategies/sort_strategy.dartconsumed bylib/Presentation/Screens/search_screen.dart - Architectural Rationale:
In the marketplace catalog search (View 05/06), sorting criteria vary dynamically based on buyer preference (price ascending, price descending, relevance, or newest publication). Implementing this logic via imperative conditional branches (
if-elseorswitch) inside UI widgets violates the Open/Closed Principle (OCP) and tightly couples presentation state with business ordering rules. By introducing theSortStrategyabstraction in the Domain layer, sorting algorithms are encapsulated in independent classes (PriceAscSortStrategy,PriceDescSortStrategy,RelevanceSortStrategy,NewestSortStrategy). The presentation widget delegates sorting directly to the active strategy instance (_filters.sort.sort(list)), allowing new ranking heuristics (such as distance or seller rating) to be added without modifying the search screen codebase.
Architectural Patterns - Kotlin
- MVVM is the central structural pattern: Compose screens render the state exposed by
AppViewModel,MeetingPointViewModel, andExchangeCheckInViewModel, and never contain business logic. - Singleton / Service Locator:
AppContaineris created once inCampusSwapApplicationand provides the single instances of every API and repository, so the whole app shares the same Retrofit clients and caches. - Adapter / Mapper converts backend DTOs into domain models (
toAppNotification(),toProduct()), keeping backend names such asopenedAt,SMART_MATCH, orLAB_EQUIPMENTout of the UI. - Observer with
StateFlowand Compose state (mutableOf,mutableStateListOf) means the UI recomposes automatically when the ViewModel state changes, with no manual refresh. - Repository abstracts the data source:
NotificationRepository,UserRepository,MaterialRepository, andRatingRepositorydepend on aRemoteDataSourceinterface implemented with Retrofit, so the ViewModel asks for domain objects without knowing whether they came from the network or from the cache of the last real data. When the request fails, the repository returns that cahced copy withisLive = false(graceful degradation). - Strategy in
MeetingPointRankingStrategy:TimeOfDayStrategySelectorpicksDaytimeStrategyorNightSafetyStrategy(monitorized zones only after 6pm) without the ViewModel knowing wich one is active. - Facade in
Analytics.log()hides the event queue, the on-disk store, and the swappableEventSinkbehind a single call used by every screen.
Strategy pattern, meeting point ranking
- Where:
domain/MeetingPointRanking.ktanddomain/RankingStrategySelector.kt, used byscreens/meeting/MeetingPointViewModel.kt. - Why Strategy: the rule for choosing a meeting point changes with the context. In daylight every public zone is acceptable; after 18:00 (it gets dark around that time all year in Bogotá) only zones monitored by campus security are suggested. Each rule is a class behind
MeetingPointRankingStrategy, andTimeOfDayStrategySelectorpicks one for the current time. The ViewModel only knows the two interfaces, so adding a rule (for example an exam-week rule) does not touch the ViewModel. - Shared criterion: both strategies minimise the longest walk of the two people (minimax) instead of the total, so neither person is sent across campus. A unit test shows the difference: the smallest total (1 min and 10 min) loses to the fairest option (6 min and 6 min).
- Tests:
MeetingPointRankingStrategyTest,TimeOfDayStrategySelectorTest,WalkingTimeTest.
MVVM and Repository in the meeting and exchange flow (Carla González Mina)
- ViewModels:
MeetingPointViewModel(Campus Guardian suggestion) andExchangeCheckInViewModel(GPS check-in) expose aStateFlow; the Compose screens only render that state. - Repositories:
MeetingPointRepository(meeting points from the API, reusing the last loaded list offline),MeetingPointPopularityRepository(BQ12 top 3 for the current hour),MeetingProposalRepository(chat room, shared free times, proposals and answers),ExchangeRepository(completes the buyer's order with the measured location) andAuthRepository. - Data sources behind interfaces:
LocationDataSource(one-shot fused location with a fresh fix), remote data sources implemented with Retrofit, andSessionStore(DataStore). - Session:
SessionManagerkeeps the JWT; an OkHttpAuthInterceptoradds it only to requests for the CampusSwap API host and ends the session on a 401.
Architectural Patterns - Flutter
In case the Teaching Assistant has astigmatism, here you go
Architectural Tactics
Click here: Online Tactics Diagram
The architectural tactics of CampusSwap are categorized across four fundamental quality attributes, establishing concrete engineering trade-offs to meet reliability, security, scalability, and maintainability requirements within an academic environment:
-
Availability:
- Offline Event Queue: To mitigate campus Wi-Fi blackouts and cellular dead zones across university buildings, mobile clients maintain an on-device persistent queue that serializes telemetry and analytics events to local storage, retrying transmission once network connectivity is re-established.
- Graceful Degradation: High-level domain services implement fail-safe fallbacks; for instance, the meeting points recommendation and analytics ingestion services degrade gracefully (serving localized heuristics or setting explicit status flags) when backend migrations or cloud dependencies become temporarily unreachable.
- Safe Deduplication: Client batches carry unique, deterministic UUIDs per recorded action, allowing the NestJS ingestion controller to process payloads via
skipDuplicatesoperations without corrupting metrics upon repeated network retries.
-
Security:
- Domain-Bound Auth: Access and student account registration are strictly constrained to verified institutional credentials (
@uniandes.edu.co), creating a closed trust perimeter for all marketplace and in-app communications. - Credential Hashing: Passwords and sensitive secrets are never stored in plaintext within the domain model;
UserEntitystrictly persists cryptographic one-way hashes (passwordHash), mitigating credential exposure risks. - Data Minimization: Physical sensor coordinates captured during exchange scheduling and browsing telemetry are truncated and rounded to 3 decimal places before persistence, preserving location utility for density mapping while eliminating granular student movement tracking.
- Domain-Bound Auth: Access and student account registration are strictly constrained to verified institutional credentials (
-
Performance:
- Event Batching: Instead of generating immediate HTTP overhead per interaction, analytical events accumulate on the device until reaching a threshold of 10 items or a 30-second window, significantly reducing radio wake-ups, battery drain, and mobile data usage.
- Client Price AI: The smart pricing estimation use case (
PriceEstimatorUseCase) executes directly on the client runtime using local parametric domain distributions, delivering sub-second price range suggestions (p25, p75) without introducing remote API latency. - Indexed Analytics: High-throughput PostgreSQL tables such as
AnalyticsEventleverage composite database indices over(userId, materialId, eventType, occurredAt), ensuring low-latency aggregations during heavy analytical querying.
-
Modifiability:
- Strategy Pattern: Catalog sorting and filtering algorithms are decoupled behind the polymorphic
SortStrategydomain interface, enabling new ranking criteria (e.g., proximity, seller reputation) to be incorporated without modifying existing search screen widgets. - Nullable Telemetry Constraints: By defining
userIdandmaterialIdforeign keys as optional inAnalyticsEvent, the analytical pipeline allows schema modifications and anonymous telemetry tracking without introducing strict referential locks on transactional tables. - Pure Metrics Calc: Core analytical calculations (such as
calculate_buyer_journey_metricsin BQ2) are implemented as isolated, deterministic pure functions, enabling exhaustive unit testing and future pipeline refactoring without touching database access layers.
- Strategy Pattern: Catalog sorting and filtering algorithms are decoupled behind the polymorphic
Features implemented this sprint, by author
Flutter App
| # | Feature | What it does | Files | Author |
|---|---|---|---|---|
| 1 | Listing Sorting Strategy (Design Pattern) | GoF Strategy pattern implementation defining SortStrategy and concrete algorithms (Relevance, PriceAsc, PriceDesc, Newest) to dynamically reorder catalog items without conditional branching. |
lib/Domain/Strategies/sort_strategy.dart, lib/Presentation/Screens/search_screen.dart |
Angie Gutiérrez |
| 2 | Catalog Search & Dynamic Filter Modal (Views 05 & 06) | Search grid with live keyword query and modal bottom sheet filter panel supporting MaterialCategoryEnum, MaterialConditionEnum, price slider, and runtime strategy switching. |
lib/Presentation/Screens/search_screen.dart |
Angie Gutiérrez |
| 3 | Camera Sensor Integration | Native mobile camera invocation using image_picker with platform permissions and in-memory preview container. |
lib/Presentation/Screens/new_listing_smart_screen.dart, ios/Runner/Info.plist |
Angie Gutiérrez |
| 4 | Smart AI Price Estimation (View 08) | Domain use case and smart valuation card estimating fair market price ranges (p25, p75, and recommended value) based on item category and condition. | lib/Domain/use_cases/price_estimator_use_case.dart, lib/Presentation/Screens/new_listing_smart_screen.dart, lib/Presentation/State Management/app_state.dart |
Angie Gutiérrez |
| 5 | Student Account Registration | User registration flow allowing verified Uniandes students to create a CampusSwap account using their institutional credentials and initialize their profile. | register_screen.dart, register_student_use_case.dart, registration_rules.dart |
Sabrina Noguera |
| 6 | GPS-Based Meeting Point Location | Uses the device's GPS sensor to obtain the user's current location and calculate distances to available CampusSwap meeting points. | device_location_data_source.dart, GetCurrentLocationUseCase and platform permissions |
Sabrina Noguera |
| 7 | Context-Aware Meeting Point Recommendation | Dynamically identifies suitable meeting points based on the user's current location, time of day, and historical density of confirmed exchanges. | meeting_point_recommendation_use_case.dart, lib/Presentation/Screens/meeting_points_screen.dart |
Sabrina Noguera |
| 8 | Smart Meeting Point Ranking | Uses a scoring model combining distance, time suitability, and historical exchange activity to rank available meeting points. | rank_meeting_points_use_case.dart, meeting_point_score.dart |
Sabrina Noguera |
| 9 | Conversation Agreement Insight (Type 2 / BQ4) | Uses BQ4 message-count and agreement-time metrics to provide feedback about the progress of an active conversation toward meeting confirmation. | lib/Domain/use_cases/conversation_insight_use_case.dart, lib/Presentation/Screens/chat_screen.dart |
Sabrina Noguera |
| 10 | Campus Map Integration | Integrates an external mapping service to display meeting points and obtain geographic information for location visualization and distance calculations. | FlutterMap + OpenStreetMap in meeting_point_screen.dart |
Sabrina Noguera |
| 11 | Buy Now & Orders | Places an order from the listing detail; the item becomes reserved and the seller is notified. | lib/Presentation/Screens/material_detail_screen.dart, lib/Presentation/State Management/account_state.dart, lib/Domain/use_cases/marketplace_use_cases.dart, lib/Domain/Entities/exchange_entity.dart, lib/Data/Repositories/marketplace_repositories_impl.dart |
Andrés Botero |
| 12 | Order Placed Screen | Confirmation with the real order number (CSW-####), next steps and shortcuts to the chat or to completing the exchange. |
lib/Presentation/Screens/confirmation_screen.dart, lib/Presentation/State Management/app_state.dart |
Andrés Botero |
| 13 | Chat Redesign | Listing strip, order status banner, day separators, read receipts and a safety tips sheet. | lib/Presentation/Screens/chat_view.dart, lib/Presentation/Screens/messages_screen.dart |
Andrés Botero |
| 14 | Meeting Proposals in Chat | Meeting cards that can be accepted, declined or withdrawn; status updates through polling and the agreed spot shows as a chip. | lib/Presentation/Screens/chat_view.dart, lib/Presentation/State Management/chat_state.dart, lib/Domain/use_cases/meetup_use_cases.dart, lib/Domain/Entities/meetup_entities.dart |
Andrés Botero |
| 15 | Meeting Point Map | OpenStreetMap map via flutter_map (no API key) with campus safe-zone markers, tap-to-select and a dark-mode filter. |
lib/Presentation/Screens/meeting_point_screen.dart, lib/Presentation/State Management/meeting_planner_state.dart, android/app/src/main/AndroidManifest.xml |
Andrés Botero |
| 16 | Campus Guardian Suggestion (Smart feature) | Suggests hours when both students are free, from their class schedules, preferring breaks between classes. | lib/Presentation/Screens/meeting_point_screen.dart, lib/Presentation/State Management/meeting_planner_state.dart, lib/Data/data_sources/meetup_remote_data_source.dart |
Andrés Botero |
| 17 | GPS Walking Time & Zone Ranking (Uses a sensor and is context-aware) | Reads location with geolocator and estimates walking time (80 m/min); "Best match" is the closest monitored zone. |
lib/Data/data_sources/device_location_data_source.dart, lib/Domain/use_cases/meetup_use_cases.dart, lib/Data/Repositories/meetup_repositories_impl.dart, android/app/src/main/AndroidManifest.xml |
Andrés Botero |
| 18 | Class Schedule | Weekly class editor with overlap validation, used by the Campus Guardian; only free hours are shared. | lib/Presentation/Screens/schedule_screen.dart, lib/Presentation/State Management/schedule_state.dart, lib/Domain/use_cases/meetup_use_cases.dart, lib/Presentation/Screens/profile_screen.dart |
Andrés Botero |
| 19 | Complete Exchange & Rating | Item checklist and received condition, then 1–5 stars, tags and an optional review; marks the item as sold. | lib/Presentation/Screens/complete_exchange_screen.dart, lib/Presentation/State Management/account_state.dart, lib/Domain/use_cases/marketplace_use_cases.dart |
Andrés Botero |
| 20 | Order Status in Profile & Seller Hub | Purchases and sales show pending/completed status; swaps and earnings count only completed exchanges. | lib/Presentation/Screens/profile_screen.dart, lib/Presentation/Screens/seller_hub_screen.dart, lib/Presentation/State Management/account_state.dart |
Andrés Botero |
| 21 | COP Prices & Campus Time Formatting | Prices as $320.000 COP, 24-hour times and chat day labels ("Today", "Wed 16 Sep"). |
lib/Presentation/Widgets/formatters.dart |
Andrés Botero |
| 22 | User Authentication | Register (only @uniandes.edu.co emails) and log in with JWT; the token is kept in secure storage, so the session restores at launch and an expired token logs you out. |
lib/Presentation/Screens/login_screen.dart, lib/Presentation/Screens/register_screen.dart, lib/Domain/rules/registration_rules.dart, lib/Domain/use_cases/login_use_case.dart, lib/Domain/use_cases/register_student_use_case.dart, lib/Domain/use_cases/restore_session_use_case.dart, lib/Domain/use_cases/logout_use_case.dart, lib/Data/Repositories/auth_repository_impl.dart, lib/Data/data_sources/auth_remote_data_source.dart, lib/Data/data_sources/session_storage.dart, lib/core/network/api_client.dart |
Andrés Botero |
| 23 | OpenStreetMap Tile Service (External service) | The meeting point map downloads its map tiles from the public OpenStreetMap tile server (tile.openstreetmap.org) through flutter_map; no API key or billing. |
lib/Presentation/Screens/meeting_point_screen.dart, pubspec.yaml, android/app/src/main/AndroidManifest.xml |
Andrés Botero |
| 24 | Photo Upload to Vercel Blob (External service) | Listing photos are picked on the phone, sent as multipart to POST /uploads, and the back-end stores them in Vercel Blob and returns their public URLs for the listing. |
lib/Presentation/Screens/new_listing_screen.dart, lib/Data/Repositories/marketplace_repositories_impl.dart, lib/Data/data_sources/marketplace_remote_data_source.dart, lib/core/network/api_client.dart, back-end: src/uploads/uploads.service.ts, src/uploads/blob-storage.ts |
Andrés Botero |
| 25 | Ambient Light Auto Theme | Reads the phone's ambient light sensor and switches light/dark automatically (dark below 50 lux, light above 200, after 3 s); manual Light/Dark in Profile → Appearance. | lib/Presentation/State Management/theme_state.dart, lib/Domain/use_cases/appearance_use_cases.dart, lib/Data/data_sources/ambient_light_data_source.dart, lib/Data/Repositories/appearance_repositories_impl.dart, lib/Presentation/Widgets/appearance_card.dart |
Andrés Botero |
| 26 | Battery Saver Mode | Reads battery level and charging state; below 20 % and unplugged, chat refreshes every 15 s instead of 4 s and shows a note, back to normal at 25 % or when charging. | lib/Presentation/State Management/power_state.dart, lib/Domain/use_cases/power_use_cases.dart, lib/Data/data_sources/battery_data_source.dart, lib/Presentation/State Management/chat_state.dart, lib/Presentation/Widgets/power_saving_note.dart |
Andrés Botero |
| 27 | Campus-Closed Notice | Uses the phone's clock to warn in the meeting planner when campus is closed (after 20:00, before 07:00, Sundays) and says when meetups can resume. | lib/Domain/rules/campus_hours.dart, lib/Presentation/Screens/meeting_point_screen.dart |
Andrés Botero |
Kotlin App
| # | Feature | What it does | Files | Author |
|---|---|---|---|---|
| 1 | App shell, theme & typography | Compose project setup; 5-color light/dark palette; Fraunces/Outfit/JetBrains Mono variable fonts; app icon | MainActivity, ui/theme/* |
Daniel Diab |
| 2 | Shared component library | ProductCard, condition/rating badges, category chips, empty states, price formatting |
components/Badges, CategoryChip, EmptyState, Formatting, ProductCard, ProductImage, SectionHeader |
Daniel Diab |
| 3 | Navigation & bottom bar | NavHost with per-tab back stacks; 5-destination bottom bar (Home, Search, Sell, Cart, Seller) |
navigation/BottomNavBar, CampusSwapApp, Destinations |
Daniel Diab |
| 4 | Login | Email/password with validation, loading state, show/hide password, demo access | screens/auth/LoginScreen |
Daniel Diab |
| 5 | Home, Search, Product detail | Header with theme/notifications/wishlist, category carousels; search with filter panel; product detail with photo carousel and related items | screens/home/HomeScreen, screens/search/SearchScreen, screens/product/ProductDetailScreen |
Daniel Diab |
| 6 | Sell flow | 4-step listing flow (photos/info, course/condition, review, confirmation) with state kept across steps | screens/sell/SellScreen |
Daniel Diab |
| 7 | Profile / Seller | Avatar, verified badge, masked email, "No reviews yet" state, "My listings" | screens/profile/ProfileScreen |
Daniel Diab |
| 8 | Cart & wishlist (v1) | Cart with quantities/total/empty state; wishlist toggle; notifications inbox | screens/cart/CartScreen, screens/home/WishlistScreen, NotificationsScreen |
Daniel Diab |
| 9 | App state (v1) | Single AppViewModel holding cart, wishlist, notifications, listings |
data/AppViewModel, Models, SampleData |
Daniel Diab |
| 10 | In-app chat | Chat thread with message states (sending → sent → delivered → read) | screens/chat/ChatScreen |
Carla González Mina |
| 11 | Meeting point coordination | Recommended on-campus meeting point and time-slot proposal, with accept/confirm flow | screens/meeting/MeetingPointScreen |
Carla González Mina |
| 12 | App state extended for chat/meetings | Chat threads, meeting proposals, canned replies added to the ViewModel | data/AppViewModel, Models, SampleData (chat/meeting additions) |
Carla González Mina |
| 13 | Design system refresh | New icon set and shared UI primitives used across the restyled screens | components/CampusIcons, components/CampusUi |
Santiago Bernal |
| 14 | Checkout & order confirmation | Checkout with service fee, order placement, confirmation screen | screens/cart/CheckoutScreen, order flow in CampusSwapApp/AppViewModel |
Santiago Bernal |
| 15 | Smart alerts (View 11) | Saved-search alerts by keyword/course/category/price/condition, with match notifications | screens/home/AlertsScreen, alert logic in AppViewModel |
Santiago Bernal |
| 16 | Exchange completion & rating (View 12) | Closing an exchange and mutual buyer/seller rating, revealed once both sides rate | screens/rating/CompletionScreen, rating logic in AppViewModel |
Santiago Bernal |
| 17 | Analytics layer | Analytics facade, disk-backed event queue with batching/retry, swappable EventSink, crash reporter, events wired into the ViewModel/navigation/search, unit tests |
analytics/*, CampusSwapApplication, src/test/.../EventQueueTest |
Daniel Diab |
| 18 | GPS exchange check-in (a · Sensor) | Before rating, the buyer confirms they are within 50 m of the agreed meeting point using a fresh fused-location fix (runtime permission with rationale; manual fallback when permission is denied, location is off or there is no fix). Confirming completes the buyer's order on the backend with the measured coordinates and logs exchange_confirmed with the method (gps or manual) |
data/location/*, domain/Proximity, data/ExchangeRepository, screens/rating/ExchangeCheckIn* |
Carla González Mina |
| 19 | Campus Guardian suggestion (c · Context-aware) | Ranks the meeting points loaded from the API using the buyer's location, the other person's location and the time of day, and explains the choice in the context card | screens/meeting/MeetingPointViewModel, MeetingPointScreen, data/MeetingPointRepository |
Carla González Mina |
| 20 | Strategy for meeting point ranking (design pattern) | Daytime and night-safety strategies chosen by the time of day; minimax on walking time | domain/MeetingPointRanking, domain/RankingStrategySelector, domain/WalkingTime |
Carla González Mina |
| 21 | "Popular now" badge (b · Type 2 BQ, from BQ12) | Marks the three busiest meeting points for the current hour; hidden without breaking the screen when analytics is unreachable | data/MeetingPointPopularityRepository, data/remote/AnalyticsApi |
Carla González Mina |
| 22 | Session persistence and protected routes (e · Auth, contribution) | Real login against the API, JWT stored in DataStore and restored on launch, bearer token added to API calls, Chat and Meeting Point require a valid session, logout clears the token | data/auth/*, data/remote/AuthInterceptor, navigation/CampusSwapApp, screens/auth/LoginScreen |
Carla González Mina |
| 23 | Meeting proposals through the backend (f · Backend service, contribution) | Proposals use the shared free times computed by the API and are sent and answered through /chatrooms and /meeting-proposals; the chat polls the proposal every 5 s while open to update the View 9 card, and whoever receives a proposal can accept or decline it |
data/MeetingProposalRepository, data/remote/MeetingProposalApi, screens/chat/ChatScreen, screens/meeting/MeetingPointScreen |
Carla González Mina |
Summary by person
| Author | Features owned |
|---|---|
| Daniel Diab | App shell/theme, component library, navigation, login, home/search/product detail, sell flow, profile, cart v1/wishlist v1, initial app state, analytics layer |
| Carla González Mina | In-app chat, meeting point coordination, GPS exchange check-in (a), Campus Guardian context-aware suggestion (c), Strategy pattern for meeting point ranking, BQ12 (schema, Django endpoint, dashboard heat map), "Popular now" badge (b), session persistence contribution (e), meeting proposals through the backend contribution (f) |
| Santiago Bernal | Design system refresh (icons/UI kit), checkout & order confirmation, smart alerts, exchange completion & rating |
| Sabrina Noguera | Student Account Registration, GPS-Based Meeting Point Location, Context-Aware Recommendation, Smart Meeting Point Ranking, Conversation Agreement Insight (BQ4), Campus Map Integration |