Previous week’s performance bar not displaying in bar chart with SQFlite data

I am working a habit tracker app using flutter with sqflite. if the task is checked then it shows the task completing bar. in the bar graph whenever i opened the same task by clicking on it another day the bar will automatically should display the bar for that particular task. i am trying to display the previous week bar, but i am not able to display the previous week performance bar. the previous day bar also not displaying. in some of the tasks when i click on same task next day it just only shows for current day progress bar. i want that whenever i swipe left the previous week days task performance should display in the bar graph as bar in blue color.

The main problem i am facing in the two methods which may be fetchProgressDataForPreviousWeek and fetchProgressDataForWeek.

  • task has been added from habit screen and display in a listview format in the habitscreen

  • after clicking on particular task in habit screen navigate to Taskscreen in which performance of the task is displayed with of bar in a bar graph

  • I used getx state management

TaskScreen.dart

//TaskScreen.dart

class TaskScreen extends StatelessWidget {
  final String taskName;
  final bool isTaskChecked;

  TaskScreen({required this.taskName, required this.isTaskChecked});

  @override
  Widget build(BuildContext context) {
    final TaskController taskController = Get.find();

    return Scaffold(
      appBar: AppBar(
        title: Text("Task Details"),
        backgroundColor: Colors.blue,
      ),
      body: PageView(
        children: [
          // Current Week Graph
          FutureBuilder<List<double>>(
            future: taskController.fetchProgressDataForWeek(taskName),
            builder: (context, snapshot) {
              if (snapshot.connectionState == ConnectionState.waiting) {
                return Center(child: CircularProgressIndicator());
              } else if (snapshot.hasError) {
                return Center(child: Text('Error: ${snapshot.error}'));
              } else if (!snapshot.hasData || snapshot.data!.isEmpty) {
                return Center(child: Text('No data available for this week'));
              }

              if (!isTaskChecked) {
                return Center(child: Text('No data available for this task.'));
              }

              final dailyProgress = snapshot.data!;
              final daysOfWeek = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
              final today = DateTime.now();
              final currentDayIndex = (today.weekday - DateTime.monday + 7) % 7;

              final progressData = List<double>.filled(7, 0.0);
              for (int i = 0; i < dailyProgress.length; i++) {
                progressData[i] = dailyProgress[i];
              }

              final barGroups = progressData.asMap().entries.map((entry) {
                int index = entry.key;
                double value = entry.value;
                Color barColor = Colors.blue;
                double barHeight = value;

                if (index < currentDayIndex) {
                  if (value == 0.0) {
                    barColor = Colors.grey;
                    barHeight = 0.2;
                  } else {
                    barColor = Colors.blue;
                    barHeight = value;
                  }
                } else if (index > currentDayIndex) {
                  // Future days should not be shown
                  barColor = Colors.transparent; 
                  barHeight = 0.0;
                }

                return BarChartGroupData(
                  x: index,
                  barRods: [
                    BarChartRodData(
                      toY: barHeight,
                      color: barColor,
                      width: 20,
                      borderRadius: BorderRadius.circular(4),
                    ),
                  ],
                );
              }).toList();

              return Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    'Current Week',
                    style: TextStyle(
                      fontSize: 24,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  SizedBox(height: 20),
                  Container(
                    height: 300,
                    child: BarChart(
                      BarChartData(
                        alignment: BarChartAlignment.spaceAround,
                        maxY: progressData.isNotEmpty ? progressData.reduce((a, b) => a > b ? a : b) + 1 : 1,
                        barGroups: barGroups,
                        titlesData: FlTitlesData(
                          bottomTitles: AxisTitles(
                            sideTitles: SideTitles(
                              showTitles: true,
                              reservedSize: 40,
                              getTitlesWidget: (value, meta) {
                                final dayLabel = daysOfWeek[value.toInt() % 7];
                                return SideTitleWidget(
                                  axisSide: meta.axisSide,
                                  child: Text(dayLabel, style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
                                );
                              },
                            ),
                          ),
                          leftTitles: AxisTitles(
                            sideTitles: SideTitles(
                              showTitles: true,
                              reservedSize: 40,
                              getTitlesWidget: (value, meta) {
                                return SideTitleWidget(
                                  axisSide: meta.axisSide,
                                  child: Text('${value.toInt()}', style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
                                );
                              },
                            ),
                          ),
                        ),
                        borderData: FlBorderData(
                          show: true,
                          border: Border.all(color: const Color(0xff37434d), width: 1),
                        ),
                        gridData: FlGridData(show: true),
                      ),
                    ),
                  ),
                ],
              );
            },
          ),
          // Previous Week Graph
          FutureBuilder<List<double>>(
            future: taskController.fetchProgressDataForPreviousWeek(taskName),
            builder: (context, snapshot) {
              if (snapshot.connectionState == ConnectionState.waiting) {
                return Center(child: CircularProgressIndicator());
              } else if (snapshot.hasError) {
                return Center(child: Text('Error: ${snapshot.error}'));
              } else if (!snapshot.hasData || snapshot.data!.isEmpty) {
                return Center(child: Text('No data available for the previous week'));
              }

              if (!isTaskChecked) {
                return Center(child: Text('No data available for this task.'));
              }

              final dailyProgress = snapshot.data!;
              final daysOfWeek = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];


              final progressData = List<double>.filled(7, 0.0);
              for (int i = 0; i < dailyProgress.length; i++) {
                progressData[i] = dailyProgress[i];
              }

              final barGroups = progressData.asMap().entries.map((entry) {
                int index = entry.key;
                double value = entry.value;
                Color barColor = value == 1.0 ? Colors.blue : Colors.grey;
                double barHeight = value == 1.0 ? 1.0 : 0.1;

                return BarChartGroupData(
                  x: index,
                  barRods: [
                    BarChartRodData(
                      toY: barHeight,
                      color: barColor,
                      width: 20,
                      borderRadius: BorderRadius.circular(4),
                    ),
                  ],
                );
              }).toList();

              return Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    'Previous Week',
                    style: TextStyle(
                      fontSize: 24,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  SizedBox(height: 20),
                  Container(
                    height: 300,
                    child: BarChart(
                      BarChartData(
                        alignment: BarChartAlignment.spaceAround,
                        maxY: progressData.isNotEmpty ? progressData.reduce((a, b) => a > b ? a : b) + 1 : 1,
                        barGroups: barGroups,
                        titlesData: FlTitlesData(
                          bottomTitles: AxisTitles(
                            sideTitles: SideTitles(
                              showTitles: true,
                              reservedSize: 40,
                              getTitlesWidget: (value, meta) {
                                final dayLabel = daysOfWeek[value.toInt() % 7];
                                return SideTitleWidget(
                                  axisSide: meta.axisSide,
                                  child: Text(dayLabel, style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
                                );
                              },
                            ),
                          ),
                          leftTitles: AxisTitles(
                            sideTitles: SideTitles(
                              showTitles: true,
                              reservedSize: 40,
                              getTitlesWidget: (value, meta) {
                                return SideTitleWidget(
                                  axisSide: meta.axisSide,
                                  child: Text('${value.toInt()}', style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
                                );
                              },
                            ),
                          ),
                        ),
                        borderData: FlBorderData(
                          show: true,
                          border: Border.all(color: const Color(0xff37434d), width: 1),
                        ),
                        gridData: FlGridData(show: true),
                      ),
                    ),
                  ),
                ],
              );
            },
          ),
        ],
      ),
    );
  }
}

DatabaseHelper.dart

import 'package:intl/intl.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

class DatabaseHelper {
  static final DatabaseHelper _instance = DatabaseHelper._internal();
  factory DatabaseHelper() => _instance;
  DatabaseHelper._internal();

