Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
M
Mapping-Software-Efficient-Routing-Algorithm
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
h703249754
Mapping-Software-Efficient-Routing-Algorithm
Commits
101cfa5c
Commit
101cfa5c
authored
Oct 04, 2025
by
aleenaasghar
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: Implement CSV address upload functionality
parent
7ee35231
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
182 additions
and
56 deletions
+182
-56
admin_dashboard_screen.dart
lib/screens/admin_dashboard_screen.dart
+73
-1
profile_screen.dart
lib/screens/profile_screen.dart
+73
-55
firestore_service.dart
lib/services/firestore_service.dart
+10
-0
pubspec.lock
pubspec.lock
+24
-0
pubspec.yaml
pubspec.yaml
+2
-0
No files found.
lib/screens/admin_dashboard_screen.dart
View file @
101cfa5c
import
'dart:convert'
;
import
'package:csv/csv.dart'
;
import
'package:file_picker/file_picker.dart'
;
import
'package:firebase_auth/firebase_auth.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_svg/flutter_svg.dart'
;
...
...
@@ -46,6 +49,74 @@ class _AdminDashboardScreenState extends State<AdminDashboardScreen> {
);
}
Future
<
void
>
_showUploadCsvDialog
()
async
{
try
{
final
result
=
await
FilePicker
.
platform
.
pickFiles
(
type:
FileType
.
custom
,
allowedExtensions:
[
'csv'
],
);
if
(
result
==
null
||
!
mounted
)
return
;
final
file
=
result
.
files
.
first
;
final
bytes
=
file
.
bytes
;
if
(
bytes
==
null
)
return
;
final
content
=
utf8
.
decode
(
bytes
);
final
list
=
const
CsvToListConverter
().
convert
(
content
);
if
(
list
.
isNotEmpty
)
{
list
.
removeAt
(
0
);
// remove header
}
final
addresses
=
list
.
map
((
row
)
{
try
{
return
DeliveryAddress
(
streetAddress:
row
[
0
].
toString
(),
city:
row
[
1
].
toString
(),
state:
row
[
2
].
toString
(),
zipCode:
row
[
3
].
toString
(),
notes:
row
.
length
>
4
?
row
[
4
].
toString
()
:
null
,
);
}
catch
(
e
)
{
print
(
'Error parsing row:
$row
, error:
$e
'
);
return
null
;
}
}).
where
((
address
)
=>
address
!=
null
).
cast
<
DeliveryAddress
>().
toList
();
if
(!
mounted
)
return
;
// Check if the widget is still in the tree
if
(
addresses
.
isNotEmpty
)
{
try
{
await
_firestoreService
.
saveAddressesFromCsv
(
addresses
);
if
(
mounted
)
{
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
const
SnackBar
(
content:
Text
(
'Addresses uploaded successfully!'
)),
);
}
}
catch
(
e
)
{
if
(
mounted
)
{
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
SnackBar
(
content:
Text
(
'Error uploading addresses:
$e
'
)),
);
}
}
}
else
{
if
(
mounted
)
{
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
const
SnackBar
(
content:
Text
(
'No valid addresses found in the CSV file.'
)),
);
}
}
}
catch
(
e
)
{
if
(
mounted
)
{
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
SnackBar
(
content:
Text
(
'Error picking file:
$e
'
)),
);
}
}
}
void
_deleteAddress
(
String
addressId
)
{
_firestoreService
.
deleteAddress
(
addressId
);
}
...
...
@@ -61,6 +132,7 @@ class _AdminDashboardScreenState extends State<AdminDashboardScreen> {
password:
_passwordController
.
text
.
trim
(),
);
}
on
FirebaseAuthException
catch
(
e
)
{
if
(!
mounted
)
return
;
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
SnackBar
(
content:
Text
(
"Login Failed:
${e.message}
"
)),
);
...
...
@@ -139,7 +211,7 @@ class _AdminDashboardScreenState extends State<AdminDashboardScreen> {
),
const
SizedBox
(
width:
16
),
OutlinedButton
.
icon
(
onPressed:
null
,
// Disabled for now
onPressed:
_showUploadCsvDialog
,
icon:
const
Icon
(
Icons
.
upload_file
),
label:
const
Text
(
'Upload CSV'
),
style:
OutlinedButton
.
styleFrom
(
...
...
lib/screens/profile_screen.dart
View file @
101cfa5c
import
'dart:typed_data'
;
import
'package:firebase_auth/firebase_auth.dart'
;
import
'package:flutter/material.dart'
;
import
'package:cloud_firestore/cloud_firestore.dart'
;
import
'package:image_picker/image_picker.dart'
;
import
'package:firebase_storage/firebase_storage.dart'
;
import
'dart:io'
;
import
'../colors.dart'
;
import
'../services/google_auth_service.dart'
;
...
...
@@ -26,7 +27,8 @@ class _ProfileScreenState extends State<ProfileScreen> {
final
TextEditingController
_companyController
=
TextEditingController
();
String
?
_profileImageUrl
;
File
?
_selectedImage
;
XFile
?
_selectedImage
;
Uint8List
?
_selectedImageBytes
;
// GraphGo specific stats
int
_totalRoutes
=
0
;
...
...
@@ -80,11 +82,13 @@ class _ProfileScreenState extends State<ProfileScreen> {
}
catch
(
e
)
{
print
(
'Error loading user data:
$e
'
);
}
finally
{
if
(
mounted
)
{
setState
(()
{
_isLoading
=
false
;
});
}
}
}
Future
<
void
>
_createUserProfile
()
async
{
try
{
...
...
@@ -126,6 +130,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
totalEfficiency
+=
(
routeData
[
'efficiency'
]
??
0.0
).
toDouble
();
}
if
(
mounted
)
{
setState
(()
{
_totalRoutes
=
totalRoutes
;
_totalDeliveries
=
totalDeliveries
;
...
...
@@ -133,6 +138,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
_averageEfficiency
=
totalRoutes
>
0
?
totalEfficiency
/
totalRoutes
:
0.0
;
});
}
}
}
catch
(
e
)
{
print
(
'Error loading user stats:
$e
'
);
}
...
...
@@ -149,12 +155,15 @@ class _ProfileScreenState extends State<ProfileScreen> {
);
if
(
image
!=
null
)
{
final
bytes
=
await
image
.
readAsBytes
();
setState
(()
{
_selectedImage
=
File
(
image
.
path
);
_selectedImage
=
image
;
_selectedImageBytes
=
bytes
;
});
}
}
catch
(
e
)
{
print
(
'Error picking image:
$e
'
);
if
(!
mounted
)
return
;
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
SnackBar
(
content:
Text
(
'Error selecting image:
$e
'
)),
);
...
...
@@ -162,7 +171,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
}
Future
<
void
>
_uploadProfileImage
()
async
{
if
(
_selectedImage
==
null
)
return
;
if
(
_selectedImage
==
null
||
_selectedImageBytes
==
null
)
return
;
try
{
setState
(()
{
...
...
@@ -175,26 +184,31 @@ class _ProfileScreenState extends State<ProfileScreen> {
.
child
(
'profile_images'
)
.
child
(
fileName
);
UploadTask
uploadTask
=
storageRef
.
put
File
(
_selectedImage
!);
UploadTask
uploadTask
=
storageRef
.
put
Data
(
_selectedImageBytes
!);
TaskSnapshot
snapshot
=
await
uploadTask
;
String
downloadUrl
=
await
snapshot
.
ref
.
getDownloadURL
();
if
(
mounted
)
{
setState
(()
{
_profileImageUrl
=
downloadUrl
;
});
}
print
(
'✅ Profile image uploaded:
$downloadUrl
'
);
}
catch
(
e
)
{
print
(
'Error uploading profile image:
$e
'
);
if
(!
mounted
)
return
;
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
SnackBar
(
content:
Text
(
'Error uploading image:
$e
'
)),
);
}
finally
{
if
(
mounted
)
{
setState
(()
{
_isLoading
=
false
;
});
}
}
}
Future
<
void
>
_saveProfile
()
async
{
try
{
...
...
@@ -203,7 +217,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
});
// Upload profile image if selected
if
(
_selectedImage
!=
null
)
{
if
(
_selectedImage
Bytes
!=
null
)
{
await
_uploadProfileImage
();
}
...
...
@@ -220,6 +234,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
'updatedAt'
:
FieldValue
.
serverTimestamp
(),
});
if
(
mounted
)
{
setState
(()
{
_isEditing
=
false
;
});
...
...
@@ -230,17 +245,21 @@ class _ProfileScreenState extends State<ProfileScreen> {
backgroundColor:
Colors
.
green
,
),
);
}
}
catch
(
e
)
{
print
(
'Error saving profile:
$e
'
);
if
(!
mounted
)
return
;
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
SnackBar
(
content:
Text
(
'Error saving profile:
$e
'
)),
);
}
finally
{
if
(
mounted
)
{
setState
(()
{
_isLoading
=
false
;
});
}
}
}
Widget
_buildStatCard
(
String
title
,
String
value
,
IconData
icon
,
Color
color
)
{
return
Expanded
(
...
...
@@ -280,7 +299,6 @@ class _ProfileScreenState extends State<ProfileScreen> {
@override
Widget
build
(
BuildContext
context
)
{
if
(
_isLoading
)
{
return
Scaffold
(
appBar:
AppBar
(
...
...
@@ -320,7 +338,8 @@ class _ProfileScreenState extends State<ProfileScreen> {
setState
(()
{
_isEditing
=
!
_isEditing
;
if
(!
_isEditing
)
{
// Reset to original values
_selectedImage
=
null
;
_selectedImageBytes
=
null
;
_loadUserData
();
}
});
...
...
@@ -345,12 +364,12 @@ class _ProfileScreenState extends State<ProfileScreen> {
children:
[
CircleAvatar
(
radius:
60
,
backgroundImage:
_selectedImage
!=
null
?
FileImage
(
_selectedImage
!)
backgroundImage:
_selectedImage
Bytes
!=
null
?
MemoryImage
(
_selectedImageBytes
!)
:
(
_profileImageUrl
!=
null
?
NetworkImage
(
_profileImageUrl
!)
as
ImageProvider
:
null
),
child:
_selectedImage
==
null
&&
_profileImageUrl
==
null
child:
_selectedImage
Bytes
==
null
&&
_profileImageUrl
==
null
?
const
Icon
(
Icons
.
person
,
size:
60
)
:
null
,
),
...
...
@@ -588,7 +607,6 @@ class _ProfileScreenState extends State<ProfileScreen> {
Expanded
(
child:
ElevatedButton
.
icon
(
onPressed:
()
{
// Navigate to route history
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
const
SnackBar
(
content:
Text
(
'Route history coming soon!'
)),
);
...
...
lib/services/firestore_service.dart
View file @
101cfa5c
...
...
@@ -16,6 +16,16 @@ class FirestoreService {
return
_db
.
collection
(
_collectionPath
).
doc
(
address
.
id
).
set
(
address
.
toJson
());
}
// Save a list of addresses from a CSV
Future
<
void
>
saveAddressesFromCsv
(
List
<
DeliveryAddress
>
addresses
)
async
{
final
batch
=
_db
.
batch
();
for
(
final
address
in
addresses
)
{
final
docRef
=
_db
.
collection
(
_collectionPath
).
doc
(
address
.
id
);
batch
.
set
(
docRef
,
address
.
toJson
());
}
await
batch
.
commit
();
}
// Delete an address
Future
<
void
>
deleteAddress
(
String
addressId
)
{
return
_db
.
collection
(
_collectionPath
).
doc
(
addressId
).
delete
();
...
...
pubspec.lock
View file @
101cfa5c
...
...
@@ -193,6 +193,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.2"
csv:
dependency: "direct main"
description:
name: csv
sha256: c6aa2679b2a18cb57652920f674488d89712efaf4d3fdf2e537215b35fc19d6c
url: "https://pub.dev"
source: hosted
version: "6.0.0"
cupertino_icons:
dependency: "direct main"
description:
...
...
@@ -233,6 +241,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.1"
file_picker:
dependency: "direct main"
description:
name: file_picker
sha256: "1bbf65dd997458a08b531042ec3794112a6c39c07c37ff22113d2e7e4f81d4e4"
url: "https://pub.dev"
source: hosted
version: "6.2.1"
file_selector_linux:
dependency: transitive
description:
...
...
@@ -1156,6 +1172,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.0"
win32:
dependency: transitive
description:
name: win32
sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03"
url: "https://pub.dev"
source: hosted
version: "5.14.0"
xdg_directories:
dependency: transitive
description:
...
...
pubspec.yaml
View file @
101cfa5c
...
...
@@ -36,6 +36,8 @@ dependencies:
http
:
^1.1.0
uuid
:
^4.2.1
image_picker
:
^1.0.4
file_picker
:
^6.2.0
csv
:
^6.0.0
dev_dependencies
:
flutter_test
:
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment