diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..863842d --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,24 @@ +name: Deploy to GitHub Pages + +on: + push: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: 'stable' + + - name: Run Makefile Deploy + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: make deploy OUTPUT=rostrem-website \ No newline at end of file diff --git a/.metadata b/.metadata index 20eb09c..9cef17b 100644 --- a/.metadata +++ b/.metadata @@ -1,11 +1,11 @@ # This file tracks properties of this Flutter project. # Used by Flutter tool to assess capabilities and perform upgrades etc. # -# This file should be version controlled. +# This file should be version controlled and should not be manually edited. version: - revision: 62bd79521d8d007524e351747471ba66696fc2d4 - channel: stable + revision: "b0850beeb25f6d5b10426284f506557f66181b36" + channel: "stable" project_type: app @@ -13,26 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: 62bd79521d8d007524e351747471ba66696fc2d4 - base_revision: 62bd79521d8d007524e351747471ba66696fc2d4 - - platform: android - create_revision: 62bd79521d8d007524e351747471ba66696fc2d4 - base_revision: 62bd79521d8d007524e351747471ba66696fc2d4 - - platform: ios - create_revision: 62bd79521d8d007524e351747471ba66696fc2d4 - base_revision: 62bd79521d8d007524e351747471ba66696fc2d4 - - platform: linux - create_revision: 62bd79521d8d007524e351747471ba66696fc2d4 - base_revision: 62bd79521d8d007524e351747471ba66696fc2d4 - - platform: macos - create_revision: 62bd79521d8d007524e351747471ba66696fc2d4 - base_revision: 62bd79521d8d007524e351747471ba66696fc2d4 + create_revision: b0850beeb25f6d5b10426284f506557f66181b36 + base_revision: b0850beeb25f6d5b10426284f506557f66181b36 - platform: web - create_revision: 62bd79521d8d007524e351747471ba66696fc2d4 - base_revision: 62bd79521d8d007524e351747471ba66696fc2d4 - - platform: windows - create_revision: 62bd79521d8d007524e351747471ba66696fc2d4 - base_revision: 62bd79521d8d007524e351747471ba66696fc2d4 + create_revision: b0850beeb25f6d5b10426284f506557f66181b36 + base_revision: b0850beeb25f6d5b10426284f506557f66181b36 # User provided section diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d63d92e --- /dev/null +++ b/Makefile @@ -0,0 +1,39 @@ +# Makefile for deploying the Flutter web projects to GitHub +# from https://codewithandrea.com/articles/flutter-web-github-pages/ + +BASE_HREF = /$(OUTPUT)/ +GITHUB_USER = devsticks +GITHUB_REPO = https://github.com/$(GITHUB_USER)/$(OUTPUT) +BUILD_VERSION := $(shell grep 'version:' pubspec.yaml | awk '{print $$2}') + +# Deploy the Flutter web project to GitHub +deploy: +ifndef OUTPUT + $(error OUTPUT is not set. Usage: make deploy OUTPUT=) +endif + + @echo "Clean existing repository" + flutter clean + + @echo "Getting packages..." + flutter pub get + + @echo "Generating the web folder..." + flutter create . --platform web + + @echo "Building for web..." + flutter build web --base-href $(BASE_HREF) --release + + @echo "Deploying to git repository" + cd build/web && \ + git init && \ + git add . && \ + git commit -m "Deploy Version $(BUILD_VERSION)" && \ + git branch -M main && \ + git remote add origin $(GITHUB_REPO) && \ + git push -u -f origin main + + @echo "✅ Finished deploy: $(GITHUB_REPO)" + @echo "🚀 Flutter web URL: https://$(GITHUB_USER).github.io/$(OUTPUT)/" + +.PHONY: deploy \ No newline at end of file diff --git a/lib/screens/roster_home_page.dart b/lib/screens/roster_home_page.dart index 481c1d2..3d54a64 100644 --- a/lib/screens/roster_home_page.dart +++ b/lib/screens/roster_home_page.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:rostrem/models/assignment_generator.dart'; -import 'package:rostrem/widgets/header_text.dart'; import 'package:rostrem/widgets/loading_overlay.dart'; import '../models/doctor.dart'; import '../models/shift.dart'; @@ -37,6 +36,8 @@ class RosterHomePageState extends State { // year and month for next month int year = DateTime.now().add(const Duration(days: 31)).year; int month = DateTime.now().add(const Duration(days: 31)).month; + late TextEditingController yearController; + late TextEditingController monthController; final List _loadingMessages = [ 'Preparing your new roster...', @@ -78,6 +79,8 @@ class RosterHomePageState extends State { @override void initState() { super.initState(); + yearController = TextEditingController(text: year.toString()); + monthController = TextEditingController(text: month.toString()); _initializeDoctors(); _initializeShifts(); _initializeRoster(); @@ -86,6 +89,8 @@ class RosterHomePageState extends State { @override void dispose() { _postCallBeforeLeaveValueNotifier.dispose(); + yearController.dispose(); + monthController.dispose(); super.dispose(); } @@ -250,35 +255,86 @@ class RosterHomePageState extends State { return Scaffold( appBar: AppBar( title: const Text('Doctors\' Overtime Call Roster'), - actions: [ - // Input for the year and month - Row( - children: [ - const Text('Year: '), - SizedBox( - width: 60, - child: TextField( - controller: TextEditingController(text: year.toString()), - keyboardType: TextInputType.number, - onChanged: (value) { - year = int.tryParse(value) ?? year; - _initializeShifts(); - }, - ), - ), - const Text('Month: '), - SizedBox( - width: 60, - child: TextField( - controller: TextEditingController(text: month.toString()), - keyboardType: TextInputType.number, - onChanged: (value) { - month = int.tryParse(value) ?? month; - _initializeShifts(); + actions: [ + LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + if (MediaQuery.of(context).size.width > 600) { + // Adjust threshold as needed + return Row( + children: [ + const Text('Year: '), + SizedBox( + width: 60, + child: TextField( + controller: yearController, + keyboardType: TextInputType.number, + onChanged: (value) { + year = int.tryParse(value) ?? year; + _initializeShifts(); + }, + ), + ), + const Text('Month: '), + SizedBox( + width: 60, + child: TextField( + controller: monthController, + keyboardType: TextInputType.number, + onChanged: (value) { + month = int.tryParse(value) ?? month; + _initializeShifts(); + }, + ), + ), + ], + ); + } else { + return IconButton( + icon: const Icon(Icons.settings), + onPressed: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text("Set Month"), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: yearController, + keyboardType: TextInputType.number, + decoration: + const InputDecoration(labelText: "Year"), + onChanged: (value) { + year = int.tryParse(value) ?? year; + _initializeShifts(); + }, + ), + TextField( + controller: monthController, + keyboardType: TextInputType.number, + decoration: + const InputDecoration(labelText: "Month"), + onChanged: (value) { + month = int.tryParse(value) ?? month; + _initializeShifts(); + }, + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('Close'), + ), + ], + ); + }, + ); }, - ), - ), - ], + ); + } + }, ), IconButton( icon: const Icon(Icons.refresh), @@ -289,84 +345,75 @@ class RosterHomePageState extends State { ), ], ), - body: Stack( + body: Row( children: [ - Row( - children: [ - Expanded( - flex: 1, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const Text( - 'Roster', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 20.0, - ), - ), - const SizedBox(height: 8.0), - Expanded( - child: RosterDisplay( - shifts: shifts, - isPublicHoliday: _isPublicHoliday, - ), - ), - ], + Expanded( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Text( + 'Roster', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 20.0, + ), ), - ), + const SizedBox(height: 8.0), + Expanded( + child: RosterDisplay( + shifts: shifts, + isPublicHoliday: _isPublicHoliday, + ), + ), + ], ), - Expanded( - flex: 1, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const Text( - 'Summary', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 20.0, - ), - ), - const SizedBox(height: 8.0), - Expanded( - flex: 1, - child: DoctorsSummaryTable(doctors: doctors), - ), - const SizedBox(height: 16), - const Text( - 'Leave Management', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 20.0, - ), + ), + ), + Expanded( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Text( + 'Summary', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 20.0, ), - const SizedBox(height: 8.0), - Expanded( - flex: 1, - child: LeaveManagement( - doctors: doctors, - onAddLeave: _addLeaveDays, - onRemoveLeave: _removeLeaveBlock, - postCallBeforeLeaveValueNotifier: - _postCallBeforeLeaveValueNotifier, - ), + ), + const SizedBox(height: 8.0), + DoctorsSummaryTable(doctors: doctors), + const SizedBox(height: 16), + const Text( + 'Leave Management', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 20.0, ), - ], - ), + ), + const SizedBox(height: 8.0), + LeaveManagement( + doctors: doctors, + onAddLeave: _addLeaveDays, + onRemoveLeave: _removeLeaveBlock, + postCallBeforeLeaveValueNotifier: + _postCallBeforeLeaveValueNotifier, + ), + ], ), ), - ], + ), ), ], ), floatingActionButton: FloatingActionButton( child: const Icon(Icons.download), onPressed: () { + // Assuming 'roster' is correctly defined somewhere in your code roster.downloadAsCsv(context); }, tooltip: 'Download as Spreadsheet (CSV)', diff --git a/lib/widgets/doctors_summary_table.dart b/lib/widgets/doctors_summary_table.dart index 5a6572d..c0f2278 100644 --- a/lib/widgets/doctors_summary_table.dart +++ b/lib/widgets/doctors_summary_table.dart @@ -8,8 +8,8 @@ class DoctorsSummaryTable extends StatelessWidget { @override Widget build(BuildContext context) { - return SingleChildScrollView( - scrollDirection: Axis.vertical, + return Container( + // scrollDirection: Axis.vertical, child: DataTable( columnSpacing: 10.0, // Adjust the column spacing as needed columns: const [ diff --git a/lib/widgets/leave_management.dart b/lib/widgets/leave_management.dart index c8c1c21..d9f2d32 100644 --- a/lib/widgets/leave_management.dart +++ b/lib/widgets/leave_management.dart @@ -91,7 +91,6 @@ class _LeaveManagementState extends State { children: [ Row( children: [ - // Check box for whether a doctor should be post-call the day before they start their leave Checkbox( value: _postCallBeforeLeave, onChanged: (value) { @@ -100,7 +99,7 @@ class _LeaveManagementState extends State { widget.postCallBeforeLeaveValueNotifier.value = value; }); }), - const Expanded(child: Text('Post-call the day before leave')), + Expanded(child: Text('Post-call the day before leave')), ], ), LayoutBuilder( @@ -122,27 +121,28 @@ class _LeaveManagementState extends State { } }, ), - Expanded( - child: ListView( - children: widget.doctors.expand((doctor) { - List> leaveBlocks = - _getLeaveBlocks(doctor.leaveDays); - return leaveBlocks.map((block) { - DateTime startDate = block.first; - DateTime endDate = block.last; - return ListTile( - title: Text( - '${doctor.name}: ${startDate.toIso8601String().split('T')[0]} - ${endDate.toIso8601String().split('T')[0]}'), - trailing: IconButton( - icon: const Icon(Icons.delete), - onPressed: () { - widget.onRemoveLeave(doctor, block); - }, - ), - ); - }).toList(); - }).toList(), - ), + ListView( + shrinkWrap: true, // Allows ListView to take minimum needed space + physics: + NeverScrollableScrollPhysics(), // Disables scrolling within the ListView + children: widget.doctors.expand((doctor) { + List> leaveBlocks = + _getLeaveBlocks(doctor.leaveDays); + return leaveBlocks.map((block) { + DateTime startDate = block.first; + DateTime endDate = block.last; + return ListTile( + title: Text( + '${doctor.name}: ${startDate.toIso8601String().split('T')[0]} - ${endDate.toIso8601String().split('T')[0]}'), + trailing: IconButton( + icon: const Icon(Icons.delete), + onPressed: () { + widget.onRemoveLeave(doctor, block); + }, + ), + ); + }).toList(); + }).toList(), ), ], );