  static Database? _database;

  // Static constants for table and column names
  static const String _tableTasks = 'tasks';
  static const String _columnId = 'id';
  static const String _columnTask = 'task';
  static const String _columnIsChecked = 'is_checked';
  static const String _columnProgress = 'progress';
  static const String _columnCreatedDate = 'created_date';

  Future<Database> get database async {
    if (_database != null) return _database!;
    _database = await _initDatabase();
    return _database!;
  }

  Future<Database> _initDatabase() async {
    final dbPath = await getDatabasesPath();
    final path = join(dbPath, 'tasks.db');

    return await openDatabase(
      path,
      version: 1,
      onCreate: (db, version) async {
        await db.execute('''
          CREATE TABLE $_tableTasks (
            $_columnId INTEGER PRIMARY KEY AUTOINCREMENT,
            $_columnTask TEXT UNIQUE,
            $_columnIsChecked INTEGER,
            $_columnProgress REAL,
            $_columnCreatedDate TEXT
          )
        ''');
      },
    );
  }

  Future<List<Map<String, dynamic>>> getTasks() async {
    final db = await database;
    return await db.query(_tableTasks);
  }

  Future<int?> insertTask(
      String task, bool isChecked, double progress, String createdDate) async {
    final db = await database;
    try {
      return await db.insert(
        _tableTasks,
        {
          _columnTask: task,
          _columnIsChecked: isChecked ? 1 : 0,
          _columnProgress: progress,
          _columnCreatedDate: createdDate,
        },
        conflictAlgorithm: ConflictAlgorithm.ignore,
      );
    } catch (e) {
      print("Error inserting task: $e");
      return null;
    }
  }

