crée la page annonce detail qui affiche les détails de l'annonce, son auteur et les avis de l'annonce : import 'package:flutter/material.dart';
import 'package:sae_mobile/models/Builder.dart' as builder_model;
import 'package:sae_mobile/views/annonceTile.dart';
import 'package:sae_mobile/models/annonce.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:sae_mobile/views/annonceDetail.dart';

final SupabaseClient supabaseClient = Supabase.instance.client;

class AnnoncesView extends StatefulWidget {
  final String categoryId;
  final String categoryName;
  final bool isUserAnnonces;
  final bool isReponduAnnonces;

  const AnnoncesView(
      {Key? key,
      required this.categoryId,
      required this.categoryName,
      this.isUserAnnonces = false,
      this.isReponduAnnonces = false})
      : super(key: key);

  @override
  State<AnnoncesView> createState() => _AnnoncesViewState();
}

class _AnnoncesViewState extends State<AnnoncesView> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.categoryName),
      ),
      body: FutureBuilder(
        future: widget.isUserAnnonces
            ? builder_model.Builder.buildAnnoncesLocalUtilisateur(
                supabaseClient.auth.currentUser!.id,
              )
            : widget.isReponduAnnonces
                ? builder_model.Builder.buildAnnoncesDistantRepondu(
                    supabaseClient.auth.currentUser!.id,
                  )
                : builder_model.Builder.buildAnnoncesDistantByType(
                    widget.categoryId,
                  ),
        builder: (context, AsyncSnapshot<List<Annonce>> snapshot) {
          if (snapshot.hasError) {
            return Center(child: Text('Error: ${snapshot.error}'));
          } else {
            if (snapshot.data == null || snapshot.data!.isEmpty) {
              return Center(child: Text("Pas d'annonces"));
            } else {
              return ListView.builder(
                itemCount: snapshot.data!.length,
                itemBuilder: (context, index) {
                  final annonce = snapshot.data![index];
                  return GestureDetector(
                    onTap: () {
                      Navigator.push(
                        context,
                        MaterialPageRoute(
                          builder: (context) =>
                              DetailAnnoncePage(annonce: annonce),
                        ),
                      );
                    },
                    child: Container(
                      height: 200,
                      child: Card(
                        margin: EdgeInsets.all(10.0),
                        child: Row(
                          children: <Widget>[
                            ClipRRect(
                              borderRadius: BorderRadius.circular(10.0),
                              child: Image.asset(
                                'images/box_base.png',
                                width: 100,
                                height: 100,
                                fit: BoxFit.cover,
                              ),
                            ),
                            Expanded(
                              child: AnnonceTile(annonce: annonce),
                            ),
                          ],
                        ),
                      ),
                    ),
                  );
                },
              );
            }
          }
        },
      ),
    );
  }
}
import 'package:sae_mobile/models/Objet.dart';
import 'package:sae_mobile/models/TypeAnnonce.dart';
import 'package:sae_mobile/models/annonce.dart';
import 'package:sae_mobile/models/queries/distant/user.dart' as uqd;
import 'package:sae_mobile/models/queries/distant/annonce.dart' as aqd;

import 'package:sae_mobile/models/queries/local/annonce.dart' as aql;
import 'package:sae_mobile/models/queries/local/objet.dart';
import 'package:sae_mobile/models/queries/local/typeAnnonce.dart';

import 'package:sae_mobile/models/User.dart' as user_model;

import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:sae_mobile/models/queries/distant/typeAnnonce.dart' as tqd;

final SupabaseClient supabaseClient = Supabase.instance.client;

/// Classe Builder
///
/// Cette classe permet de construire des objets à partir de données distantes ou locales.
class Builder {
  /// Construit un utilisateur à partir de son id.
  ///
  /// [id] est l'id de l'utilisateur.
  ///
  /// Retourne un objet de type [user_model.User].
  static Future<user_model.User> buildUserById(String id) async {
    final data =
        await uqd.UserQueries.getUserById(id).then((value) => value.first);

    return user_model.User.fromJson(data);
  }

  /// Construit une liste d'annonces à partir de données distantes.
  ///
  /// Retourne une liste d'annonces.
  static Future<List<Annonce>> buildAnnoncesDistant() async {
    final data = await aqd.AnnonceQueries.getAnnonces().then((value) => value);

    List<Annonce> annonces = [];
    for (var annonce in data) {
      String user_id = await aqd.AnnonceQueries.getAuteurAnnonce(annonce['id']);
      annonces.add(Annonce.fromJson(annonce, (await buildUserById(user_id))));
    }

    return annonces;
  }

