Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/README.md → app/file_structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ lib/
│ ├── cabinet_model.dart
│ ├── extensions.dart
│ ├── intake_model.dart
│ ├── medicine_category.dart
│ └── profile_model.dart
├── pages/
│ ├── about_page.dart
Expand Down
6 changes: 4 additions & 2 deletions app/lib/db/README_DB.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ ____________________________________________
cabinet structure:
id --> integer AUTOINCREMENTED
name --> text not null
dosage --> int not null
dosage --> text not null
time --> time not null
currstock --> int not null
initstock --> int not null
priority --> int not null
category --> text not null (default 'tablet')
unit --> text not null (default 'pills')
____________________________________________
create new medicine:
createmed(cabinet 'model')
Expand All @@ -44,4 +46,4 @@ create new log:
createlog(intake 'model')

read log:
readintakelog)
readintakelog()
96 changes: 39 additions & 57 deletions app/lib/db/cabinet_db.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ class DatabaseHelper {
final dbPath = await getDatabasesPath();
final path = join(dbPath, filePath);

return await openDatabase(path, version: 1, onCreate: _createDB);
return await openDatabase(
path,
version: 2,
onCreate: _createDB,
onUpgrade: _upgradeDB,
);
}

Future _createDB(Database db, int version) async {
Expand All @@ -34,40 +39,47 @@ CREATE TABLE cabinet (
time $textType,
currstock $integerType,
initstock $integerType,
priority $integerType
priority $integerType,
category $textType DEFAULT 'tablet',
unit $textType DEFAULT 'pills'
)
''');
}

Future _upgradeDB(Database db, int oldVersion, int newVersion) async {
if (oldVersion < 2) {
await db.execute(
"ALTER TABLE cabinet ADD COLUMN category TEXT NOT NULL DEFAULT 'tablet'",
);
await db.execute(
"ALTER TABLE cabinet ADD COLUMN unit TEXT NOT NULL DEFAULT 'pills'",
);
}
}

static const String _ensureTable = '''
CREATE TABLE IF NOT EXISTS cabinet (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
dosage TEXT NOT NULL,
time TEXT NOT NULL,
currstock INTEGER NOT NULL,
initstock INTEGER NOT NULL,
priority INTEGER NOT NULL,
category TEXT NOT NULL DEFAULT 'tablet',
unit TEXT NOT NULL DEFAULT 'pills'
)
''';

Future<int> createMedicine(Cabinet cabinet) async {
final db = await instance.database;
await db.execute('''
CREATE TABLE IF NOT EXISTS cabinet (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
dosage TEXT NOT NULL,
time TEXT NOT NULL,
currstock INTEGER NOT NULL,
initstock INTEGER NOT NULL,
priority INTEGER NOT NULL
)
''');
await db.execute(_ensureTable);
return await db.insert('cabinet', cabinet.toMap());
}

Future<Cabinet?> readMedicine(int id) async {
final db = await instance.database;
await db.execute('''
CREATE TABLE IF NOT EXISTS cabinet (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
dosage TEXT NOT NULL,
time TEXT NOT NULL,
currstock INTEGER NOT NULL,
initstock INTEGER NOT NULL,
priority INTEGER NOT NULL
)
''');
await db.execute(_ensureTable);
final maps = await db.query('cabinet', where: 'id = ?', whereArgs: [id]);

if (maps.isNotEmpty) {
Expand All @@ -78,35 +90,15 @@ CREATE TABLE cabinet (

Future<List<Cabinet>> readAllMedicines() async {
final db = await instance.database;
await db.execute('''
CREATE TABLE IF NOT EXISTS cabinet (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
dosage TEXT NOT NULL,
time TEXT NOT NULL,
currstock INTEGER NOT NULL,
initstock INTEGER NOT NULL,
priority INTEGER NOT NULL
)
''');
await db.execute(_ensureTable);
const orderBy = 'time ASC';
final result = await db.query('cabinet', orderBy: orderBy);
return result.map((json) => Cabinet.fromMap(json)).toList();
}

Future<int> updateMedicine(Cabinet medicine) async {
final db = await instance.database;
await db.execute('''
CREATE TABLE IF NOT EXISTS cabinet (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
dosage TEXT NOT NULL,
time TEXT NOT NULL,
currstock INTEGER NOT NULL,
initstock INTEGER NOT NULL,
priority INTEGER NOT NULL
)
''');
await db.execute(_ensureTable);
return await db.update(
'cabinet',
medicine.toMap(),
Expand All @@ -117,17 +109,7 @@ CREATE TABLE cabinet (

Future<int> deleteMedicine(int id) async {
final db = await instance.database;
await db.execute('''
CREATE TABLE IF NOT EXISTS cabinet (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
dosage TEXT NOT NULL,
time TEXT NOT NULL,
currstock INTEGER NOT NULL,
initstock INTEGER NOT NULL,
priority INTEGER NOT NULL
)
''');
await db.execute(_ensureTable);
return await db.delete('cabinet', where: 'id = ?', whereArgs: [id]);
}
}
87 changes: 84 additions & 3 deletions app/lib/dose.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:dose/pages/home_page.dart';
import 'package:dose/pages/analytics_page.dart';
import 'package:dose/pages/profile_page.dart';
import 'package:dose/pages/add_menu_page.dart';
import 'package:dose/models/medicine_category.dart';

class Dose extends StatefulWidget {
const Dose({super.key});
Expand All @@ -16,6 +17,7 @@ class _DoseState extends State<Dose> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();

Key _homeKey = UniqueKey();
MedicineCategory? _selectedCategoryForNewMed;

List<Widget> _pages() => [
HomePage(key: _homeKey),
Expand Down Expand Up @@ -53,6 +55,86 @@ class _DoseState extends State<Dose> {
super.dispose();
}

void _showCategoryBottomSheet(BuildContext context) {
showModalBottomSheet(
context: context,
backgroundColor: Theme.of(context).colorScheme.surface,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
),
builder: (context) {
final cs = Theme.of(context).colorScheme;
return Padding(
padding: EdgeInsets.only(
left: 24.0,
right: 24.0,
top: 24.0,
bottom: MediaQuery.of(context).padding.bottom + 24.0,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Add Medicine',
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold,
color: cs.onSurface,
),
),
const SizedBox(height: 20),
Flexible(
child: ListView.separated(
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
itemCount: MedicineCategory.values.length,
separatorBuilder: (context, index) => const SizedBox(height: 12),
itemBuilder: (context, index) {
final cat = MedicineCategory.values[index];
return InkWell(
onTap: () {
Navigator.pop(context);
setState(() {
_selectedCategoryForNewMed = cat;
});
_scaffoldKey.currentState?.openEndDrawer();
},
borderRadius: BorderRadius.circular(16),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
decoration: BoxDecoration(
border: Border.all(
color: cs.outlineVariant.withValues(alpha: 0.5),
width: 1.5),
borderRadius: BorderRadius.circular(16),
color: cs.surfaceContainerLowest,
),
child: Row(
children: [
Icon(cat.icon, size: 28, color: cs.onSurfaceVariant),
const SizedBox(width: 16),
Text(
cat.label,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
color: cs.onSurface,
),
),
],
),
),
);
},
),
),
],
),
);
},
);
}

@override
Widget build(BuildContext context) {
return Scaffold(
Expand All @@ -78,6 +160,7 @@ class _DoseState extends State<Dose> {
endDrawer: Drawer(
width: MediaQuery.of(context).size.width,
child: AddMedicineMenu(
initialCategory: _selectedCategoryForNewMed,
onSave: () {
setState(() {
_homeKey = UniqueKey();
Expand All @@ -87,9 +170,7 @@ class _DoseState extends State<Dose> {
),
floatingActionButton: _selectedIndex == 0
? FloatingActionButton(
onPressed: () {
_scaffoldKey.currentState?.openEndDrawer();
},
onPressed: () => _showCategoryBottomSheet(context),
child: const Icon(Icons.add),
)
: null,
Expand Down
8 changes: 8 additions & 0 deletions app/lib/models/cabinet_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ class Cabinet {
final int initstock;
final int currstock;
final int priority;
final String category;
final String unit;

Cabinet({
this.id,
Expand All @@ -15,6 +17,8 @@ class Cabinet {
required this.initstock,
required this.currstock,
required this.priority,
this.category = 'tablet',
this.unit = 'pills',
});

Map<String, dynamic> toMap() {
Expand All @@ -26,6 +30,8 @@ class Cabinet {
'initstock': initstock,
'currstock': currstock,
'priority': priority,
'category': category,
'unit': unit,
};
}

Expand All @@ -38,6 +44,8 @@ class Cabinet {
initstock: map['initstock'],
currstock: map['currstock'],
priority: map['priority'],
category: map['category'] ?? 'tablet',
unit: map['unit'] ?? 'pills',
);
}
}
5 changes: 4 additions & 1 deletion app/lib/models/extensions.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
extension DoseString on String {
/// Formats a raw dosage value with its unit for display.
/// Example: "2" with unit "pills" → displayed via "$dosage $unit" at call site.
/// This getter strips any legacy 'pills/spoons' suffix left from old data.
String get formattedDosage {
return replaceAll('mg', 'pills/spoons');
return replaceAll(' pills/spoons', '').trim();
}
}
55 changes: 55 additions & 0 deletions app/lib/models/medicine_category.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import 'package:flutter/material.dart';

enum MedicineCategory {
tablet(
label: 'Tablet',
icon: Icons.medication,
units: ['pills', 'mg'],
),
capsule(
label: 'Capsule',
icon: Icons.medication_outlined,
units: ['capsules', 'mg'],
),
liquid(
label: 'Liquid',
icon: Icons.water_drop,
units: ['mL', 'spoons'],
),
injection(
label: 'Injection',
icon: Icons.vaccines,
units: ['mL', 'units'],
),
topical(
label: 'Topical',
icon: Icons.back_hand,
units: ['applications', 'mg'],
),
inhaler(
label: 'Inhaler',
icon: Icons.air,
units: ['puffs', 'mg'],
);

final String label;
final IconData icon;
final List<String> units;

const MedicineCategory({
required this.label,
required this.icon,
required this.units,
});

String get defaultUnit => units.first;

/// Look up a category by its stored string name.
/// Returns [MedicineCategory.tablet] for unknown values.
static MedicineCategory fromString(String value) {
for (final cat in MedicineCategory.values) {
if (cat.name == value) return cat;
}
return MedicineCategory.tablet;
}
}
Loading
Loading