  Future<int> deleteTask(String task) async {
    final db = await database;
    return await db.delete(
      _tableTasks,
      where: '$_columnTask = ?',
      whereArgs: [task],
    );
  }

  Future<int> updateTask(String oldTask, String newTask, bool isChecked,
      double progress, String updatedDate) async {
    final db = await database;
    return await db.update(
      _tableTasks,
      {
        _columnTask: newTask,
        _columnIsChecked: isChecked ? 1 : 0,
        _columnProgress: progress,
        _columnCreatedDate: updatedDate,
      },
      where: '$_columnTask = ?',
      whereArgs: [oldTask],
    );
  }
   //this is from where i stuck in further in fetchprogressDataForWeek &fetchProgressDataForPreviousWeek
  Future<List<Map<String, dynamic>>> getTaskProgressForRange(
      String taskName, String startDate, String endDate) async {
    final db = await database;
    final results = await db.query(
      _tableTasks,
      where: '$_columnTask = ? AND $_columnCreatedDate BETWEEN ? AND ?',
      whereArgs: [taskName, startDate, endDate],
    );
    //final progressData = await getTaskProgressForRange(taskName, startDate, endDate);
    print('Queried Data: $results');
    return results;
  }

  Future<List<double>> fetchProgressDataForWeek(String taskName) async {
    final now = DateTime.now();
    final currentWeekday = now.weekday;

    // Calculate the start and end of the week (Monday to Sunday)
    final startOfWeek =
        now.subtract(Duration(days: currentWeekday - DateTime.monday));
    final endOfWeek = now.add(Duration(days: DateTime.sunday - currentWeekday));

    final startDate = DateFormat('yyyy-MM-dd').format(startOfWeek);
    final endDate = DateFormat('yyyy-MM-dd').format(endOfWeek);

    print('Fetching data from $startDate to $endDate');

    try {
      final progressData =
          await getTaskProgressForRange(taskName, startDate, endDate);

      // Initialize result list with zeros for all 7 days
      final result = List<double>.filled(7, 0.0);

      // Populate result list with actual data
      if (progressData.isNotEmpty) {
        for (var entry in progressData) {
          final dateString = entry[_columnCreatedDate] as String?;
          final progressValue = entry[_columnProgress] as double?;

          if (dateString != null && progressValue != null) {
            final date = DateTime.parse(dateString).toLocal();
            final dayOfWeek = (date.weekday - DateTime.monday + 7) %
                7; // Adjust to match Monday start
            if (dayOfWeek >= 0 && dayOfWeek < 7) {
              result[dayOfWeek] = progressValue;
            }
          }
        }
      } else {
        print('No progress data found for the specified range.');
      }

      //  current day's bar is visible with the correct progress
      final currentDayIndex = (now.weekday - DateTime.monday + 7) % 7;
      if (result[currentDayIndex] == 0.0) {
        result[currentDayIndex] = 1.0; //  current day's bar shows 1.0 progress if the task is completed
      }

      print('Progress Data for the Week: $result');
      return result;
    } catch (e) {
      print('Error fetching progress data: $e');
      return List<double>.filled(7, 0.0);
    }
  }

