Flutter Workgroup Meetings

Prep WorkgroupMeetings API

Laravel api/WorkgroupMeetingController.php:

<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Workgroup;
use App\Models\WorkgroupMeeting;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class WorkgroupMeetingController extends Controller
{
    public function index($workgroupId)
    {
        $user = auth()->user();
        $workgroups = Workgroup::where('mngr_email', $user->email)
            ->get();

        // Check if the requested workgroup belongs to the authenticated user
        $requestedWorkgroup = $workgroups->firstWhere('id', $workgroupId);
        if (!$requestedWorkgroup) {
            return response()->json(['error' => 'Unauthorized'], 403);
        }

        $workgroupMeetings = WorkgroupMeeting::where('workgroup_id', $workgroupId)->get();

        return response()->json($workgroupMeetings);
    }
}

Laravel routes/api.php:

...
use App\Http\Controllers\Api\WorkgroupMeetingController;
Route::middleware('auth:sanctum')->group(function () {
    Route::get('workgroup/{workgroup_id}/meeting', [WorkgroupMeetingController::class, 'index']);
});
...

Edit WorkgroupMeetings Screen

File-> lib/modules/workgroupmeetings.dart

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:hive/hive.dart';

class WorkgroupMeetingsScreen extends StatefulWidget {
  final dynamic workgroup;

  const WorkgroupMeetingsScreen({Key? key, required this.workgroup})
      : super(key: key);

  @override
  _WorkgroupMeetingsScreenState createState() =>
      _WorkgroupMeetingsScreenState();
}

class _WorkgroupMeetingsScreenState extends State<WorkgroupMeetingsScreen> {
  List<dynamic> _meetings = [];
  final Box _boxUser = Hive.box("user");
  bool _isLoading = true;

  @override
  void initState() {
    super.initState();
    _fetchMeetings();
  }

  Future<void> _fetchMeetings() async {
    final url =
        'https://demo.razzi.my/spotnet/public/api/workgroup/${widget.workgroup['id']}/meeting';
    final authToken = await _boxUser.get('user_token');

    try {
      final response = await http.get(
        Uri.parse(url),
        headers: {
          'Authorization': 'Bearer $authToken',
          'Content-Type': 'application/json',
        },
      );

      if (response.statusCode == 200) {
        final data = jsonDecode(response.body) as List;
        setState(() {
          _meetings = data;
          _isLoading = false;
        });
      } else {
        setState(() {
          _isLoading = false;
        });
        // Handle error
        print('Error fetching meetings: ${response.statusCode}');
      }
    } catch (e) {
      setState(() {
        _isLoading = false;
      });
      // Handle network error
      print('Error fetching meetings: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Meetings for ${widget.workgroup['name']}'),
        actions: [
          ElevatedButton(
            onPressed: () {
              // Navigate to the "Create Meeting" screen
              Navigator.push(
                  context,
                  MaterialPageRoute(
                      builder: (_) => CreateMeetingScreen(
                            workgroup: widget.workgroup,
                          ))).then((_) {
                // This block runs when you have come back to the 1st Page from 2nd.
                _fetchMeetings();
              });
            },
            style: ElevatedButton.styleFrom(
              backgroundColor: Color.fromARGB(
                  255, 122, 192, 245), // Set the background color to red
              foregroundColor: Colors.white, // Set the font color to white
              padding:
                  const EdgeInsets.all(8.0), // Adjust the padding as needed
            ),
            child: const Icon(Icons.add),
          ),
        ],
      ),
      body: _isLoading
          ? const Center(child: CircularProgressIndicator())
          : Padding(
              padding: const EdgeInsets.symmetric(horizontal: 16.0),
              child: ListView.builder(
                itemCount: _meetings.length,
                itemBuilder: (context, index) {
                  final meeting = _meetings[index];
                  return ListTile(
                    leading: IconButton(
                      icon: const Icon(Icons.info),
                      onPressed: () {
                        Navigator.push(
                                context,
                                MaterialPageRoute(
                                    builder: (_) =>
                                        MeetingDetailsScreen(meeting: meeting)))
                            .then((_) {
                          // This block runs when you have come back to the 1st Page from 2nd.
                          _fetchMeetings();
                        });
                      },
                    ),
                    title: Text('Description: ${meeting['desn']}'),
                    subtitle: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text('Reference: ${meeting['refn']}'),
                        Text('Location: ${meeting['locn']}'),
                        Text(
                            'Start Date: ${DateTime.fromMillisecondsSinceEpoch(meeting['estartdate'] * 1000).toString()}'),
                        Text('Attendees: ${meeting['attd']}'),
                      ],
                    ),
                    trailing: IconButton(
                      icon: const Icon(Icons.chevron_right),
                      onPressed: () {
                        Navigator.push(
                          context,
                          MaterialPageRoute(
                            builder: (_) =>
                                MeetingDetailsScreen(meeting: meeting),
                          ),
                        ).then((_) {
                          // This block runs when you have come back to the 1st Page from 2nd.
                          _fetchMeetings();
                        });
                      },
                    ),
                  );
                },
              ),
            ),
    );
  }
}

class CreateMeetingScreen extends StatefulWidget {
  final dynamic workgroup;

  const CreateMeetingScreen({Key? key, required this.workgroup})
      : super(key: key);

  @override
  _CreateMeetingScreenState createState() => _CreateMeetingScreenState();
}

class _CreateMeetingScreenState extends State<CreateMeetingScreen> {
  // Implement the logic for creating a new meeting
  @override
  dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}

class MeetingDetailsScreen extends StatefulWidget {
  final dynamic meeting;

  const MeetingDetailsScreen({Key? key, required this.meeting})
      : super(key: key);

  @override
  _MeetingDetailsScreenState createState() => _MeetingDetailsScreenState();
}

class _MeetingDetailsScreenState extends State<MeetingDetailsScreen> {
  // Implement the logic for displaying the details of a meeting
  @override
  dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}

At the moment MeetingDetailsScreen and CreateMeetingScreen are just stub classes.