  static Future<List<Annonce>> buildAnnoncesDistantByType(String type) async {
    final data =
        await aqd.AnnonceQueries.getAnnoncesByType(type).then((value) => value);

    List<Annonce> annonces = [];
    for (var annonce in data) {
      print("les annonces du type $type sont : $annonce");
      String user_id = await aqd.AnnonceQueries.getAuteurAnnonce(annonce['id']);
      annonces.add(Annonce.fromJson(annonce, (await buildUserById(user_id))));
    }

    return annonces;
  }

  /// Construit une liste d'annonces non répondues à partir de données distantes.
  ///
  /// Retourne une liste d'annonces.
  static Future<List<Annonce>> buildAnnoncesDistantNonRepondu() async {
    final data =
        await aqd.AnnonceQueries.getAnnonceNonRepondu().then((value) => value);

    List<Annonce> annonces = [];
    for (var annonce in data) {
      String user_id = await aqd.AnnonceQueries.getAuteurAnnonce(annonce['id']);
      annonces.add(Annonce.fromJson(annonce, (await buildUserById(user_id))));
    }

    return annonces;
  }

  /// Construit une liste d'annonces répondues par l'utilisateur à partir de données distantes.
  ///
  /// [id] est l'id de l'utilisateur.
  ///
  /// Retourne une liste d'annonces.
  static Future<List<Annonce>> buildAnnoncesDistantRepondu(String id) async {
    print("L'id de l'utilisateur est annonce distant repondu : $id");
    final data =
        await aqd.AnnonceQueries.getAnnonceRepondu(id).then((value) => value);

    List<Annonce> annonces = [];
    for (var annonce in data) {
      String user_id = await aqd.AnnonceQueries.getAuteurAnnonce(annonce['id']);
      annonces.add(Annonce.fromJson(annonce, (await buildUserById(user_id))));
    }
    print("Les annonces répondues : $annonces");
    return annonces;
  }

  static Future<List<Annonce>> buildAnnoncesLocalUtilisateur(String id) async {
    final data = await aql.AnnonceQueries.getAnnoncesByUser(id);

    List<Annonce> annonces = [];
    for (var annonce in data) {
      annonces.add(Annonce.fromJson(annonce, await buildUserById(id)));
    }
    print("Les annonces locales : $annonces");
    return annonces;
  }

  /// Construit une liste d'annonces par id de l'annonce à partir de données distantes.
  ///
  /// [id] est l'id de l'annonce.
  ///
  /// Retourne une liste d'annonces.
  static Future<Annonce> buildAnnonceByIdDistant(String id) async {
    final data = await aqd.AnnonceQueries.getAnnonceById(id)
        .then((value) => value.first);

    String user_id = await aqd.AnnonceQueries.getAnnonceById(data['id'])
        .then((value) => value.first['id_user']);
    return Annonce.fromJson(data, await buildUserById(user_id));
  }

  /// Construit une liste d'annonces à partir de données locales.
  static Future<List<Annonce>> buildAnnoncesLocal() async {
    final data = await aql.AnnonceQueries.getAnnonces().then((value) => value);
    List<Annonce> annonces = [];
    print(data);
    for (var annonce in data) {
      annonces.add(Annonce.fromJson(
          annonce, await buildUserById(supabaseClient.auth.currentUser!.id)));
    }

    return annonces;
  }

  /// Construit une liste d'annonces par id de l'annonce à partir de données locales.
  static Future<Annonce> buildAnnonceByIdLocal(String id) async {
    final data = await aql.AnnonceQueries.getAnnonceById(id);

    return Annonce.fromJson(
        data, await buildUserById(supabaseClient.auth.currentUser!.id));
  }

  /// Construit une liste d'objets à partir de données locales.
  ///
  /// Retourne une liste d'objets.
  static Future<List<Objet>> buildObjets() async {
    final data = await ObjetQueries.getObjets().then((value) => value);

    List<Objet> objets = [];
    for (var objet in data) {
      objets.add(Objet.fromJson(objet));
    }

    return objets;
  }

  /// Construit une liste de types d'annonces à partir de données locales.
  ///
  /// Retourne une liste de types d'annonces.
  static Future<List<TypeAnnonce>> buildTypesAnnonce() async {
    final data =
        await TypeAnnoncesQueries.getTypeAnnonces().then((value) => value);

    List<TypeAnnonce> typesAnnonce = [];
    for (var typeAnnonce in data) {
      print('la categorie est : $typeAnnonce');
      typesAnnonce.add(TypeAnnonce.fromJson(typeAnnonce));
    }

    return typesAnnonce;
  }

  static Future<List<TypeAnnonce>> buildTypesAnnonceDistant() async {
    final data =
        await tqd.TypeAnnonceQueries.getTypeAnnonces().then((value) => value);

    List<TypeAnnonce> typesAnnonce = [];
    for (var typeAnnonce in data) {
      print(typeAnnonce);
      typesAnnonce.add(TypeAnnonce.fromJson(typeAnnonce));
    }

    return typesAnnonce;
  }
}
import 'package:sae_mobile/models/annonce.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

final SupabaseClient supabaseClient = Supabase.instance.client;

class AnnonceQueries {
  static Future<String> publishAnnonce(Annonce annonce) async {
    print("publishAnnonce");
    print(annonce.dateDeb.toIso8601String());
    print(annonce.dateFin.toIso8601String());
    print(annonce.titre);
    PostgrestList result = await supabaseClient.from('ANNONCES').insert({
      'titre': annonce.titre,
      'description': annonce.description,
      'dateDeb': annonce.dateDeb.toIso8601String(),
      'dateFin': annonce.dateFin.toIso8601String(),
      'idType': 1,
      'idEtat': 2,
    }).select('id');
    print("result");

    if (result.isEmpty) {
      throw Exception('Failed to create annonce');
    }

    String id = result[0]['id'];

    await supabaseClient.from('PUBLIE').insert({
      'id_a': id,
      'id_user': annonce.auteur.id,
    });

    return id;
  }

  static Future<PostgrestList> getAnnonces() async {
    final response = await supabaseClient.from('ANNONCES').select();
    if (response.isEmpty) {
      throw Exception('Failed to get annonces');
    }
    return response;
  }

  static Future<PostgrestList> getAnnoncesByType(String id) async {
    final response =
        await supabaseClient.from('ANNONCES').select().eq('idType', id);
    if (response.isEmpty) {
      throw Exception('Aucune annonce de ce type');
    }
    return response;
  }

  static Future<PostgrestList> getAnnonceNonRepondu() async {
    final response =
        await supabaseClient.from('ANNONCES').select().eq('idEtat', 2);
    if (response.isEmpty) {
      throw Exception('Failed to get annonces');
    }
    return response;
  }

  static Future<PostgrestList> getAnnonceRepondu(String id_u) async {
    final ids_a =
        await supabaseClient.from('REPONDS').select().eq('id_user', id_u);
    if (ids_a.isEmpty) {
      throw Exception("Pas d'annonces repondues");
    }
    final response = await supabaseClient
        .from('ANNONCES')
        .select()
        .inFilter('id', ids_a.map((e) => e['id_a']).toList());
    if (response.isEmpty) {
      throw Exception('Failed to get annonces');
    }
    return response;
  }

  static Future<PostgrestList> getAnnonceCloturer(String id_u) async {
    final ids_a =
        await supabaseClient.from('REPONDS').select().eq('id_user', id_u);
    if (ids_a.isEmpty) {
      throw Exception('Failed to get annonces');
    }
    final response = await supabaseClient.from('ANNONCES').select().inFilter(
        'id',
        ids_a.map((e) => e['id_a']).where((e) => e['id_etat'] == 3).toList());
    if (response.isEmpty) {
      throw Exception('Failed to get annonces');
    }
    return response;
  }

  static Future<PostgrestList> getAnnonceById(String id) async {
    final response =
        await supabaseClient.from('ANNONCES').select().eq('id', id);
    if (response.isEmpty) {
      throw Exception('Failed to get annonce');
    }
    return response;
  }

  static Future<String> getAuteurAnnonce(String id) async {
    final response =
        await supabaseClient.from('PUBLIE').select().eq('id_a', id);
    if (response.isEmpty) {
      throw Exception('Failed to get auteur');
    }
    return response[0]['id_user'];
  }

  static Future<void> accepterAnnonce(String id_a, String id_user) async {
    await supabaseClient.from('REPONDS').insert({
      'id_a': id_a,
      'id_user': id_user,
    });
  }

  static Future<void> updateAnnonceEtat(String id, int etat) async {
    await supabaseClient.from('ANNONCES').update({'idEtat': etat}).eq('id', id);
  }

  static Future<void> mettreAvis(String id_a, String id_u, String avis) async {
    await supabaseClient.from('AVIS').insert({
      'id_a': id_a,
      'id_user': id_u,
      'avis': avis,
    });
  }

  static Future<PostgrestList> getAnnonceAvis(String id_a) async {
    final response =
        await supabaseClient.from('AVIS').select().eq('id_a', id_a);
    if (response.isEmpty) {
      throw Exception('Failed to get avis');
    }
    return response;
  }
}