  Future<List<double>> fetchProgressDataForPreviousWeek(String taskName) async {
    final now = DateTime.now();
    final currentWeekday = now.weekday;

    // current week monday
    final mondayOfCurrentWeek = now.subtract(Duration(days: (currentWeekday - 1) % 7));

     // Calculate Start and End of Previous Week
    final startOfPreviousWeek = mondayOfCurrentWeek.subtract(Duration(days: 7));
    final endOfPreviousWeek = mondayOfCurrentWeek.subtract(Duration(days: 1));


    final startDate = DateFormat('yyyy-MM-dd').format(startOfPreviousWeek);
    final endDate = DateFormat('yyyy-MM-dd').format(endOfPreviousWeek);
    print('Monday of Current Week: $mondayOfCurrentWeek');
    print('Start of Previous Week: $startOfPreviousWeek');
    print('End of Previous Week: $endOfPreviousWeek');

    print('Fetching data from $startDate to $endDate');

    try {
      final progressData =
          await getTaskProgressForRange(taskName, startDate, endDate);

      print('Fetched Data: $progressData');

      final result = List<double>.filled(7, 0.0);

      if (progressData.isNotEmpty) {
        for (var entry in progressData) {
          final dateString = entry[_columnCreatedDate] as String?;
          final progressValue = entry[_columnProgress] as double?;

          print('Date String: $dateString, Progress Value: $progressValue');

          if (dateString != null && progressValue != null) {
            final date = DateTime.parse(dateString).toLocal();
            final dayOfWeek = (date.weekday - DateTime.monday + 7) % 7; // Adjust to match Monday start
            print('Day of Week: $dayOfWeek');
            if (dayOfWeek >= 0 && dayOfWeek < 7) {
              result[dayOfWeek] = progressValue;
            }
          }
        }
      } else {
        print('No progress data found for the specified range.');
      }

      print('Progress Data for the Previous Week: $result');
      return result;
    } catch (e) {
      print('Error fetching progress data: $e');
      return List<double>.filled(7, 0.0);
    }
  }
}

TaskController.dart

import 'package:get/get.dart';
import '../database/dbhelper.dart';

class TaskController extends GetxController {
  final DatabaseHelper _databaseHelper = DatabaseHelper();
  var taskList = <String>[].obs;
  var isTaskSelected = <bool>[].obs;

  @override
  void onInit() {
    super.onInit();
    loadTasks();
  }

  void loadTasks() async {
    try {
      final tasks = await _databaseHelper.getTasks();
      taskList.value = tasks.map((task) => task['task'] as String).toList();
      isTaskSelected.value = tasks.map((task) {
        final isChecked = task['is_checked'];
        return (isChecked != null && (isChecked as int) == 1);
      }).toList();
    } catch (e) {
      print("Error loading tasks: $e");
    }
  }

  void addTask(String task) async {
    try {
      bool isChecked = false;
      double progress = 0.0;
      String createdDate = DateTime.now().toIso8601String();

      int? result = await _databaseHelper.insertTask(
          task, isChecked, progress, createdDate);
      if (result == null) {
        print("Task already exists");
      } else {
        print("Task added successfully");
        loadTasks();
      }
    } catch (e) {
      print("Error adding task: $e");
    }
  }

  void deleteTask(int index) async {
    try {
      String taskToDelete = taskList[index];
      await _databaseHelper.deleteTask(taskToDelete);
      taskList.removeAt(index);
      isTaskSelected.removeAt(index);
    } catch (e) {
      print("Error deleting task: $e");
    }
  }

  Future<void> toggleTaskSelection(int index, bool value) async {
    try {
      if (index < 0 || index >= taskList.length) {
        throw ArgumentError("Index out of range");
      }

      String taskToUpdate = taskList[index];
      double progress = value ? 1.0 : 0.0;
      String updatedDate = DateTime.now().toIso8601String();

      isTaskSelected[index] = value;
      await _databaseHelper.updateTask(
          taskToUpdate, taskToUpdate, value, progress, updatedDate);
      print("Task updated successfully");
    } catch (e) {
      print("Error toggling task selection: $e");
    }
  }

  Future<List<double>> fetchProgressDataForWeek(String taskName) async {
    try {
      print("Fetching progress data for the week for task: $taskName");
      final progressData =
      await _databaseHelper.fetchProgressDataForWeek(taskName);
      print("Fetched weekly progress data: $progressData");
      return progressData;
    } catch (e) {
      print("Error fetching weekly progress data: $e");
      return List<double>.filled(7, 0.0);
    }
  }

