Avance del programa

This commit is contained in:
2026-05-23 07:39:29 -06:00
parent c6a1a67469
commit ebce0badde
6 changed files with 1098 additions and 430 deletions

View File

@@ -12,7 +12,8 @@ class DbHelper {
static Future<Database> _initDb() async {
final path = join(await getDatabasesPath(), 'celaya_v3.db');
return openDatabase(path, version: 1, onCreate: _onCreate);
return openDatabase(path, version: 2,
onCreate: _onCreate, onUpgrade: _onUpgrade);
}
static Future<void> _onCreate(Database db, int v) async {
@@ -76,6 +77,28 @@ class DbHelper {
user_id INTEGER PRIMARY KEY, activo INTEGER DEFAULT 1,
notas TEXT)''');
// NOTAS DE ADMIN SOBRE REPORTES
await db.execute('''CREATE TABLE reporte_notas(
id INTEGER PRIMARY KEY AUTOINCREMENT,
reporte_id INTEGER NOT NULL, admin_id INTEGER NOT NULL,
admin_nombre TEXT NOT NULL, nota TEXT NOT NULL,
fecha TEXT NOT NULL)''');
// EVIDENCIAS DEL ADMIN EN REPORTES
await db.execute('''CREATE TABLE reporte_evidencias(
id INTEGER PRIMARY KEY AUTOINCREMENT,
reporte_id INTEGER NOT NULL, admin_id INTEGER NOT NULL,
pie_imagen TEXT NOT NULL, foto_path TEXT,
fecha TEXT NOT NULL)''');
// CHAT POR REPORTE (admin <-> ciudadano)
await db.execute('''CREATE TABLE reporte_chat(
id INTEGER PRIMARY KEY AUTOINCREMENT,
reporte_id INTEGER NOT NULL, user_id INTEGER NOT NULL,
rol TEXT NOT NULL,
mensaje TEXT NOT NULL, fecha TEXT NOT NULL,
leido INTEGER DEFAULT 0)''');
await db.insert('users', {'nombre':'Administrador','email':'admin@celaya.gob.mx',
'password':'admin123','rol':'ADMINISTRADOR'});
final conductorId = await db.insert('users', {'nombre':'Juan Conductor',
@@ -83,6 +106,47 @@ class DbHelper {
await db.insert('user_meta', {'user_id': conductorId, 'activo': 1});
}
// Migración incremental — se ejecuta al actualizar la app
static Future<void> _onUpgrade(Database db, int oldV, int newV) async {
// Agregar columnas/tablas que pueden faltar en instalaciones anteriores
final helpers = [
// foto_path en reportes
"ALTER TABLE reportes ADD COLUMN foto_path TEXT",
// Tablas nuevas (IF NOT EXISTS para no fallar si ya existen)
'''CREATE TABLE IF NOT EXISTS notification_history(
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER, route_id TEXT NOT NULL,
event_type TEXT NOT NULL, title TEXT NOT NULL,
body TEXT NOT NULL, fecha TEXT NOT NULL,
leida INTEGER DEFAULT 0)''',
'''CREATE TABLE IF NOT EXISTS user_meta(
user_id INTEGER PRIMARY KEY, activo INTEGER DEFAULT 1, notas TEXT)''',
'''CREATE TABLE IF NOT EXISTS reporte_notas(
id INTEGER PRIMARY KEY AUTOINCREMENT,
reporte_id INTEGER NOT NULL, admin_id INTEGER NOT NULL,
admin_nombre TEXT NOT NULL, nota TEXT NOT NULL, fecha TEXT NOT NULL)''',
'''CREATE TABLE IF NOT EXISTS reporte_evidencias(
id INTEGER PRIMARY KEY AUTOINCREMENT,
reporte_id INTEGER NOT NULL, admin_id INTEGER NOT NULL,
pie_imagen TEXT NOT NULL, foto_path TEXT, fecha TEXT NOT NULL)''',
'''CREATE TABLE IF NOT EXISTS reporte_chat(
id INTEGER PRIMARY KEY AUTOINCREMENT,
reporte_id INTEGER NOT NULL, user_id INTEGER NOT NULL,
rol TEXT NOT NULL, mensaje TEXT NOT NULL,
fecha TEXT NOT NULL, leido INTEGER DEFAULT 0)''',
'''CREATE TABLE IF NOT EXISTS route_definitions(
id INTEGER PRIMARY KEY AUTOINCREMENT,
route_id TEXT UNIQUE NOT NULL, nombre TEXT NOT NULL,
dias TEXT NOT NULL, hora_inicio TEXT NOT NULL,
hora_fin TEXT NOT NULL, turno TEXT NOT NULL,
colonias TEXT NOT NULL, activa INTEGER DEFAULT 1)''',
];
for (final sql in helpers) {
try { await db.execute(sql); } catch (_) {}
// Ignorar errores (ej. columna ya existe)
}
}
// ── USERS ────────────────────────────────────────────────────────────────
static Future<int> insertUser(UserModel u) async =>
(await database).insert('users', u.toMap(), conflictAlgorithm: ConflictAlgorithm.abort);
@@ -385,4 +449,50 @@ class DbHelper {
FROM reviews
GROUP BY semana ORDER BY semana DESC LIMIT 8''');
}
// ── NOTAS DE ADMIN ───────────────────────────────────────────────────────
static Future<void> insertReporteNota(int reporteId, int adminId, String adminNombre, String nota) async =>
(await database).insert('reporte_notas', {
'reporte_id': reporteId, 'admin_id': adminId,
'admin_nombre': adminNombre, 'nota': nota,
'fecha': DateTime.now().toIso8601String(),
});
static Future<List<Map<String, dynamic>>> getReporteNotas(int reporteId) async =>
(await database).query('reporte_notas',
where: 'reporte_id=?', whereArgs: [reporteId], orderBy: 'fecha ASC');
// ── EVIDENCIAS DEL ADMIN ─────────────────────────────────────────────────
static Future<void> insertReporteEvidencia(int reporteId, int adminId, String pie, String? fotoPath) async =>
(await database).insert('reporte_evidencias', {
'reporte_id': reporteId, 'admin_id': adminId,
'pie_imagen': pie, 'foto_path': fotoPath,
'fecha': DateTime.now().toIso8601String(),
});
static Future<List<Map<String, dynamic>>> getReporteEvidencias(int reporteId) async =>
(await database).query('reporte_evidencias',
where: 'reporte_id=?', whereArgs: [reporteId], orderBy: 'fecha ASC');
// ── CHAT POR REPORTE ─────────────────────────────────────────────────────
static Future<void> insertChatMsg(int reporteId, int userId, String rol, String mensaje) async =>
(await database).insert('reporte_chat', {
'reporte_id': reporteId, 'user_id': userId, 'rol': rol,
'mensaje': mensaje, 'fecha': DateTime.now().toIso8601String(), 'leido': 0,
});
static Future<List<Map<String, dynamic>>> getChatMsgs(int reporteId) async =>
(await database).query('reporte_chat',
where: 'reporte_id=?', whereArgs: [reporteId], orderBy: 'fecha ASC');
static Future<int> getChatUnread(int reporteId, String rolLector) async {
final res = await (await database).rawQuery(
"SELECT COUNT(*) as c FROM reporte_chat WHERE reporte_id=? AND rol!=? AND leido=0",
[reporteId, rolLector]);
return (res.first['c'] as int? ?? 0);
}
static Future<void> markChatRead(int reporteId, String rolLector) async =>
(await database).update('reporte_chat', {'leido': 1},
where: 'reporte_id=? AND rol!=?', whereArgs: [reporteId, rolLector]);
}