First Commit
This commit is contained in:
commit
cf9784f468
43
.gitignore
vendored
Normal file
43
.gitignore
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
**/ios/Flutter/.last_build_id
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.pub-cache/
|
||||
.pub/
|
||||
/build/
|
||||
|
||||
# Symbolication related
|
||||
app.*.symbols
|
||||
|
||||
# Obfuscation related
|
||||
app.*.map.json
|
||||
|
||||
# Android Studio will place build artifacts here
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
45
.metadata
Normal file
45
.metadata
Normal file
@ -0,0 +1,45 @@
|
||||
# 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 and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: "4cf269e36de2573851eaef3c763994f8f9be494d"
|
||||
channel: "stable"
|
||||
|
||||
project_type: app
|
||||
|
||||
# Tracks metadata for the flutter migrate command
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: 4cf269e36de2573851eaef3c763994f8f9be494d
|
||||
base_revision: 4cf269e36de2573851eaef3c763994f8f9be494d
|
||||
- platform: android
|
||||
create_revision: 4cf269e36de2573851eaef3c763994f8f9be494d
|
||||
base_revision: 4cf269e36de2573851eaef3c763994f8f9be494d
|
||||
- platform: ios
|
||||
create_revision: 4cf269e36de2573851eaef3c763994f8f9be494d
|
||||
base_revision: 4cf269e36de2573851eaef3c763994f8f9be494d
|
||||
- platform: linux
|
||||
create_revision: 4cf269e36de2573851eaef3c763994f8f9be494d
|
||||
base_revision: 4cf269e36de2573851eaef3c763994f8f9be494d
|
||||
- platform: macos
|
||||
create_revision: 4cf269e36de2573851eaef3c763994f8f9be494d
|
||||
base_revision: 4cf269e36de2573851eaef3c763994f8f9be494d
|
||||
- platform: web
|
||||
create_revision: 4cf269e36de2573851eaef3c763994f8f9be494d
|
||||
base_revision: 4cf269e36de2573851eaef3c763994f8f9be494d
|
||||
- platform: windows
|
||||
create_revision: 4cf269e36de2573851eaef3c763994f8f9be494d
|
||||
base_revision: 4cf269e36de2573851eaef3c763994f8f9be494d
|
||||
|
||||
# User provided section
|
||||
|
||||
# List of Local paths (relative to this file) that should be
|
||||
# ignored by the migrate tool.
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
16
README.md
Normal file
16
README.md
Normal file
@ -0,0 +1,16 @@
|
||||
# baligh_dashboard
|
||||
|
||||
A new Flutter project.
|
||||
|
||||
## Getting Started
|
||||
|
||||
This project is a starting point for a Flutter application.
|
||||
|
||||
A few resources to get you started if this is your first Flutter project:
|
||||
|
||||
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
||||
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
|
||||
|
||||
For help getting started with Flutter development, view the
|
||||
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
||||
samples, guidance on mobile development, and a full API reference.
|
28
analysis_options.yaml
Normal file
28
analysis_options.yaml
Normal file
@ -0,0 +1,28 @@
|
||||
# This file configures the analyzer, which statically analyzes Dart code to
|
||||
# check for errors, warnings, and lints.
|
||||
#
|
||||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||
# invoked from the command line by running `flutter analyze`.
|
||||
|
||||
# The following line activates a set of recommended lints for Flutter apps,
|
||||
# packages, and plugins designed to encourage good coding practices.
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
linter:
|
||||
# The lint rules applied to this project can be customized in the
|
||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||
# included above or to enable additional rules. A list of all available lints
|
||||
# and their documentation is published at https://dart.dev/lints.
|
||||
#
|
||||
# Instead of disabling a lint rule for the entire project in the
|
||||
# section below, it can also be suppressed for a single line of code
|
||||
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||
# producing the lint.
|
||||
rules:
|
||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
BIN
assets/fonts/Cairo-Bold.ttf
Normal file
BIN
assets/fonts/Cairo-Bold.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Cairo-Light.ttf
Normal file
BIN
assets/fonts/Cairo-Light.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Cairo-Medium.ttf
Normal file
BIN
assets/fonts/Cairo-Medium.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Cairo-Regular.ttf
Normal file
BIN
assets/fonts/Cairo-Regular.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Cairo-SemiBold.ttf
Normal file
BIN
assets/fonts/Cairo-SemiBold.ttf
Normal file
Binary file not shown.
1
firebase.json
Normal file
1
firebase.json
Normal file
@ -0,0 +1 @@
|
||||
{"flutter":{"platforms":{"dart":{"lib/firebase_options.dart":{"projectId":"baligh-fbef7","configurations":{"web":"1:580708075572:web:72a4af92dc560d72702261"}}}}}}
|
66
lib/constants/theme.dart
Normal file
66
lib/constants/theme.dart
Normal file
@ -0,0 +1,66 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MyCustomTheme {
|
||||
static Color primaryColor = const Color.fromARGB(255, 42, 61, 171);
|
||||
static Color cardColor = const Color(0xFFECECEC);
|
||||
static Color textColor = const Color(0xFF181818);
|
||||
static Color bgColor = const Color(0xFFF8FAFC);
|
||||
static Color dividerColor = const Color(0xFF4C4C4C);
|
||||
|
||||
static ThemeData theme = ThemeData(
|
||||
primaryColor: primaryColor,
|
||||
useMaterial3: true,
|
||||
cardColor: cardColor,
|
||||
scaffoldBackgroundColor: bgColor,
|
||||
dividerColor: dividerColor,
|
||||
textTheme: TextTheme(
|
||||
headlineLarge: TextStyle(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontFamily: "Cairo",
|
||||
color: textColor,
|
||||
),
|
||||
titleLarge: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontFamily: "Cairo",
|
||||
color: textColor,
|
||||
),
|
||||
bodyLarge: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.normal,
|
||||
fontFamily: "Cairo",
|
||||
color: textColor,
|
||||
),
|
||||
bodyMedium: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.normal,
|
||||
fontFamily: "Cairo",
|
||||
color: textColor,
|
||||
),
|
||||
bodySmall: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.normal,
|
||||
fontFamily: "Cairo",
|
||||
color: textColor,
|
||||
),
|
||||
),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
elevation: const WidgetStatePropertyAll(0),
|
||||
backgroundColor: WidgetStatePropertyAll(primaryColor),
|
||||
shape: WidgetStatePropertyAll(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
)),
|
||||
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
elevation: const WidgetStatePropertyAll(0),
|
||||
shape: WidgetStatePropertyAll(RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
)),
|
||||
)),
|
||||
);
|
||||
}
|
64
lib/firebase_options.dart
Normal file
64
lib/firebase_options.dart
Normal file
@ -0,0 +1,64 @@
|
||||
// File generated by FlutterFire CLI.
|
||||
// ignore_for_file: type=lint
|
||||
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
|
||||
import 'package:flutter/foundation.dart'
|
||||
show defaultTargetPlatform, kIsWeb, TargetPlatform;
|
||||
|
||||
/// Default [FirebaseOptions] for use with your Firebase apps.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// import 'firebase_options.dart';
|
||||
/// // ...
|
||||
/// await Firebase.initializeApp(
|
||||
/// options: DefaultFirebaseOptions.currentPlatform,
|
||||
/// );
|
||||
/// ```
|
||||
class DefaultFirebaseOptions {
|
||||
static FirebaseOptions get currentPlatform {
|
||||
if (kIsWeb) {
|
||||
return web;
|
||||
}
|
||||
switch (defaultTargetPlatform) {
|
||||
case TargetPlatform.android:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions have not been configured for android - '
|
||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||
);
|
||||
case TargetPlatform.iOS:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions have not been configured for ios - '
|
||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||
);
|
||||
case TargetPlatform.macOS:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions have not been configured for macos - '
|
||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||
);
|
||||
case TargetPlatform.windows:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions have not been configured for windows - '
|
||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||
);
|
||||
case TargetPlatform.linux:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions have not been configured for linux - '
|
||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||
);
|
||||
default:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions are not supported for this platform.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static const FirebaseOptions web = FirebaseOptions(
|
||||
apiKey: 'AIzaSyBwuk1s6fLiPK0DjEpjYK-s9aZby8z-FiM',
|
||||
appId: '1:580708075572:web:72a4af92dc560d72702261',
|
||||
messagingSenderId: '580708075572',
|
||||
projectId: 'baligh-fbef7',
|
||||
authDomain: 'baligh-fbef7.firebaseapp.com',
|
||||
storageBucket: 'baligh-fbef7.appspot.com',
|
||||
measurementId: 'G-KQX9WQ9Y8M',
|
||||
);
|
||||
}
|
34
lib/main.dart
Normal file
34
lib/main.dart
Normal file
@ -0,0 +1,34 @@
|
||||
import 'package:baligh_dashboard/constants/theme.dart';
|
||||
import 'package:baligh_dashboard/firebase_options.dart';
|
||||
import 'package:baligh_dashboard/screens/login_screen.dart';
|
||||
import 'package:bot_toast/bot_toast.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
await Firebase.initializeApp(
|
||||
options: DefaultFirebaseOptions.currentPlatform,
|
||||
);
|
||||
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
// This widget is the root of your application.
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'بلغ',
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: MyCustomTheme.theme,
|
||||
locale: const Locale("ar"),
|
||||
builder: BotToastInit(),
|
||||
home: const LoginScreen(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
156
lib/screens/admin_screen.dart
Normal file
156
lib/screens/admin_screen.dart
Normal file
@ -0,0 +1,156 @@
|
||||
import 'package:baligh_dashboard/widgets/app_toast.dart';
|
||||
import 'package:baligh_dashboard/widgets/custom_button.dart';
|
||||
import 'package:baligh_dashboard/widgets/custom_text_field.dart';
|
||||
import 'package:bot_toast/bot_toast.dart';
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AdminScreen extends StatefulWidget {
|
||||
const AdminScreen({super.key});
|
||||
|
||||
@override
|
||||
State<AdminScreen> createState() => _AdminScreenState();
|
||||
}
|
||||
|
||||
class _AdminScreenState extends State<AdminScreen> {
|
||||
TextEditingController usernameController = TextEditingController();
|
||||
TextEditingController passwordController = TextEditingController();
|
||||
|
||||
String selectedRole = "التحرش";
|
||||
List<String> availableRoles = [
|
||||
"التحرش",
|
||||
"التعنيف",
|
||||
"الابتزاز",
|
||||
"المخدرات",
|
||||
"الرشوة",
|
||||
];
|
||||
|
||||
bool isLoading = false;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
usernameController.dispose();
|
||||
passwordController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Scaffold(
|
||||
body: SafeArea(
|
||||
child: Center(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 500,
|
||||
minWidth: 100,
|
||||
),
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
padding: const EdgeInsets.all(15),
|
||||
children: [
|
||||
Text(
|
||||
"اضافى مشرف",
|
||||
style: Theme.of(context).textTheme.headlineLarge,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 50,
|
||||
),
|
||||
CustomTextField(
|
||||
controller: usernameController,
|
||||
labelText: "اسم المشرف",
|
||||
inputType: TextInputType.text),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
CustomTextField(
|
||||
controller: passwordController,
|
||||
labelText: "كلمة المرور",
|
||||
inputType: TextInputType.text,
|
||||
isPassword: true,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
DropdownMenu<String>(
|
||||
initialSelection: "التحرش",
|
||||
label: const Text("نوع البلاغ"),
|
||||
onSelected: (String? value) {
|
||||
setState(() {
|
||||
selectedRole = value ?? "التحرش";
|
||||
});
|
||||
},
|
||||
dropdownMenuEntries: availableRoles
|
||||
.map<DropdownMenuEntry<String>>((value) {
|
||||
return DropdownMenuEntry<String>(
|
||||
value: value,
|
||||
label: value,
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
CustomButton(
|
||||
label: "اضافة المشرف",
|
||||
isElevated: true,
|
||||
onPressed: handleCreateAdmin,
|
||||
isLoading: isLoading,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> handleCreateAdmin() async {
|
||||
if (passwordController.text.trim().isEmpty ||
|
||||
usernameController.text.trim().isEmpty) {
|
||||
BotToast.showCustomText(toastBuilder: (_) {
|
||||
return const AppToast(text: "يجب ملئ جميع الحقول");
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
await FirebaseFirestore.instance.collection("admins").add({
|
||||
"role": selectedRole,
|
||||
"username": usernameController.text.trim(),
|
||||
"password": passwordController.text.trim(),
|
||||
"createdAt": FieldValue.serverTimestamp(),
|
||||
});
|
||||
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
usernameController.clear();
|
||||
passwordController.clear();
|
||||
});
|
||||
// if (!mounted) return;
|
||||
|
||||
BotToast.showCustomText(toastBuilder: (_) {
|
||||
return const AppToast(text: "تم اضافة مشرف");
|
||||
});
|
||||
} catch (e) {
|
||||
print(e);
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
BotToast.showCustomText(toastBuilder: (_) {
|
||||
return const AppToast(text: "حدث خطأ ما");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
245
lib/screens/dashboard_screen.dart
Normal file
245
lib/screens/dashboard_screen.dart
Normal file
@ -0,0 +1,245 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:baligh_dashboard/screens/admin_screen.dart';
|
||||
import 'package:baligh_dashboard/widgets/custom_button.dart';
|
||||
import 'package:baligh_dashboard/widgets/dashboard_tile.dart';
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DashboardScreen extends StatefulWidget {
|
||||
const DashboardScreen({super.key, required this.adminRole});
|
||||
|
||||
final String adminRole;
|
||||
|
||||
@override
|
||||
State<DashboardScreen> createState() => _DashboardScreenState();
|
||||
}
|
||||
|
||||
class _DashboardScreenState extends State<DashboardScreen> {
|
||||
bool isLoading = false;
|
||||
String? error;
|
||||
|
||||
List<Map<String, dynamic>> reports = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
getInitialData();
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Scaffold(
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 50,
|
||||
),
|
||||
AutoSizeText(
|
||||
"البلاغات",
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
minFontSize: 12,
|
||||
),
|
||||
if (isLoading) ...[
|
||||
const SizedBox(
|
||||
height: 50,
|
||||
),
|
||||
Text("جار التحميل",
|
||||
style: Theme.of(context).textTheme.bodyLarge),
|
||||
] else if (error != null) ...[
|
||||
const SizedBox(
|
||||
height: 50,
|
||||
),
|
||||
Text(error!, style: Theme.of(context).textTheme.bodyLarge),
|
||||
] else ...[
|
||||
if (widget.adminRole == "admin") ...[
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
SizedBox(
|
||||
width: 200,
|
||||
child: CustomButton(
|
||||
label: "اضافة مشرف",
|
||||
isElevated: true,
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const AdminScreen()));
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
"اسم صاحب البلاغ",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
maxLines: 3,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
"عنوان صاحب البلاغ",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
maxLines: 3,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
"المدرسة",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
maxLines: 3,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
"نوع البلاغ",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
maxLines: 3,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
"الحالة",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
maxLines: 3,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Expanded(
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
itemCount: reports.length,
|
||||
separatorBuilder: (context, index) => const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
itemBuilder: (context, index) => DashboardTile(
|
||||
id: reports[index]["id"] ?? "",
|
||||
name: reports[index]["name"] ?? "",
|
||||
age: reports[index]["age"] ?? "",
|
||||
phone: reports[index]["phone"] ?? "",
|
||||
address: reports[index]["address"] ?? "",
|
||||
school: reports[index]["schoolName"] ?? "",
|
||||
nationalIdNumber:
|
||||
reports[index]["nationalIdNumber"] ?? "",
|
||||
description: reports[index]["description"] ?? "",
|
||||
attachments: reports[index]["attachments"] ?? "",
|
||||
type: reports[index]["type"] ?? "",
|
||||
createdAt: reports[index]["createdAt"] ?? "",
|
||||
status: reports[index]["status"] ?? ""),
|
||||
),
|
||||
)
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> getInitialData() async {
|
||||
try {
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
|
||||
var temp = await getReportsWithUsers();
|
||||
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
reports = temp;
|
||||
});
|
||||
} catch (e, s) {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
error = "حدث خطأ ما";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<Map<String, dynamic>>> getReportsWithUsers() async {
|
||||
QuerySnapshot reportsSnapshot;
|
||||
|
||||
if (widget.adminRole == "admin") {
|
||||
reportsSnapshot = await FirebaseFirestore.instance
|
||||
.collection('reports')
|
||||
.orderBy('createdAt', descending: true)
|
||||
.get();
|
||||
} else {
|
||||
reportsSnapshot = await FirebaseFirestore.instance
|
||||
.collection('reports')
|
||||
.where('type', isEqualTo: widget.adminRole)
|
||||
.orderBy('createdAt', descending: true)
|
||||
.get();
|
||||
}
|
||||
|
||||
List<Map<String, dynamic>> reportsWithUsers = [];
|
||||
Set<DocumentReference> userRefs = {};
|
||||
|
||||
// Collect unique user references
|
||||
for (var reportDoc in reportsSnapshot.docs) {
|
||||
var reportData = reportDoc.data() as Map<String, dynamic>;
|
||||
if (reportData['userRef'] == null) continue;
|
||||
userRefs.add(reportData['userRef'] as DocumentReference);
|
||||
reportsWithUsers.add({
|
||||
'id': reportDoc.id,
|
||||
'type': reportData["type"],
|
||||
'description': reportData["description"],
|
||||
'attachments': reportData["attachments"],
|
||||
'status': reportData["status"],
|
||||
'createdAt': reportData["createdAt"],
|
||||
'userId': reportData["userId"],
|
||||
});
|
||||
}
|
||||
|
||||
// Fetch all user data in one go
|
||||
var userDocs = await Future.wait(userRefs.map((ref) => ref.get()));
|
||||
|
||||
for (var el in userDocs) {
|
||||
int index = reportsWithUsers.indexWhere((r) => r["userId"] == el["uid"]);
|
||||
reportsWithUsers[index]["name"] = el["name"];
|
||||
reportsWithUsers[index]["phone"] = el["phone"];
|
||||
reportsWithUsers[index]["age"] = el["age"];
|
||||
reportsWithUsers[index]["schoolName"] = el["schoolName"];
|
||||
reportsWithUsers[index]["stage"] = el["stage"];
|
||||
reportsWithUsers[index]["address"] = el["address"];
|
||||
reportsWithUsers[index]["nationalIdNumber"] = el["nationalIdNumber"];
|
||||
}
|
||||
|
||||
return reportsWithUsers;
|
||||
}
|
||||
}
|
136
lib/screens/login_screen.dart
Normal file
136
lib/screens/login_screen.dart
Normal file
@ -0,0 +1,136 @@
|
||||
import 'package:baligh_dashboard/screens/dashboard_screen.dart';
|
||||
import 'package:baligh_dashboard/widgets/app_toast.dart';
|
||||
import 'package:baligh_dashboard/widgets/custom_button.dart';
|
||||
import 'package:baligh_dashboard/widgets/custom_text_field.dart';
|
||||
import 'package:bot_toast/bot_toast.dart';
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class LoginScreen extends StatefulWidget {
|
||||
const LoginScreen({super.key});
|
||||
|
||||
@override
|
||||
State<LoginScreen> createState() => _LoginScreenState();
|
||||
}
|
||||
|
||||
class _LoginScreenState extends State<LoginScreen> {
|
||||
TextEditingController usernameController = TextEditingController();
|
||||
TextEditingController passwordController = TextEditingController();
|
||||
|
||||
bool isLoading = false;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
usernameController.dispose();
|
||||
passwordController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Scaffold(
|
||||
body: SafeArea(
|
||||
child: Center(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 500,
|
||||
minWidth: 100,
|
||||
),
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
padding: const EdgeInsets.all(15),
|
||||
children: [
|
||||
Text(
|
||||
"تسجيل الدخول",
|
||||
style: Theme.of(context).textTheme.headlineLarge,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 50,
|
||||
),
|
||||
CustomTextField(
|
||||
controller: usernameController,
|
||||
labelText: "اسم المشرف",
|
||||
inputType: TextInputType.text),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
CustomTextField(
|
||||
controller: passwordController,
|
||||
labelText: "كلمة المرور",
|
||||
inputType: TextInputType.text,
|
||||
isPassword: true,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
CustomButton(
|
||||
label: "تسجيل الدخول",
|
||||
isElevated: true,
|
||||
onPressed: handleLogIn,
|
||||
isLoading: isLoading,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> handleLogIn() async {
|
||||
if (usernameController.text.trim().isEmpty ||
|
||||
passwordController.text.trim().isEmpty) {
|
||||
BotToast.showCustomText(toastBuilder: (_) {
|
||||
return const AppToast(text: "يجب ملئ جميع الحقول");
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
final QuerySnapshot snapshot = await FirebaseFirestore.instance
|
||||
.collection('admins')
|
||||
.where('username', isEqualTo: usernameController.text.trim())
|
||||
.where('password', isEqualTo: passwordController.text.trim())
|
||||
.get();
|
||||
|
||||
if (snapshot.docs.isEmpty) {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
BotToast.showCustomText(toastBuilder: (_) {
|
||||
return const AppToast(text: "اسم المشرف او كلمة المرور خطأ");
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var admin = snapshot.docs.first.data() as Map<String, dynamic>;
|
||||
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
if (!mounted) return;
|
||||
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => DashboardScreen(adminRole: admin["role"])),
|
||||
);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
BotToast.showCustomText(toastBuilder: (_) {
|
||||
return const AppToast(text: "حدث خطأ ما");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
244
lib/screens/report_details_screen.dart
Normal file
244
lib/screens/report_details_screen.dart
Normal file
@ -0,0 +1,244 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:baligh_dashboard/utils/file_classification.dart';
|
||||
import 'package:baligh_dashboard/widgets/app_toast.dart';
|
||||
import 'package:baligh_dashboard/widgets/dashboard_tile.dart';
|
||||
import 'package:baligh_dashboard/widgets/file_card.dart';
|
||||
import 'package:bot_toast/bot_toast.dart';
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ReportDetailsScreen extends StatefulWidget {
|
||||
const ReportDetailsScreen({
|
||||
super.key,
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.address,
|
||||
required this.school,
|
||||
required this.type,
|
||||
required this.status,
|
||||
required this.age,
|
||||
required this.attachments,
|
||||
required this.createdAt,
|
||||
required this.description,
|
||||
required this.nationalIdNumber,
|
||||
required this.phone,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String name;
|
||||
final String address;
|
||||
final String school;
|
||||
final String type;
|
||||
final String status;
|
||||
final Timestamp createdAt;
|
||||
final int age;
|
||||
final String phone;
|
||||
final int nationalIdNumber;
|
||||
final String description;
|
||||
final List attachments;
|
||||
|
||||
@override
|
||||
State<ReportDetailsScreen> createState() => _ReportDetailsScreenState();
|
||||
}
|
||||
|
||||
class _ReportDetailsScreenState extends State<ReportDetailsScreen> {
|
||||
String selectedStatus = "قيد الانتظار";
|
||||
List<String> availableStatus = [
|
||||
"قيد الانتظار",
|
||||
"قيد المعالجة",
|
||||
"مكتمل",
|
||||
];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
setState(() {
|
||||
selectedStatus = widget.status;
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Scaffold(
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 50,
|
||||
),
|
||||
AutoSizeText(
|
||||
"تفاصيل البلاغ",
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
minFontSize: 12,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
Wrap(
|
||||
children: [
|
||||
Text(
|
||||
"حالة البلاغ:",
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
DropdownMenu<String>(
|
||||
initialSelection: "قيد الانتظار",
|
||||
label: const Text("الحالة"),
|
||||
onSelected: (String? value) async {
|
||||
var old = selectedStatus;
|
||||
setState(() {
|
||||
selectedStatus = value ?? "قيد الانتظار";
|
||||
});
|
||||
try {
|
||||
await FirebaseFirestore.instance
|
||||
.collection("reports")
|
||||
.doc(widget.id)
|
||||
.update({
|
||||
'status': selectedStatus,
|
||||
});
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
selectedStatus = old;
|
||||
});
|
||||
BotToast.showCustomText(toastBuilder: (_) {
|
||||
return const AppToast(text: "حدث خطأ ما");
|
||||
});
|
||||
}
|
||||
},
|
||||
dropdownMenuEntries:
|
||||
availableStatus.map<DropdownMenuEntry<String>>((value) {
|
||||
return DropdownMenuEntry<String>(
|
||||
value: value,
|
||||
label: value,
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
"اسم صاحب البلاغ",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
maxLines: 3,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
"عنوان صاحب البلاغ",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
maxLines: 3,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
"المدرسة",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
maxLines: 3,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
"نوع البلاغ",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
maxLines: 3,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
"الحالة",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
maxLines: 3,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
DashboardTile(
|
||||
id: widget.id,
|
||||
name: widget.name,
|
||||
age: widget.age,
|
||||
phone: widget.phone,
|
||||
address: widget.address,
|
||||
school: widget.school,
|
||||
nationalIdNumber: widget.nationalIdNumber,
|
||||
description: widget.description,
|
||||
attachments: widget.attachments,
|
||||
type: widget.type,
|
||||
createdAt: widget.createdAt,
|
||||
status: widget.status,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
AutoSizeText(
|
||||
"وصف البلاغ",
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
minFontSize: 12,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Text(
|
||||
widget.description,
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
AutoSizeText(
|
||||
"الملفات المرفقة",
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
minFontSize: 12,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Wrap(
|
||||
children: widget.attachments
|
||||
.map(
|
||||
(el) => FileCard(
|
||||
type: FileClassification.getFileClass(el),
|
||||
filename: FileClassification.getFileName(el) ?? "",
|
||||
fileUrl: el,),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
54
lib/utils/file_classification.dart
Normal file
54
lib/utils/file_classification.dart
Normal file
@ -0,0 +1,54 @@
|
||||
import 'package:file_saver/file_saver.dart';
|
||||
|
||||
class FileClassification {
|
||||
static String? getFileName(String url) {
|
||||
Uri uri = Uri.parse(url);
|
||||
return uri.pathSegments.isNotEmpty ? uri.pathSegments.last : null;
|
||||
}
|
||||
|
||||
static String getFileClass(String url) {
|
||||
String? fileName = getFileName(url);
|
||||
if (fileName == null) return 'unknown';
|
||||
|
||||
// Get the file extension
|
||||
String extension = fileName.split('.').last.toLowerCase();
|
||||
|
||||
// Determine the class based on the file extension
|
||||
if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].contains(extension)) {
|
||||
return 'image';
|
||||
} else if (['mp3', 'wav', 'ogg', 'aac'].contains(extension)) {
|
||||
return 'audio';
|
||||
} else if (['mp4', 'mkv', 'webm', 'avi', 'mov'].contains(extension)) {
|
||||
return 'video';
|
||||
} else {
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static MimeType getMimeType(String filename) {
|
||||
String extension = filename.split('.').last.toLowerCase();
|
||||
switch (extension.toLowerCase()) {
|
||||
case 'jpg':
|
||||
case 'jpeg':
|
||||
return MimeType.jpeg;
|
||||
case 'png':
|
||||
return MimeType.png;
|
||||
case 'gif':
|
||||
return MimeType.gif;
|
||||
case 'bmp':
|
||||
return MimeType.bmp;
|
||||
case 'mp3':
|
||||
return MimeType.mp3;
|
||||
case 'aac':
|
||||
return MimeType.aac;
|
||||
case 'mp4':
|
||||
return MimeType.mp4Video;
|
||||
case 'avi':
|
||||
return MimeType.avi;
|
||||
default:
|
||||
return MimeType.other; // Fallback for unknown types
|
||||
}
|
||||
}
|
||||
|
||||
}
|
42
lib/widgets/app_toast.dart
Normal file
42
lib/widgets/app_toast.dart
Normal file
@ -0,0 +1,42 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AppToast extends StatelessWidget {
|
||||
const AppToast({
|
||||
super.key,
|
||||
required this.text,
|
||||
this.bottomPadding,
|
||||
this.color,
|
||||
this.textStyle,
|
||||
});
|
||||
|
||||
final String text;
|
||||
final double? bottomPadding;
|
||||
final Color? color;
|
||||
final TextStyle? textStyle;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: bottomPadding ?? 100),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: color ?? Theme.of(context).scaffoldBackgroundColor,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.4),
|
||||
offset: const Offset(2, 2),
|
||||
blurRadius: 5,
|
||||
spreadRadius: 1
|
||||
)
|
||||
]
|
||||
),
|
||||
child: Text(
|
||||
text,
|
||||
style: textStyle ?? Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
86
lib/widgets/custom_button.dart
Normal file
86
lib/widgets/custom_button.dart
Normal file
@ -0,0 +1,86 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:baligh_dashboard/constants/theme.dart';
|
||||
import 'package:baligh_dashboard/widgets/small_spinner.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CustomButton extends StatelessWidget {
|
||||
const CustomButton({
|
||||
super.key,
|
||||
required this.onPressed,
|
||||
required this.label,
|
||||
required this.isElevated,
|
||||
this.isLoading = false,
|
||||
this.textStyle,
|
||||
this.color,
|
||||
});
|
||||
|
||||
final void Function() onPressed;
|
||||
final String label;
|
||||
final bool isLoading;
|
||||
final bool isElevated;
|
||||
final TextStyle? textStyle;
|
||||
final Color? color;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return isElevated
|
||||
? ElevatedButton(
|
||||
onPressed: isLoading ? null : onPressed,
|
||||
style: Theme.of(context).elevatedButtonTheme.style?.copyWith(
|
||||
backgroundColor: WidgetStatePropertyAll(color ?? MyCustomTheme.primaryColor)
|
||||
),
|
||||
child: isLoading
|
||||
? const SmallSpinner()
|
||||
: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 3),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
label,
|
||||
minFontSize: 8,
|
||||
maxFontSize: 14,
|
||||
maxLines: 1,
|
||||
textAlign: TextAlign.center,
|
||||
style: textStyle ?? Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
color: MyCustomTheme.bgColor,
|
||||
)
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: OutlinedButton(
|
||||
onPressed: isLoading ? null : onPressed,
|
||||
style: Theme.of(context).elevatedButtonTheme.style?.copyWith(
|
||||
backgroundColor: WidgetStatePropertyAll(Theme.of(context).scaffoldBackgroundColor),
|
||||
side: WidgetStatePropertyAll(BorderSide(
|
||||
width: 1.5,
|
||||
color: color ?? MyCustomTheme.primaryColor,
|
||||
))
|
||||
),
|
||||
child: isLoading
|
||||
? SmallSpinner(color: MyCustomTheme.primaryColor,)
|
||||
: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 3),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
label,
|
||||
minFontSize: 8,
|
||||
maxFontSize: 14,
|
||||
maxLines: 1,
|
||||
textAlign: TextAlign.center,
|
||||
style: textStyle ?? Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
color: color ?? MyCustomTheme.primaryColor,
|
||||
)
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
77
lib/widgets/custom_text_field.dart
Normal file
77
lib/widgets/custom_text_field.dart
Normal file
@ -0,0 +1,77 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CustomTextField extends StatefulWidget {
|
||||
const CustomTextField({
|
||||
super.key,
|
||||
required this.controller,
|
||||
required this.labelText,
|
||||
required this.inputType,
|
||||
this.errorText,
|
||||
this.isPassword = false,
|
||||
this.maxLines = 1,
|
||||
this.focusNode,
|
||||
});
|
||||
|
||||
final TextEditingController controller;
|
||||
final String labelText;
|
||||
final TextInputType inputType;
|
||||
final String? errorText;
|
||||
final bool isPassword;
|
||||
final int maxLines;
|
||||
final FocusNode? focusNode;
|
||||
|
||||
@override
|
||||
State<CustomTextField> createState() => _CustomTextFieldState();
|
||||
}
|
||||
|
||||
class _CustomTextFieldState extends State<CustomTextField> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextField(
|
||||
controller: widget.controller,
|
||||
focusNode: widget.focusNode,
|
||||
keyboardType: widget.inputType,
|
||||
obscureText: widget.isPassword,
|
||||
enableSuggestions: !widget.isPassword,
|
||||
autocorrect: false,
|
||||
maxLines: widget.maxLines,
|
||||
textDirection: TextDirection.rtl,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
decoration: InputDecoration(
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 5, horizontal: 20),
|
||||
labelText: widget.labelText,
|
||||
alignLabelWithHint: true,
|
||||
labelStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: Theme.of(context).dividerColor),
|
||||
floatingLabelStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: Theme.of(context).dividerColor),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: BorderSide(color: Theme.of(context).cardColor, width: 1.5)
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: BorderSide(color: Theme.of(context).dividerColor, width: 1)
|
||||
),
|
||||
filled: true,
|
||||
fillColor: Theme.of(context).cardColor,
|
||||
errorText: widget.errorText,
|
||||
errorStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Colors.red
|
||||
),
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: const BorderSide(color: Colors.red, width: 1.5)
|
||||
),
|
||||
focusedErrorBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: const BorderSide(color: Colors.red, width: 1.5)
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
127
lib/widgets/dashboard_tile.dart
Normal file
127
lib/widgets/dashboard_tile.dart
Normal file
@ -0,0 +1,127 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:baligh_dashboard/screens/report_details_screen.dart';
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DashboardTile extends StatelessWidget {
|
||||
const DashboardTile({
|
||||
super.key,
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.address,
|
||||
required this.school,
|
||||
required this.type,
|
||||
required this.status,
|
||||
required this.age,
|
||||
required this.attachments,
|
||||
required this.createdAt,
|
||||
required this.description,
|
||||
required this.nationalIdNumber,
|
||||
required this.phone,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String name;
|
||||
final String address;
|
||||
final String school;
|
||||
final String type;
|
||||
final String status;
|
||||
final Timestamp createdAt;
|
||||
final int age;
|
||||
final String phone;
|
||||
final int nationalIdNumber;
|
||||
final String description;
|
||||
final List attachments;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
hoverColor: Colors.transparent,
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ReportDetailsScreen(
|
||||
id: id,
|
||||
name: name,
|
||||
age: age,
|
||||
phone: phone,
|
||||
address: address,
|
||||
school: school,
|
||||
nationalIdNumber: nationalIdNumber,
|
||||
description: description,
|
||||
attachments: attachments,
|
||||
type: type,
|
||||
createdAt: createdAt,
|
||||
status: status,
|
||||
)));
|
||||
},
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).cardColor,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
name,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
maxLines: 3,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
address,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
maxLines: 3,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
school,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
maxLines: 3,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
type,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
maxLines: 3,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
status,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
maxLines: 3,
|
||||
minFontSize: 8,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
94
lib/widgets/file_card.dart
Normal file
94
lib/widgets/file_card.dart
Normal file
@ -0,0 +1,94 @@
|
||||
import 'dart:html' as html;
|
||||
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:baligh_dashboard/widgets/app_toast.dart';
|
||||
import 'package:bot_toast/bot_toast.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
|
||||
class FileCard extends StatelessWidget {
|
||||
const FileCard(
|
||||
{super.key,
|
||||
required this.type,
|
||||
required this.filename,
|
||||
required this.fileUrl});
|
||||
|
||||
final String type;
|
||||
final String filename;
|
||||
final String fileUrl;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
hoverColor: Colors.transparent,
|
||||
onTap: () async {
|
||||
try {
|
||||
html.AnchorElement(href: fileUrl)
|
||||
..setAttribute('target', '_blank')
|
||||
..setAttribute('download', '$filename.pdf') // Specify the filename
|
||||
..click();
|
||||
|
||||
BotToast.showCustomText(toastBuilder: (_) {
|
||||
return const AppToast(text: "تم التحميل");
|
||||
});
|
||||
} catch (e) {
|
||||
print(e);
|
||||
BotToast.showCustomText(toastBuilder: (_) {
|
||||
return const AppToast(text: "حدث خطأ ما في التحميل");
|
||||
});
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
width: 180,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
padding: const EdgeInsets.all(8),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 3),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).cardColor,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
color: type == "image"
|
||||
? Colors.red.withOpacity(0.2)
|
||||
: type == "video"
|
||||
? Colors.blue.withOpacity(0.2)
|
||||
: Colors.grey.withOpacity(0.2),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
type == "image"
|
||||
? FontAwesomeIcons.image
|
||||
: type == "video"
|
||||
? FontAwesomeIcons.video
|
||||
: FontAwesomeIcons.microphone,
|
||||
color: type == "image"
|
||||
? Colors.red
|
||||
: type == "video"
|
||||
? Colors.blue
|
||||
: Colors.grey,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Expanded(
|
||||
child: AutoSizeText(
|
||||
filename,
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
minFontSize: 6,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
19
lib/widgets/small_spinner.dart
Normal file
19
lib/widgets/small_spinner.dart
Normal file
@ -0,0 +1,19 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SmallSpinner extends StatelessWidget {
|
||||
final Color? color;
|
||||
|
||||
const SmallSpinner({super.key, this.color});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 3,
|
||||
color: color ?? Colors.white,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
490
pubspec.lock
Normal file
490
pubspec.lock
Normal file
@ -0,0 +1,490 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
_flutterfire_internals:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _flutterfire_internals
|
||||
sha256: "5534e701a2c505fed1f0799e652dd6ae23bd4d2c4cf797220e5ced5764a7c1c2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.44"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.0"
|
||||
auto_size_text:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: auto_size_text
|
||||
sha256: "3f5261cd3fb5f2a9ab4e2fc3fba84fd9fcaac8821f20a1d4e71f557521b22599"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
bot_toast:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: bot_toast
|
||||
sha256: "6b93030a99a98335b8827ecd83021e92e885ffc61d261d3825ffdecdd17f3bdf"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.3"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
cloud_firestore:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: cloud_firestore
|
||||
sha256: bdc7607e9169ee3ce736bbbe6a81c2a6cb15c41379346b74f77f8e641211a17f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.4.4"
|
||||
cloud_firestore_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cloud_firestore_platform_interface
|
||||
sha256: "884fa34c6be2d9c7c1f4af86f90f36c0a3b3afef585a12b350a5d15368e7ec7a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.4.3"
|
||||
cloud_firestore_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cloud_firestore_web
|
||||
sha256: "6e621bbcc999f32db0bc6bfcb18d9991617ec20f8d6bf51b6a1571f5c324fafd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.3.2"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.18.0"
|
||||
csslib:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: csslib
|
||||
sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
dio:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dio
|
||||
sha256: "5598aa796bbf4699afd5c67c0f5f6e2ed542afc956884b9cd58c306966efc260"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.7.0"
|
||||
dio_web_adapter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dio_web_adapter
|
||||
sha256: "33259a9276d6cea88774a0000cfae0d861003497755969c92faa223108620dc8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
file_saver:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: file_saver
|
||||
sha256: "017a127de686af2d2fbbd64afea97052d95f2a0f87d19d25b87e097407bf9c1e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.14"
|
||||
firebase_auth:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: firebase_auth
|
||||
sha256: d453acec0d958ba0e25d41a9901b32cb77d1535766903dea7a61b2788c304596
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.3.1"
|
||||
firebase_auth_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_auth_platform_interface
|
||||
sha256: "78966c2ef774f5bf2a8381a307222867e9ece3509110500f7a138c115926aa65"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.4.7"
|
||||
firebase_auth_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_auth_web
|
||||
sha256: "77ad3b252badedd3f08dfa21a4c7fe244be96c6da3a4067f253b13ea5d32424c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.13.2"
|
||||
firebase_core:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: firebase_core
|
||||
sha256: "51dfe2fbf3a984787a2e7b8592f2f05c986bfedd6fdacea3f9e0a7beb334de96"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.6.0"
|
||||
firebase_core_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core_platform_interface
|
||||
sha256: e30da58198a6d4b49d5bce4e852f985c32cb10db329ebef9473db2b9f09ce810
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.3.0"
|
||||
firebase_core_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core_web
|
||||
sha256: f967a7138f5d2ffb1ce15950e2a382924239eaa521150a8f144af34e68b3b3e5
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.18.1"
|
||||
firebase_storage:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: firebase_storage
|
||||
sha256: "8a8a21f3a359129a1257e2e77ece1de9678f40e43876635b3d411388ee388729"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "12.3.2"
|
||||
firebase_storage_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_storage_platform_interface
|
||||
sha256: "462621bbdb5ab496518aa0f4785cb6db87763d5f1063aa228e1f65562937af1d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.1.31"
|
||||
firebase_storage_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_storage_web
|
||||
sha256: "40c52d5585ce63659b4b698fee0d47412ce499392ae3edf69c8e6141c22daf9a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.10.2"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
font_awesome_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: font_awesome_flutter
|
||||
sha256: "275ff26905134bcb59417cf60ad979136f1f8257f2f449914b2c3e05bbb4cd6f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.7.0"
|
||||
html:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: html
|
||||
sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.15.4"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.5"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.5"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.16+1"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.15.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.0"
|
||||
path_provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: f7544c346a0742aee1450f9e5c0f5269d7c602b9c95fdbcd9fb8f5b1df13b1cc
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.11"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.5"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.99"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.2"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.2"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.2.5"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
sdks:
|
||||
dart: ">=3.5.2 <4.0.0"
|
||||
flutter: ">=3.24.0"
|
108
pubspec.yaml
Normal file
108
pubspec.yaml
Normal file
@ -0,0 +1,108 @@
|
||||
name: baligh_dashboard
|
||||
description: "A new Flutter project."
|
||||
# The following line prevents the package from being accidentally published to
|
||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
|
||||
# The following defines the version and build number for your application.
|
||||
# A version number is three numbers separated by dots, like 1.2.43
|
||||
# followed by an optional build number separated by a +.
|
||||
# Both the version and the builder number may be overridden in flutter
|
||||
# build by specifying --build-name and --build-number, respectively.
|
||||
# In Android, build-name is used as versionName while build-number used as versionCode.
|
||||
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
|
||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
sdk: ^3.5.2
|
||||
|
||||
# Dependencies specify other packages that your package needs in order to work.
|
||||
# To automatically upgrade your package dependencies to the latest versions
|
||||
# consider running `flutter pub upgrade --major-versions`. Alternatively,
|
||||
# dependencies can be manually updated by changing the version numbers below to
|
||||
# the latest version available on pub.dev. To see which dependencies have newer
|
||||
# versions available, run `flutter pub outdated`.
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
auto_size_text: ^3.0.0
|
||||
font_awesome_flutter: ^10.7.0
|
||||
firebase_core: ^3.6.0
|
||||
firebase_auth: ^5.3.1
|
||||
cloud_firestore: ^5.4.3
|
||||
bot_toast: ^4.1.3
|
||||
firebase_storage: ^12.3.2
|
||||
file_saver: ^0.2.14
|
||||
html: ^0.15.4
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
# The "flutter_lints" package below contains a set of recommended lints to
|
||||
# encourage good coding practices. The lint set provided by the package is
|
||||
# activated in the `analysis_options.yaml` file located at the root of your
|
||||
# package. See that file for information about deactivating specific lint
|
||||
# rules and activating additional ones.
|
||||
flutter_lints: ^4.0.0
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
# The following section is specific to Flutter packages.
|
||||
flutter:
|
||||
|
||||
# The following line ensures that the Material Icons font is
|
||||
# included with your application, so that you can use the icons in
|
||||
# the material Icons class.
|
||||
uses-material-design: true
|
||||
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/to/resolution-aware-images
|
||||
|
||||
# For details regarding adding assets from package dependencies, see
|
||||
# https://flutter.dev/to/asset-from-package
|
||||
|
||||
# To add custom fonts to your application, add a fonts section here,
|
||||
# in this "flutter" section. Each entry in this list should have a
|
||||
# "family" key with the font family name, and a "fonts" key with a
|
||||
# list giving the asset and other descriptors for the font. For
|
||||
# example:
|
||||
fonts:
|
||||
- family: Cairo
|
||||
fonts:
|
||||
- asset: assets/fonts/Cairo-Light.ttf
|
||||
weight: 300
|
||||
- family: Cairo
|
||||
fonts:
|
||||
- asset: assets/fonts/Cairo-Regular.ttf
|
||||
weight: 400
|
||||
- family: Cairo
|
||||
fonts:
|
||||
- asset: assets/fonts/Cairo-Medium.ttf
|
||||
weight: 500
|
||||
- family: Cairo
|
||||
fonts:
|
||||
- asset: assets/fonts/Cairo-SemiBold.ttf
|
||||
weight: 600
|
||||
- family: Cairo
|
||||
fonts:
|
||||
- asset: assets/fonts/Cairo-Bold.ttf
|
||||
weight: 700
|
||||
#
|
||||
# For details regarding fonts from package dependencies,
|
||||
# see https://flutter.dev/to/font-from-package
|
30
test/widget_test.dart
Normal file
30
test/widget_test.dart
Normal file
@ -0,0 +1,30 @@
|
||||
// This is a basic Flutter widget test.
|
||||
//
|
||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||
// utility in the flutter_test package. For example, you can send tap and scroll
|
||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
||||
// tree, read text, and verify that the values of widget properties are correct.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:baligh_dashboard/main.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||
// Build our app and trigger a frame.
|
||||
await tester.pumpWidget(const MyApp());
|
||||
|
||||
// Verify that our counter starts at 0.
|
||||
expect(find.text('0'), findsOneWidget);
|
||||
expect(find.text('1'), findsNothing);
|
||||
|
||||
// Tap the '+' icon and trigger a frame.
|
||||
await tester.tap(find.byIcon(Icons.add));
|
||||
await tester.pump();
|
||||
|
||||
// Verify that our counter has incremented.
|
||||
expect(find.text('0'), findsNothing);
|
||||
expect(find.text('1'), findsOneWidget);
|
||||
});
|
||||
}
|
BIN
web/favicon.png
Normal file
BIN
web/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 917 B |
BIN
web/icons/Icon-192.png
Normal file
BIN
web/icons/Icon-192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
BIN
web/icons/Icon-512.png
Normal file
BIN
web/icons/Icon-512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.1 KiB |
BIN
web/icons/Icon-maskable-192.png
Normal file
BIN
web/icons/Icon-maskable-192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.5 KiB |
BIN
web/icons/Icon-maskable-512.png
Normal file
BIN
web/icons/Icon-maskable-512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
38
web/index.html
Normal file
38
web/index.html
Normal file
@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<!--
|
||||
If you are serving your web app in a path other than the root, change the
|
||||
href value below to reflect the base path you are serving from.
|
||||
|
||||
The path provided below has to start and end with a slash "/" in order for
|
||||
it to work correctly.
|
||||
|
||||
For more details:
|
||||
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
|
||||
|
||||
This is a placeholder for base href that will be replaced by the value of
|
||||
the `--base-href` argument provided to `flutter build`.
|
||||
-->
|
||||
<base href="$FLUTTER_BASE_HREF">
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
|
||||
<meta name="description" content="A new Flutter project.">
|
||||
|
||||
<!-- iOS meta tags & icons -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta name="apple-mobile-web-app-title" content="baligh_dashboard">
|
||||
<link rel="apple-touch-icon" href="icons/Icon-192.png">
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||
|
||||
<title>baligh_dashboard</title>
|
||||
<link rel="manifest" href="manifest.json">
|
||||
</head>
|
||||
<body>
|
||||
<script src="flutter_bootstrap.js" async></script>
|
||||
</body>
|
||||
</html>
|
35
web/manifest.json
Normal file
35
web/manifest.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "baligh_dashboard",
|
||||
"short_name": "baligh_dashboard",
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"background_color": "#0175C2",
|
||||
"theme_color": "#0175C2",
|
||||
"description": "A new Flutter project.",
|
||||
"orientation": "portrait-primary",
|
||||
"prefer_related_applications": false,
|
||||
"icons": [
|
||||
{
|
||||
"src": "icons/Icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/Icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/Icon-maskable-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "icons/Icon-maskable-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
]
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user