  Future<List<double>> fetchProgressDataForPreviousWeek(String taskName) async {
    try {
      print("Fetching progress data for the previous week for task: $taskName");
      final progressData =
      await _databaseHelper.fetchProgressDataForPreviousWeek(taskName);
      print("Fetched previous week's progress data: $progressData");
      return progressData;
    } catch (e) {
      print("Error fetching previous week's progress data: $e");
      return List<double>.filled(7, 0.0);
    }
  }
}

HabitScreen.dart

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:goalsync/controller/task_controller.dart';
import 'package:goalsync/screens/task_screen.dart';

class HabitScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final TaskController taskController = Get.find();

    return Scaffold(
      appBar: _appBar(),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(17.0),
            child: Row(
              children: [],
            ),
          ),
          Expanded(
            child: Obx(() {
              if (taskController.isTaskSelected.length < taskController.taskList.length) {
                taskController.isTaskSelected.addAll(
                  List.generate(
                    taskController.taskList.length - taskController.isTaskSelected.length,
                        (_) => false,
                  ),
                );
              }

              return ListView.builder(
                itemCount: taskController.taskList.length,
                itemBuilder: (context, index) {
                  return Dismissible(
                    key: ValueKey(taskController.taskList[index]),
                    background: Container(
                      color: Colors.blue.shade300,
                      child: Icon(Icons.delete_outline, size: 35),
                      alignment: Alignment.centerRight,
                      padding: EdgeInsets.symmetric(horizontal: 20),
                    ),
                    direction: DismissDirection.endToStart,
                    onDismissed: (direction) {
                      taskController.deleteTask(index);
                    },
                    child: Padding(
                      padding: const EdgeInsets.symmetric(horizontal: 16),
                      child: GestureDetector(
                        onTap: () {
                          Get.to(TaskScreen(
                            taskName: taskController.taskList[index],
                            isTaskChecked: taskController.isTaskSelected[index],

                          ));
                        },
                        child: Container(
                          margin: EdgeInsets.symmetric(vertical: 5.0),
                          padding: EdgeInsets.all(6.0),
                          decoration: BoxDecoration(
                            borderRadius: BorderRadius.circular(12),
                            border: Border.all(color: Colors.black54, width: 2),
                          ),
                          child: Row(
                            children: [
                              Expanded(
                                child: Text(
                                  taskController.taskList[index],
                                  style: TextStyle(color: Colors.black, fontSize: 18),
                                ),
                              ),
                              Checkbox(
                                value: taskController.isTaskSelected[index],
                                onChanged: (bool? value) {
                                  if (value != null) {
                                    taskController.toggleTaskSelection(index, value);
                                  }
                                },
                                activeColor: Colors.blue,
                                checkColor: Colors.white,
                              ),
                            ],
                          ),
                        ),
                      ),
                    ),
                  );
                },
              );
            }),
          )
        ],
      ),
    );
  }

  AppBar _appBar() {
    return AppBar(
      title: Text("Habit Screen"),
      backgroundColor: Colors.blue,
      actions: [
        TextButton(
          onPressed: () {
            _showAddTaskDialog();
          },
          child: Text(
            'Add Task',
            style: TextStyle(
              color: Colors.black,
              fontSize: 16,
            ),
          ),
        ),
      ],
    );
  }

  void _showAddTaskDialog() {
    final TaskController taskController = Get.find();
    final TextEditingController taskControllerInput = TextEditingController();

    showDialog(
      context: Get.context!,
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text('Add New Task'),
          content: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              TextField(
                controller: taskControllerInput,
                decoration: InputDecoration(hintText: 'Enter task'),
                autofocus: true,
              ),

            ],
          ),
          actions: [
            TextButton(
              onPressed: () {
                final task = taskControllerInput.text.trim();
                if (task.isNotEmpty) {
                  taskController.addTask(task);
                }
                Get.back(); // Close the dialog
              },
              child: Text('Save'),
            ),
            TextButton(
              onPressed: () {
                Get.back(); // Close the dialog
              },
              child: Text('Cancel'),
            ),
          ],
        );
      },
    );
  }
}

9

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật