chore(branding): rebrand FixMate to CityPulse across codebase
- Update product name in README, backend docs, and design tokens - Rename Flutter root widget to CityPulseApp and update tests - Update dashboard brand strings and HTML title - Refresh i18n strings (en/ms) and welcome taglines - Adjust backend API title/description and root message - Minor formatting in ApiService comments; no logic changes - Update Android/iOS manifest comments and pubspec description No API endpoints or response schema changes.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# 🏗️ FixMate - Smart Citizen-Driven Urban Maintenance Platform
|
||||
# 🏗️ CityPulse - Smart Citizen-Driven Urban Maintenance Platform
|
||||
|
||||
FixMate is a comprehensive citizen reporting application that combines **Flutter frontend** with **Python FastAPI backend** and **AI-powered image classification**. Users can capture urban issues (potholes, broken streetlights, trash, etc.), get automatic AI classification, and track their reports through a complete management system.
|
||||
CityPulse is a comprehensive citizen reporting application that combines **Flutter frontend** with **Python FastAPI backend** and **AI-powered image classification**. Users can capture urban issues (potholes, broken streetlights, trash, etc.), get automatic AI classification, and track their reports through a complete management system.
|
||||
|
||||
## 🎯 System Architecture
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Permissions required for FixMate -->
|
||||
<!-- Permissions required for CityPulse -->
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"app.name": "FixMate",
|
||||
"app.name": "CityPulse",
|
||||
"nav.report": "Report",
|
||||
"nav.map": "Map",
|
||||
"nav.myReports": "My Reports",
|
||||
@@ -81,14 +81,14 @@
|
||||
"settings.theme.dark": "Dark",
|
||||
"lang.en": "English",
|
||||
"lang.ms": "Bahasa Malaysia",
|
||||
"welcome.title": "Spot it. Snap it. Fix it.",
|
||||
"welcome.title": "Snap. Detect. Fix.",
|
||||
"welcome.subtitle": "Report city issues in seconds with AI-powered detection. Help create safer, better communities together.",
|
||||
"cta.continueGuest": "Continue as guest",
|
||||
"cta.signIn": "Sign in",
|
||||
"cta.skip": "Skip",
|
||||
"cta.next": "Next",
|
||||
"cta.getStarted": "Get started",
|
||||
"onboarding.header": "Welcome to FixMate",
|
||||
"onboarding.header": "Welcome to CityPulse",
|
||||
"onboarding.title1": "Fast Issue Reporting",
|
||||
"onboarding.body1": "Simply take a photo of any urban issue - our AI automatically identifies and categorizes the problem in seconds.",
|
||||
"onboarding.title2": "Smart City Mapping",
|
||||
@@ -99,12 +99,12 @@
|
||||
"auth.signInWithApple": "Sign in with Apple",
|
||||
"auth.signInWithGoogle": "Sign in with Google",
|
||||
"auth.comingSoon": "Coming soon",
|
||||
"welcome.title": "Spot it. Snap it. Fix it.",
|
||||
"welcome.title": "Snap. Detect. Fix.",
|
||||
"welcome.subtitle": "Report city issues in seconds with AI-powered detection. Help create safer, better communities together.",
|
||||
"cta.continueGuest": "Continue as Guest",
|
||||
"cta.signIn": "Sign In",
|
||||
"cta.skip": "Skip for now",
|
||||
"onboarding.header": "Welcome to FixMate",
|
||||
"onboarding.header": "Welcome to CityPulse",
|
||||
"onboarding.title1": "Fast Issue Reporting",
|
||||
"onboarding.subtitle1": "AI-Powered Detection",
|
||||
"onboarding.body1": "Simply take a photo of any urban issue - our AI automatically identifies and categorizes the problem in seconds.",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"app.name": "FixMate",
|
||||
"app.name": "CityPulse",
|
||||
"nav.report": "Lapor",
|
||||
"nav.map": "Peta",
|
||||
"nav.myReports": "Laporan Saya",
|
||||
@@ -88,7 +88,7 @@
|
||||
"cta.skip": "Langkau",
|
||||
"cta.next": "Seterusnya",
|
||||
"cta.getStarted": "Mula",
|
||||
"onboarding.header": "Selamat datang ke FixMate",
|
||||
"onboarding.header": "Selamat datang ke CityPulse",
|
||||
"onboarding.title1": "Tangkap pantas",
|
||||
"onboarding.body1": "Ambil gambar dan hantar dalam kurang satu minit.",
|
||||
"onboarding.title2": "Peta yang jelas",
|
||||
@@ -104,7 +104,7 @@
|
||||
"cta.continueGuest": "Teruskan sebagai Tetamu",
|
||||
"cta.signIn": "Log Masuk",
|
||||
"cta.skip": "Langkau buat masa ini",
|
||||
"onboarding.header": "Selamat Datang ke FixMate",
|
||||
"onboarding.header": "Selamat Datang ke CityPulse",
|
||||
"onboarding.title1": "Laporan Isu Pantas",
|
||||
"onboarding.subtitle1": "Pengesanan Berkuasa AI",
|
||||
"onboarding.body1": "Hanya ambil gambar mana-mana isu bandar - AI kami secara automatik mengenal pasti dan mengkategorikan masalah dalam beberapa saat.",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"meta": {
|
||||
"name": "FixMate Design Tokens",
|
||||
"name": "CityPulse Design Tokens",
|
||||
"version": "1.0.0",
|
||||
"brand": "Civic Premium – Citizen First"
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@ Perfect 👍 thanks for clarifying — let’s keep it **venv only**. I’ll adj
|
||||
|
||||
---
|
||||
|
||||
# 🛠️ FixMate Backend – Hackathon Prototype
|
||||
# 🛠️ CityPulse Backend – Hackathon Prototype
|
||||
|
||||
Smart citizen-driven urban maintenance platform powered by **Computer Vision + Generative AI**.
|
||||
This backend runs fully **locally** (no cloud required).
|
||||
|
||||
Binary file not shown.
@@ -16,18 +16,18 @@ logger = logging.getLogger(__name__)
|
||||
# ----------------------
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
logger.info("Starting FixMate Backend...")
|
||||
logger.info("Starting CityPulse Backend...")
|
||||
init_ai_service() # ✅ Models load once here
|
||||
logger.info("AI models loaded successfully.")
|
||||
yield
|
||||
logger.info("FixMate Backend shutting down...")
|
||||
logger.info("CityPulse Backend shutting down...")
|
||||
|
||||
# ----------------------
|
||||
# Initialize FastAPI
|
||||
# ----------------------
|
||||
app = FastAPI(
|
||||
title="FixMate Backend API",
|
||||
description="Backend for FixMate Hackathon Prototype",
|
||||
title="CityPulse Backend API",
|
||||
description="Backend for CityPulse Hackathon Prototype",
|
||||
version="1.0.0",
|
||||
lifespan=lifespan
|
||||
)
|
||||
@@ -80,7 +80,7 @@ except Exception as e:
|
||||
|
||||
@app.get("/")
|
||||
def root():
|
||||
return {"message": "Welcome to FixMate Backend API! Visit /docs for API documentation."}
|
||||
return {"message": "Welcome to CityPulse Backend API! Visit /docs for API documentation."}
|
||||
|
||||
@app.get("/test")
|
||||
def test():
|
||||
|
||||
@@ -2,7 +2,7 @@ Perfect 👍 Before I drop a full codebase, let’s agree on the **flow + plan**
|
||||
|
||||
---
|
||||
|
||||
# ⚡ Backend Flow (FixMate Local Prototype)
|
||||
# ⚡ Backend Flow (CityPulse Local Prototype)
|
||||
|
||||
### 1. Citizen Upload Flow
|
||||
|
||||
@@ -32,7 +32,7 @@ Perfect 👍 Before I drop a full codebase, let’s agree on the **flow + plan**
|
||||
* Every report:
|
||||
|
||||
* Pass image to model → detect objects.
|
||||
* Map objects to FixMate categories (`pothole`, `streetlight`, `trash`, `signage`).
|
||||
* Map objects to CityPulse categories (`pothole`, `streetlight`, `trash`, `signage`).
|
||||
* Apply **severity scoring** (e.g. bounding box area = High if > certain %).
|
||||
* If model fails (no internet, missing weights):
|
||||
|
||||
|
||||
@@ -430,7 +430,7 @@ const cycleStatus = async (reportId) => {
|
||||
return (
|
||||
<div className="app-root">
|
||||
<header className="header">
|
||||
<div className="brand">{t('dashboard.brand') || 'FixMate'}</div>
|
||||
<div className="brand">{t('dashboard.brand') || 'CityPulse'}</div>
|
||||
<div className="lang-toggle">
|
||||
<label style={{fontSize:12, color:'#374151'}}>{t('label.language') || 'Language'}</label>
|
||||
<select value={lang} onChange={e=>setLang(e.target.value)}>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"dashboard.brand": "FixMate",
|
||||
"dashboard.brand": "CityPulse",
|
||||
"dashboard.filters": "Filters",
|
||||
"queue.title": "Tickets",
|
||||
"drawer.details": "Details",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"dashboard.brand": "FixMate",
|
||||
"dashboard.brand": "CityPulse",
|
||||
"dashboard.filters": "Penapis",
|
||||
"queue.title": "Tiket",
|
||||
"drawer.details": "Maklumat",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>FixMate Dashboard</title>
|
||||
<title>CityPulse Dashboard</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.css" />
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
|
||||
<!-- Permissions required by FixMate -->
|
||||
<!-- Permissions required by CityPulse -->
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Camera access is required to capture issue photos.</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
|
||||
@@ -10,8 +10,8 @@ import 'screens/settings/settings_screen.dart';
|
||||
import 'theme/themes.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class FixMateApp extends StatelessWidget {
|
||||
const FixMateApp({super.key});
|
||||
class CityPulseApp extends StatelessWidget {
|
||||
const CityPulseApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -4,21 +4,21 @@ import 'app.dart';
|
||||
import 'l10n/i18n.dart';
|
||||
import 'l10n/locale_provider.dart';
|
||||
export 'app.dart';
|
||||
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
|
||||
// Initialize locale provider
|
||||
final localeProvider = LocaleProvider();
|
||||
await localeProvider.init();
|
||||
|
||||
|
||||
// Initialize i18n with the current locale
|
||||
await I18n.init(localeProvider.locale);
|
||||
|
||||
|
||||
runApp(
|
||||
ChangeNotifierProvider.value(
|
||||
value: localeProvider,
|
||||
child: const FixMateApp(),
|
||||
child: const CityPulseApp(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import 'package:uuid/uuid.dart';
|
||||
import '../models/report.dart';
|
||||
import '../models/enums.dart';
|
||||
|
||||
/// Service for communicating with the FixMate Backend API
|
||||
/// Service for communicating with the CityPulse Backend API
|
||||
class ApiService {
|
||||
// Configure this to match your backend URL
|
||||
// Use localhost for web/desktop, network IP for mobile/emulator
|
||||
@@ -77,8 +77,10 @@ class ApiService {
|
||||
request.fields['latitude'] = latitude.toString();
|
||||
request.fields['longitude'] = longitude.toString();
|
||||
request.fields['description'] = description;
|
||||
if (userName != null && userName.isNotEmpty) request.fields['user_name'] = userName;
|
||||
if (address != null && address.isNotEmpty) request.fields['address'] = address;
|
||||
if (userName != null && userName.isNotEmpty)
|
||||
request.fields['user_name'] = userName;
|
||||
if (address != null && address.isNotEmpty)
|
||||
request.fields['address'] = address;
|
||||
|
||||
// Add the image file
|
||||
request.files.add(
|
||||
@@ -162,7 +164,9 @@ class ApiService {
|
||||
if (response.statusCode == 200 || response.statusCode == 204) {
|
||||
return true;
|
||||
} else {
|
||||
print('Failed to delete ticket: ${response.statusCode} ${response.body}');
|
||||
print(
|
||||
'Failed to delete ticket: ${response.statusCode} ${response.body}',
|
||||
);
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -190,7 +194,8 @@ class ApiService {
|
||||
/// Convert API ticket response to Report model
|
||||
static Report _convertApiTicketToReport(Map<String, dynamic> data) {
|
||||
final id = (data['id'] ?? data['ticket_id'] ?? '').toString();
|
||||
final imageUrl = (data['image_url'] as String?) ??
|
||||
final imageUrl =
|
||||
(data['image_url'] as String?) ??
|
||||
(data['image_path'] != null
|
||||
? '$_uploadsUrl/${(data['image_path'] as String).split('/').last}'
|
||||
: null);
|
||||
@@ -207,9 +212,19 @@ class ApiService {
|
||||
lat: (data['latitude'] as num?)?.toDouble() ?? 0.0,
|
||||
lng: (data['longitude'] as num?)?.toDouble() ?? 0.0,
|
||||
),
|
||||
createdAt: (data['created_at'] ?? data['createdAt'] ?? DateTime.now().toIso8601String()) as String,
|
||||
updatedAt: (data['updated_at'] ?? data['updatedAt'] ?? DateTime.now().toIso8601String()) as String,
|
||||
deviceId: data['user_id'] != null ? data['user_id'].toString() : 'api-$id',
|
||||
createdAt:
|
||||
(data['created_at'] ??
|
||||
data['createdAt'] ??
|
||||
DateTime.now().toIso8601String())
|
||||
as String,
|
||||
updatedAt:
|
||||
(data['updated_at'] ??
|
||||
data['updatedAt'] ??
|
||||
DateTime.now().toIso8601String())
|
||||
as String,
|
||||
deviceId: data['user_id'] != null
|
||||
? data['user_id'].toString()
|
||||
: 'api-$id',
|
||||
notes: data['description'] as String?,
|
||||
address: data['address'] as String?,
|
||||
submittedBy: data['user_name'] as String?,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Design tokens and themes for FixMate (Civic Pro Minimal)
|
||||
/// Design tokens and themes for CityPulse (Civic Pro Minimal)
|
||||
class AppColors {
|
||||
// Primary civic colors
|
||||
static const Color civicBlue = Color(0xFF2563EB);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: citypulse
|
||||
description: "FixMate - A citizen reporting app for community issues"
|
||||
description: "CityPulse - A citizen reporting app for community issues"
|
||||
# The following line prevents the package from being accidentally published to
|
||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
|
||||
@@ -6,7 +6,7 @@ import 'package:citypulse/app.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('App builds MaterialApp', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(const FixMateApp());
|
||||
await tester.pumpWidget(const CityPulseApp());
|
||||
expect(find.byType(MaterialApp), findsOneWidget);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user