Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
Sai Srinivas
gen_rentals
Commits
5ac94fe2
Commit
5ac94fe2
authored
Oct 18, 2025
by
Sai Srinivas
Browse files
New Screen and apis
parent
df116895
Changes
29
Expand all
Show whitespace changes
Inline
Side-by-side
android/app/build.gradle.kts
View file @
5ac94fe2
...
@@ -25,7 +25,7 @@ android {
...
@@ -25,7 +25,7 @@ android {
applicationId
=
"in.webgrid.genrentals"
applicationId
=
"in.webgrid.genrentals"
// You can update the following values to match your application needs.
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk
=
flutter
.
minSdkVersion
minSdk
=
23
targetSdk
=
36
targetSdk
=
36
versionCode
=
flutter
.
versionCode
versionCode
=
flutter
.
versionCode
versionName
=
flutter
.
versionName
versionName
=
flutter
.
versionName
...
...
android/app/src/main/AndroidManifest.xml
View file @
5ac94fe2
<manifest
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<manifest
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<uses-permission
android:name=
"android.permission.CAMERA"
/>
<uses-permission
android:name=
"android.permission.READ_EXTERNAL_STORAGE"
/>
<uses-permission
android:name=
"android.permission.WRITE_EXTERNAL_STORAGE"
/>
<application
<application
android:label=
"Gen Rentals"
android:label=
"Gen Rentals"
android:name=
"${applicationName}"
android:name=
"${applicationName}"
...
...
assets/svg/cross_down_arrow.svg
View file @
5ac94fe2
<svg
width=
"14"
height=
"14"
viewBox=
"0 0 14 14"
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
>
<svg
width=
"14"
height=
"14"
viewBox=
"0 0 14 14"
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
>
<path
d=
"M13.4376 1.70711C13.8281 1.31658 13.8281 0.683417 13.4376 0.292893C13.0471 -0.0976311 12.4139 -0.097631 12.0234 0.292893L12.7305 1L13.4376 1.70711ZM-3.43156e-05 12.7305C-3.40162e-05 13.2828 0.447681 13.7305 0.999966 13.7305L9.99997 13.7305C10.5523 13.7305 11 13.2828 11 12.7305C11 12.1782 10.5523 11.7305 9.99997 11.7305L1.99997 11.7305L1.99997 3.7305C1.99997 3.17822 1.55225 2.7305 0.999965 2.7305C0.447681 2.7305 -3.46572e-05 3.17822 -3.42735e-05 3.7305L-3.43156e-05 12.7305ZM12.7305 1L12.0234 0.292893L0.292859 12.0234L0.999966 12.7305L1.70707 13.4376L13.4376 1.70711L12.7305 1Z"
fill=
"#
4CAF5
0"
/>
<path
d=
"M13.4376 1.70711C13.8281 1.31658 13.8281 0.683417 13.4376 0.292893C13.0471 -0.0976311 12.4139 -0.097631 12.0234 0.292893L12.7305 1L13.4376 1.70711ZM-3.43156e-05 12.7305C-3.40162e-05 13.2828 0.447681 13.7305 0.999966 13.7305L9.99997 13.7305C10.5523 13.7305 11 13.2828 11 12.7305C11 12.1782 10.5523 11.7305 9.99997 11.7305L1.99997 11.7305L1.99997 3.7305C1.99997 3.17822 1.55225 2.7305 0.999965 2.7305C0.447681 2.7305 -3.46572e-05 3.17822 -3.42735e-05 3.7305L-3.43156e-05 12.7305ZM12.7305 1L12.0234 0.292893L0.292859 12.0234L0.999966 12.7305L1.70707 13.4376L13.4376 1.70711L12.7305 1Z"
fill=
"#
F0000
0"
/>
</svg>
</svg>
assets/svg/cross_up_arrow.svg
View file @
5ac94fe2
<svg
width=
"14"
height=
"14"
viewBox=
"0 0 14 14"
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
>
<svg
width=
"14"
height=
"14"
viewBox=
"0 0 14 14"
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
>
<path
d=
"M0.292893 12.0234C-0.0976311 12.4139 -0.0976311 13.0471 0.292893 13.4376C0.683418 13.8281 1.31658 13.8281 1.70711 13.4376L1 12.7305L0.292893 12.0234ZM13.7305 0.999965C13.7305 0.44768 13.2828 -3.50516e-05 12.7305 -3.53045e-05L3.7305 -3.45037e-05C3.17822 -3.48408e-05 2.7305 0.447681 2.7305 0.999965C2.7305 1.55225 3.17822 1.99996 3.7305 1.99997L11.7305 1.99996L11.7305 9.99996C11.7305 10.5522 12.1782 11 12.7305 11C13.2828 11 13.7305 10.5522 13.7305 9.99997L13.7305 0.999965ZM1 12.7305L1.70711 13.4376L13.4376 1.70707L12.7305 0.999965L12.0234 0.292858L0.292893 12.0234L1 12.7305Z"
fill=
"#
ED3424
"
/>
<path
d=
"M0.292893 12.0234C-0.0976311 12.4139 -0.0976311 13.0471 0.292893 13.4376C0.683418 13.8281 1.31658 13.8281 1.70711 13.4376L1 12.7305L0.292893 12.0234ZM13.7305 0.999965C13.7305 0.44768 13.2828 -3.50516e-05 12.7305 -3.53045e-05L3.7305 -3.45037e-05C3.17822 -3.48408e-05 2.7305 0.447681 2.7305 0.999965C2.7305 1.55225 3.17822 1.99996 3.7305 1.99997L11.7305 1.99996L11.7305 9.99996C11.7305 10.5522 12.1782 11 12.7305 11C13.2828 11 13.7305 10.5522 13.7305 9.99997L13.7305 0.999965ZM1 12.7305L1.70711 13.4376L13.4376 1.70707L12.7305 0.999965L12.0234 0.292858L0.292893 12.0234L1 12.7305Z"
fill=
"#
4CAF50
"
/>
</svg>
</svg>
assets/svg/edit_ic.svg
0 → 100644
View file @
5ac94fe2
<svg
width=
"16"
height=
"16"
viewBox=
"0 0 16 16"
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
>
<g
clip-path=
"url(#clip0_591_316)"
>
<mask
id=
"mask0_591_316"
style=
"mask-type:luminance"
maskUnits=
"userSpaceOnUse"
x=
"0"
y=
"0"
width=
"16"
height=
"16"
>
<path
d=
"M16 0H0V16H16V0Z"
fill=
"white"
/>
</mask>
<g
mask=
"url(#mask0_591_316)"
>
<path
d=
"M14.6666 8.66699V10.0003C14.6666 13.3337 13.3333 14.667 9.99992 14.667H5.99992C2.66659 14.667 1.33325 13.3337 1.33325 10.0003V8.98699"
stroke=
"#2D2D2D"
stroke-linecap=
"round"
stroke-linejoin=
"round"
/>
<path
d=
"M7.33325 1.33301H5.99992C2.66659 1.33301 1.33325 2.66634 1.33325 5.99967"
stroke=
"#2D2D2D"
stroke-linecap=
"round"
stroke-linejoin=
"round"
/>
<path
d=
"M13.2866 6.00586L13.9866 5.30586C14.8933 4.39919 15.3199 3.34586 13.9866 2.01253C12.6533 0.679192 11.5999 1.10586 10.6933 2.01253L5.43992 7.26587C5.23992 7.46587 5.03992 7.85921 4.99992 8.14587L4.71325 10.1525C4.60659 10.8792 5.11992 11.3859 5.84659 11.2859L7.85327 10.9992C8.13327 10.9592 8.5266 10.7592 8.73327 10.5592L10.8533 8.43921L11.3399 7.95254"
stroke=
"#2D2D2D"
stroke-miterlimit=
"10"
stroke-linecap=
"round"
stroke-linejoin=
"round"
/>
<path
d=
"M9.94019 2.7666C10.3869 4.35993 11.6335 5.6066 13.2335 6.05993"
stroke=
"#2D2D2D"
stroke-miterlimit=
"10"
stroke-linecap=
"round"
stroke-linejoin=
"round"
/>
</g>
</g>
<defs>
<clipPath
id=
"clip0_591_316"
>
<rect
width=
"16"
height=
"16"
fill=
"white"
/>
</clipPath>
</defs>
</svg>
assets/svg/person_ic.svg
0 → 100644
View file @
5ac94fe2
<svg
width=
"17"
height=
"17"
viewBox=
"0 0 17 17"
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
>
<g
clip-path=
"url(#clip0_383_1601)"
>
<path
d=
"M4.00647 4.47004C4.00647 6.93496 6.0116 8.94009 8.47651 8.94009C10.9414 8.94009 12.9466 6.93496 12.9466 4.47004C12.9466 2.00513 10.9414 0 8.47651 0C6.0116 0 4.00647 2.00513 4.00647 4.47004Z"
fill=
"#45C1F1"
/>
<path
d=
"M12.9466 4.47004C12.9466 2.00513 10.9415 0 8.47656 0V8.94009C10.9415 8.94009 12.9466 6.93496 12.9466 4.47004Z"
fill=
"#44A4EC"
/>
<path
d=
"M1.02649 16.4565C1.02649 16.7311 1.24863 16.9532 1.52316 16.9532H15.43C15.7045 16.9532 15.9266 16.7311 15.9266 16.4565C15.9266 12.8964 13.03 9.93359 9.4699 9.93359H7.48322C3.92308 9.93359 1.02649 12.8964 1.02649 16.4565Z"
fill=
"#45C1F1"
/>
<path
d=
"M9.46991 9.93359H8.47656V16.9532H15.43C15.7045 16.9532 15.9266 16.7311 15.9266 16.4565C15.9266 12.8964 13.03 9.93359 9.46991 9.93359Z"
fill=
"#44A4EC"
/>
</g>
<defs>
<clipPath
id=
"clip0_383_1601"
>
<rect
width=
"16.9531"
height=
"16.9531"
fill=
"white"
/>
</clipPath>
</defs>
</svg>
lib/Models/CommonResponse.dart
View file @
5ac94fe2
...
@@ -6,16 +6,17 @@ class CommonResponse {
...
@@ -6,16 +6,17 @@ class CommonResponse {
CommonResponse
({
this
.
error
,
this
.
balance
,
this
.
message
});
CommonResponse
({
this
.
error
,
this
.
balance
,
this
.
message
});
CommonResponse
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
CommonResponse
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
error
=
json
[
'error'
]
;
error
=
int
.
tryParse
(
json
[
'error'
]?.
toString
()
??
''
)
;
balance
=
json
[
'balance'
];
balance
=
int
.
tryParse
(
json
[
'balance'
]
?.
toString
()
??
''
)
;
message
=
json
[
'message'
];
message
=
json
[
'message'
]
?.
toString
()
;
}
}
Map
<
String
,
dynamic
>
toJson
()
{
Map
<
String
,
dynamic
>
toJson
()
{
final
Map
<
String
,
dynamic
>
data
=
new
Map
<
String
,
dynamic
>();
return
{
data
[
'error'
]
=
this
.
error
;
'error'
:
error
,
data
[
'balance'
]
=
this
.
balance
;
'balance'
:
balance
,
data
[
'message'
]
=
this
.
message
;
'message'
:
message
,
return
data
;
}
;
}
}
}
}
lib/Models/HelpAndEnquiryModels/TicketChatDisplayResponse.dart
0 → 100644
View file @
5ac94fe2
class
TicketChatDisplayResponse
{
String
?
status
;
String
?
ticketId
;
List
<
FeedBacks
>?
feedBacks
;
String
?
error
;
String
?
message
;
TicketChatDisplayResponse
(
{
this
.
status
,
this
.
ticketId
,
this
.
feedBacks
,
this
.
error
,
this
.
message
});
TicketChatDisplayResponse
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
status
=
json
[
'status'
];
ticketId
=
json
[
'ticket_id'
];
if
(
json
[
'feed_backs'
]
!=
null
)
{
feedBacks
=
<
FeedBacks
>[];
json
[
'feed_backs'
].
forEach
((
v
)
{
feedBacks
!.
add
(
new
FeedBacks
.
fromJson
(
v
));
});
}
error
=
json
[
'error'
];
message
=
json
[
'message'
];
}
Map
<
String
,
dynamic
>
toJson
()
{
final
Map
<
String
,
dynamic
>
data
=
new
Map
<
String
,
dynamic
>();
data
[
'status'
]
=
this
.
status
;
data
[
'ticket_id'
]
=
this
.
ticketId
;
if
(
this
.
feedBacks
!=
null
)
{
data
[
'feed_backs'
]
=
this
.
feedBacks
!.
map
((
v
)
=>
v
.
toJson
()).
toList
();
}
data
[
'error'
]
=
this
.
error
;
data
[
'message'
]
=
this
.
message
;
return
data
;
}
}
class
FeedBacks
{
String
?
id
;
String
?
text
;
List
<
String
>?
imgNames
;
List
<
String
>?
images
;
String
?
user
;
String
?
userName
;
String
?
createdDatetime
;
String
?
userImg
;
FeedBacks
(
{
this
.
id
,
this
.
text
,
this
.
imgNames
,
this
.
images
,
this
.
user
,
this
.
userName
,
this
.
createdDatetime
});
FeedBacks
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
id
=
json
[
'id'
];
text
=
json
[
'text'
];
imgNames
=
json
[
'img_names'
].
cast
<
String
>();
images
=
json
[
'images'
].
cast
<
String
>();
user
=
json
[
'user'
];
userName
=
json
[
'user_name'
];
createdDatetime
=
json
[
'created_datetime'
];
userImg
=
json
[
'user_img'
];
}
Map
<
String
,
dynamic
>
toJson
()
{
final
Map
<
String
,
dynamic
>
data
=
new
Map
<
String
,
dynamic
>();
data
[
'id'
]
=
this
.
id
;
data
[
'text'
]
=
this
.
text
;
data
[
'img_names'
]
=
this
.
imgNames
;
data
[
'images'
]
=
this
.
images
;
data
[
'user'
]
=
this
.
user
;
data
[
'user_name'
]
=
this
.
userName
;
data
[
'created_datetime'
]
=
this
.
createdDatetime
;
data
[
'user_img'
]
=
this
.
userImg
;
return
data
;
}
}
lib/Models/HelpAndEnquiryModels/ticketListResponse.dart
0 → 100644
View file @
5ac94fe2
class
TicketListResponse
{
final
Tickets
?
tickets
;
final
String
?
error
;
final
String
?
message
;
TicketListResponse
({
this
.
tickets
,
this
.
error
,
this
.
message
});
factory
TicketListResponse
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
TicketListResponse
(
tickets:
json
[
'tickets'
]
!=
null
?
Tickets
.
fromJson
(
json
[
'tickets'
])
:
null
,
error:
json
[
'error'
]?.
toString
(),
message:
json
[
'message'
]?.
toString
(),
);
}
Map
<
String
,
dynamic
>
toJson
()
=>
{
if
(
tickets
!=
null
)
'tickets'
:
tickets
!.
toJson
(),
'error'
:
error
,
'message'
:
message
,
};
}
class
Tickets
{
final
List
<
TicketItem
>?
closed
;
final
List
<
TicketItem
>?
inProgress
;
Tickets
({
this
.
closed
,
this
.
inProgress
});
factory
Tickets
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
Tickets
(
closed:
(
json
[
'closed'
]
as
List
?)
?.
map
((
v
)
=>
TicketItem
.
fromJson
(
v
))
.
toList
(),
inProgress:
(
json
[
'in_progress'
]
as
List
?)
?.
map
((
v
)
=>
TicketItem
.
fromJson
(
v
))
.
toList
(),
);
}
Map
<
String
,
dynamic
>
toJson
()
=>
{
if
(
closed
!=
null
)
'closed'
:
closed
!.
map
((
v
)
=>
v
.
toJson
()).
toList
(),
if
(
inProgress
!=
null
)
'in_progress'
:
inProgress
!.
map
((
v
)
=>
v
.
toJson
()).
toList
(),
};
}
class
TicketItem
{
final
String
?
id
;
final
String
?
ticketNumber
;
final
String
?
type
;
final
String
?
date
;
TicketItem
({
this
.
id
,
this
.
ticketNumber
,
this
.
type
,
this
.
date
});
factory
TicketItem
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
TicketItem
(
id:
json
[
'id'
]?.
toString
(),
ticketNumber:
json
[
'ticket_number'
]?.
toString
(),
type:
json
[
'type'
]?.
toString
(),
date:
json
[
'date'
]?.
toString
(),
);
}
Map
<
String
,
dynamic
>
toJson
()
=>
{
'id'
:
id
,
'ticket_number'
:
ticketNumber
,
'type'
:
type
,
'date'
:
date
,
};
}
lib/Models/TicketChatDisplayResponse.dart
deleted
100644 → 0
View file @
df116895
class
TicketChatDisplayResponse
{
List
<
Ticket
>?
ticket
;
int
?
error
;
String
?
message
;
TicketChatDisplayResponse
({
this
.
ticket
,
this
.
error
,
this
.
message
});
TicketChatDisplayResponse
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
if
(
json
[
'ticket'
]
!=
null
)
{
ticket
=
<
Ticket
>[];
json
[
'ticket'
].
forEach
((
v
)
{
ticket
!.
add
(
new
Ticket
.
fromJson
(
v
));
});
}
error
=
json
[
'error'
];
message
=
json
[
'message'
];
}
Map
<
String
,
dynamic
>
toJson
()
{
final
Map
<
String
,
dynamic
>
data
=
new
Map
<
String
,
dynamic
>();
if
(
this
.
ticket
!=
null
)
{
data
[
'ticket'
]
=
this
.
ticket
!.
map
((
v
)
=>
v
.
toJson
()).
toList
();
}
data
[
'error'
]
=
this
.
error
;
data
[
'message'
]
=
this
.
message
;
return
data
;
}
}
class
Ticket
{
String
?
tid
;
String
?
datetime
;
String
?
msg
;
String
?
type
;
Ticket
({
this
.
tid
,
this
.
datetime
,
this
.
msg
,
this
.
type
});
Ticket
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
tid
=
json
[
'tid'
];
datetime
=
json
[
'datetime'
];
msg
=
json
[
'msg'
];
type
=
json
[
'type'
];
}
Map
<
String
,
dynamic
>
toJson
()
{
final
Map
<
String
,
dynamic
>
data
=
new
Map
<
String
,
dynamic
>();
data
[
'tid'
]
=
this
.
tid
;
data
[
'datetime'
]
=
this
.
datetime
;
data
[
'msg'
]
=
this
.
msg
;
data
[
'type'
]
=
this
.
type
;
return
data
;
}
}
lib/Models/ticketListResponse.dart
deleted
100644 → 0
View file @
df116895
class
TicketListResponse
{
List
<
Ticket
>?
ticket
;
int
?
error
;
String
?
message
;
TicketListResponse
({
this
.
ticket
,
this
.
error
,
this
.
message
});
TicketListResponse
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
if
(
json
[
'ticket'
]
!=
null
)
{
ticket
=
<
Ticket
>[];
json
[
'ticket'
].
forEach
((
v
)
{
ticket
!.
add
(
new
Ticket
.
fromJson
(
v
));
});
}
error
=
json
[
'error'
];
message
=
json
[
'message'
];
}
Map
<
String
,
dynamic
>
toJson
()
{
final
Map
<
String
,
dynamic
>
data
=
new
Map
<
String
,
dynamic
>();
if
(
this
.
ticket
!=
null
)
{
data
[
'ticket'
]
=
this
.
ticket
!.
map
((
v
)
=>
v
.
toJson
()).
toList
();
}
data
[
'error'
]
=
this
.
error
;
data
[
'message'
]
=
this
.
message
;
return
data
;
}
}
class
Ticket
{
String
?
tid
;
String
?
datetime
;
String
?
status
;
String
?
subject
;
Ticket
({
this
.
tid
,
this
.
datetime
,
this
.
status
,
this
.
subject
});
Ticket
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
tid
=
json
[
'tid'
];
datetime
=
json
[
'datetime'
];
status
=
json
[
'status'
];
subject
=
json
[
'subject'
];
}
Map
<
String
,
dynamic
>
toJson
()
{
final
Map
<
String
,
dynamic
>
data
=
new
Map
<
String
,
dynamic
>();
data
[
'tid'
]
=
this
.
tid
;
data
[
'datetime'
]
=
this
.
datetime
;
data
[
'status'
]
=
this
.
status
;
data
[
'subject'
]
=
this
.
subject
;
return
data
;
}
}
lib/Notifier/HelpAndEnquiryProvider.dart
0 → 100644
View file @
5ac94fe2
import
'dart:io'
;
import
'package:flutter/material.dart'
;
import
'package:gen_rentals/Services/api_calling.dart'
;
import
'../Models/HelpAndEnquiryModels/TicketChatDisplayResponse.dart'
;
import
'../Models/HelpAndEnquiryModels/ticketListResponse.dart'
;
import
'../Utility/CustomSnackbar.dart'
;
class
HelpAndEnquiryProvider
extends
ChangeNotifier
{
bool
_isLoading
=
false
;
bool
get
isLoading
=>
_isLoading
;
String
?
_message
;
String
?
get
message
=>
_message
;
int
?
_balance
;
int
?
get
balance
=>
_balance
;
Future
<
bool
>
submitEnquiry
({
required
String
sessionId
,
required
String
accId
,
required
String
name
,
required
String
email
,
required
String
mobile
,
required
String
requirement
,
required
String
note
,
})
async
{
try
{
_isLoading
=
true
;
_message
=
null
;
notifyListeners
();
final
response
=
await
ApiCalling
.
submitEnquiryApi
(
sessionId
,
accId
,
name
,
email
,
mobile
,
requirement
,
note
,
);
_isLoading
=
false
;
if
(
response
!=
null
)
{
_message
=
response
.
message
;
_balance
=
response
.
balance
;
notifyListeners
();
if
(
response
.
error
==
0
)
{
// success
debugPrint
(
"✅ Enquiry submitted successfully:
${response.message}
"
);
return
true
;
}
else
{
// failed
debugPrint
(
"⚠️ Failed to submit enquiry:
${response.message}
"
);
return
false
;
}
}
else
{
_isLoading
=
false
;
notifyListeners
();
debugPrint
(
"❌ Null response received."
);
return
false
;
}
}
catch
(
e
)
{
_isLoading
=
false
;
notifyListeners
();
debugPrint
(
"❌ Exception in submitEnquiry:
$e
"
);
return
false
;
}
}
String
?
_errorMessage
;
TicketListResponse
?
_ticketListResponse
;
String
?
get
errorMessage
=>
_errorMessage
;
TicketListResponse
?
get
ticketListResponse
=>
_ticketListResponse
;
/// ✅ Fetch ticket list from API
Future
<
void
>
fetchTicketList
({
required
String
sessionId
,
required
String
accId
,
})
async
{
_isLoading
=
true
;
_errorMessage
=
null
;
notifyListeners
();
try
{
final
response
=
await
ApiCalling
.
fetchTicketListApi
(
sessionId
,
accId
,);
if
(
response
!=
null
&&
response
.
error
==
"0"
)
{
_ticketListResponse
=
response
;
}
else
{
_errorMessage
=
response
?.
message
??
"Something went wrong"
;
}
}
catch
(
e
)
{
debugPrint
(
"❌ Provider Error (fetchTicketList):
$e
"
);
_errorMessage
=
e
.
toString
();
}
finally
{
_isLoading
=
false
;
notifyListeners
();
}
}
/// ✅ Optional: Clear state (useful on logout or refresh)
void
clearTickets
()
{
_ticketListResponse
=
null
;
_errorMessage
=
null
;
notifyListeners
();
}
TicketChatDisplayResponse
?
_chatResponse
;
TicketChatDisplayResponse
?
get
chatResponse
=>
_chatResponse
;
/// Fetch chat details for a specific ticket
Future
<
void
>
fetchTicketChatDisplay
({
required
String
sessionId
,
required
String
accId
,
required
String
ticketId
,
})
async
{
_isLoading
=
true
;
_errorMessage
=
null
;
notifyListeners
();
try
{
final
response
=
await
ApiCalling
.
fetchTicketChatDisplayApi
(
sessionId
,
accId
,
ticketId
,
);
if
(
response
!=
null
&&
response
.
error
==
"0"
)
{
_chatResponse
=
response
;
_errorMessage
=
null
;
}
else
{
_chatResponse
=
null
;
_errorMessage
=
response
?.
message
??
"Something went wrong"
;
}
}
catch
(
e
)
{
debugPrint
(
"❌ Provider Error (fetchTicketChatDisplay):
$e
"
);
_errorMessage
=
e
.
toString
();
_chatResponse
=
null
;
}
finally
{
_isLoading
=
false
;
notifyListeners
();
}
}
/// Optional: Clear data (for refresh or reset)
void
clearChatData
()
{
_chatResponse
=
null
;
_errorMessage
=
null
;
notifyListeners
();
}
bool
_isSending
=
false
;
bool
get
isSending
=>
_isSending
;
void
_setSending
(
bool
value
)
{
_isSending
=
value
;
notifyListeners
();
}
Future
<
void
>
sendMessage
(
BuildContext
context
,
{
required
String
sessionId
,
required
String
accId
,
required
String
ticketId
,
required
String
msgText
,
required
List
<
File
>
images
,
})
async
{
_setSending
(
true
);
try
{
final
response
=
await
ApiCalling
.
addMessageApi
(
sessionId
,
accId
,
ticketId
,
msgText
,
images
,
);
// Check if widget is still mounted before showing dialogs
if
(!
context
.
mounted
)
return
;
if
(
response
!=
null
||
response
?.
error
==
0
)
{
CustomSnackBar
.
showSuccess
(
context:
context
,
message:
response
?.
message
??
"Message sent successfully!"
,
);
// Refresh the chat after sending message
if
(
context
.
mounted
)
{
fetchTicketChatDisplay
(
sessionId:
sessionId
,
accId:
accId
,
ticketId:
ticketId
,
);
}
}
else
{
CustomSnackBar
.
showError
(
context:
context
,
message:
response
?.
message
??
"Failed to send message!"
,
);
}
}
catch
(
e
)
{
debugPrint
(
"❌ sendMessage error:
$e
"
);
// Check if widget is still mounted before showing error
if
(
context
.
mounted
)
{
CustomSnackBar
.
showError
(
context:
context
,
message:
"Error sending message"
,
);
}
}
finally
{
_setSending
(
false
);
}
}
}
lib/Screens/BillDetailListScreen.dart
View file @
5ac94fe2
...
@@ -17,25 +17,24 @@ class _BillDetailListScreenState extends State<BillDetailListScreen> {
...
@@ -17,25 +17,24 @@ class _BillDetailListScreenState extends State<BillDetailListScreen> {
top:
false
,
top:
false
,
child:
Scaffold
(
child:
Scaffold
(
appBar:
AppBar
(
appBar:
AppBar
(
automaticallyImplyLeading:
false
,
backgroundColor:
Colors
.
white
,
backgroundColor:
Colors
.
white
,
elevation:
0
,
title:
Row
(
title:
Row
(
children:
[
children:
[
InkResponse
(
InkResponse
(
onTap:
()
=>
Navigator
.
pop
(
context
),
onTap:
()
=>
Navigator
.
pop
(
context
,
true
),
child:
SvgPicture
.
asset
(
child:
SvgPicture
.
asset
(
"assets/svg/continue_left_ic.svg"
,
"assets/svg/continue_left_ic.svg"
,
height:
25
,
height:
25
,
width:
25
,
),
),
),
),
const
SizedBox
(
width:
1
2
),
const
SizedBox
(
width:
1
0
),
const
Text
(
const
Text
(
"Bill
Details
"
,
"Bill
List
"
,
style:
TextStyle
(
style:
TextStyle
(
fontSize:
16
,
fontSize:
16
,
fontFamily:
"P
oppi
ns"
,
fontFamily:
"P
lus Jakarta Sa
ns"
,
fontWeight:
FontWeight
.
w
5
00
,
fontWeight:
FontWeight
.
w
6
00
,
color:
Colors
.
black87
,
color:
Colors
.
black87
,
),
),
),
),
...
@@ -126,16 +125,41 @@ class _BillDetailListScreenState extends State<BillDetailListScreen> {
...
@@ -126,16 +125,41 @@ class _BillDetailListScreenState extends State<BillDetailListScreen> {
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
children:
[
children:
[
Text
(
Text
(
"
Bill Cycle
"
,
"
Invoice raised against
"
,
style:
TextStyle
(
style:
TextStyle
(
fontFamily:
"Poppins"
,
fontFamily:
"Poppins"
,
color:
AppColors
.
subtitle
Text
,
color:
AppColors
.
normal
Text
,
fontWeight:
FontWeight
.
w400
,
fontWeight:
FontWeight
.
w400
,
fontSize:
12
,
fontSize:
12
,
),
),
),
),
Text
(
Text
(
"7th Sep 2025 - 7th Oct 2025"
,
// Fixed date range
"#1253"
,
// Fixed date range
style:
TextStyle
(
fontFamily:
"Poppins"
,
color:
AppColors
.
amountText
,
fontWeight:
FontWeight
.
w400
,
// Medium for dates
fontSize:
12
,
),
),
],
),
SizedBox
(
height:
4
,),
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
children:
[
Text
(
"order ID: 1253"
,
style:
TextStyle
(
fontFamily:
"Poppins"
,
color:
AppColors
.
normalText
,
fontWeight:
FontWeight
.
w400
,
fontSize:
12
,
),
),
Text
(
"7th Oct 2025"
,
// Fixed date range
style:
TextStyle
(
style:
TextStyle
(
fontFamily:
"Poppins"
,
fontFamily:
"Poppins"
,
color:
AppColors
.
normalText
,
color:
AppColors
.
normalText
,
...
@@ -190,7 +214,8 @@ class _BillDetailListScreenState extends State<BillDetailListScreen> {
...
@@ -190,7 +214,8 @@ class _BillDetailListScreenState extends State<BillDetailListScreen> {
}
}
Widget
_buildBillItem
({
Widget
_buildBillItem
({
String
title
=
"Bill Cycle"
,
String
orderId
=
"#1253"
,
String
title
=
"Invoice raised against"
,
required
String
fromDate
,
required
String
fromDate
,
required
String
toDate
,
required
String
toDate
,
required
String
amount
,
required
String
amount
,
...
@@ -216,16 +241,42 @@ class _BillDetailListScreenState extends State<BillDetailListScreen> {
...
@@ -216,16 +241,42 @@ class _BillDetailListScreenState extends State<BillDetailListScreen> {
crossAxisAlignment:
CrossAxisAlignment
.
start
,
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
children:
[
Text
(
orderId
,
style:
TextStyle
(
fontFamily:
"Poppins"
,
color:
AppColors
.
amountText
,
fontWeight:
FontWeight
.
w400
,
fontSize:
12
,
),
),
const
SizedBox
(
height:
2
),
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
children:
[
Text
(
Text
(
title
,
title
,
style:
TextStyle
(
style:
TextStyle
(
fontFamily:
"Poppins"
,
fontFamily:
"Poppins"
,
color:
AppColors
.
subtitle
Text
,
color:
AppColors
.
normal
Text
,
fontWeight:
FontWeight
.
w400
,
fontWeight:
FontWeight
.
w400
,
fontStyle:
FontStyle
.
normal
,
fontStyle:
FontStyle
.
normal
,
fontSize:
1
2
,
fontSize:
1
4
,
),
),
),
),
Text
(
"₹
$amount
"
,
style:
TextStyle
(
fontFamily:
"Poppins"
,
color:
AppColors
.
amountText
,
fontSize:
18
,
fontWeight:
FontWeight
.
w500
,
),
),
],
),
const
SizedBox
(
height:
2
),
const
SizedBox
(
height:
2
),
Row
(
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
...
...
lib/Screens/DashboardScreen.dart
View file @
5ac94fe2
...
@@ -4,6 +4,7 @@ import 'package:gen_rentals/Screens/HelpScreens/EnquiryScreen.dart';
...
@@ -4,6 +4,7 @@ import 'package:gen_rentals/Screens/HelpScreens/EnquiryScreen.dart';
import
'package:gen_rentals/Screens/HelpScreens/HelpScreen.dart'
;
import
'package:gen_rentals/Screens/HelpScreens/HelpScreen.dart'
;
import
'package:gen_rentals/Screens/ProductsDetailScreen.dart'
;
import
'package:gen_rentals/Screens/ProductsDetailScreen.dart'
;
import
'package:gen_rentals/Screens/TransactionsScreen.dart'
;
import
'package:gen_rentals/Screens/TransactionsScreen.dart'
;
import
'package:gen_rentals/Utility/AppColors.dart'
;
import
'package:provider/provider.dart'
;
import
'package:provider/provider.dart'
;
import
'../Models/DashboardResponse.dart'
;
import
'../Models/DashboardResponse.dart'
;
import
'../Notifier/DashboardProvider.dart'
;
import
'../Notifier/DashboardProvider.dart'
;
...
@@ -199,7 +200,17 @@ class _DashboardScreenState extends State<DashboardScreen> {
...
@@ -199,7 +200,17 @@ class _DashboardScreenState extends State<DashboardScreen> {
child:
Column
(
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
children:
[
Row
(
InkResponse
(
onTap:
()
{
Navigator
.
push
(
context
,
MaterialPageRoute
(
builder:
(
context
)
=>
TransactionsScreen
(
sessionId:
widget
.
sessionId
,
accId:
widget
.
accId
,
)),
);
},
child:
Row
(
children:
[
children:
[
const
Text
(
const
Text
(
"Balance Amount"
,
"Balance Amount"
,
...
@@ -219,6 +230,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
...
@@ -219,6 +230,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
),
),
],
],
),
),
),
const
SizedBox
(
height:
8
),
const
SizedBox
(
height:
8
),
Row
(
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
...
@@ -341,7 +353,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
...
@@ -341,7 +353,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
onTap:
()
{
onTap:
()
{
Navigator
.
push
(
Navigator
.
push
(
context
,
context
,
MaterialPageRoute
(
builder:
(
context
)
=>
EnquiryScreen
()
MaterialPageRoute
(
builder:
(
context
)
=>
EnquiryScreen
(
sessionId:
widget
.
sessionId
,
accId:
widget
.
accId
,
)
)
)
);
);
},
},
...
@@ -429,7 +441,10 @@ class _DashboardScreenState extends State<DashboardScreen> {
...
@@ -429,7 +441,10 @@ class _DashboardScreenState extends State<DashboardScreen> {
onTap:
()
{
onTap:
()
{
Navigator
.
push
(
Navigator
.
push
(
context
,
context
,
MaterialPageRoute
(
builder:
(
context
)
=>
HelpScreen
()),
MaterialPageRoute
(
builder:
(
context
)
=>
HelpScreen
(
sessionId:
widget
.
sessionId
,
accId:
widget
.
accId
,
)),
);
);
},
},
child:
Column
(
child:
Column
(
...
@@ -835,14 +850,152 @@ class _DashboardScreenState extends State<DashboardScreen> {
...
@@ -835,14 +850,152 @@ class _DashboardScreenState extends State<DashboardScreen> {
// }
// }
// }
// }
void
showPaymentBottomSheet
(
BuildContext
context
)
{
void
showPaymentBottomSheet
(
BuildContext
context
,
{
String
?
payTotal
=
"4218"
,
String
?
payBill
=
""
,
})
{
showModalBottomSheet
(
showModalBottomSheet
(
context:
context
,
context:
context
,
isScrollControlled:
true
,
isScrollControlled:
true
,
// This is important
backgroundColor:
Colors
.
transparent
,
backgroundColor:
Colors
.
transparent
,
isDismissible:
true
,
enableDrag:
true
,
builder:
(
BuildContext
context
)
{
builder:
(
BuildContext
context
)
{
return
SafeArea
(
return
PaymentBottomSheetContent
(
bottom:
true
,
payTotal:
payTotal
,
payBill:
payBill
,
flag:
false
,
);
},
);
}
}
class
PaymentBottomSheetContent
extends
StatefulWidget
{
final
String
?
payTotal
;
final
String
?
payBill
;
final
bool
flag
;
const
PaymentBottomSheetContent
({
super
.
key
,
this
.
payTotal
,
this
.
payBill
,
required
this
.
flag
,
});
@override
State
<
PaymentBottomSheetContent
>
createState
()
=>
_PaymentBottomSheetContentState
();
}
class
_PaymentBottomSheetContentState
extends
State
<
PaymentBottomSheetContent
>
{
int
selectedOption
=
-
1
;
// -1 = none, 0 = total, 1 = bill, 2 = part
final
TextEditingController
partAmountController
=
TextEditingController
();
final
FocusNode
partAmountFocusNode
=
FocusNode
();
@override
void
initState
()
{
super
.
initState
();
// Auto-focus when part payment is selected
partAmountFocusNode
.
addListener
(()
{
if
(
selectedOption
==
2
&&
partAmountFocusNode
.
hasFocus
)
{
// Ensure the bottom sheet scrolls to show the text field
Future
.
delayed
(
const
Duration
(
milliseconds:
300
),
()
{
Scrollable
.
ensureVisible
(
partAmountFocusNode
.
context
!,
duration:
const
Duration
(
milliseconds:
300
),
);
});
}
});
}
@override
void
dispose
()
{
partAmountController
.
dispose
();
partAmountFocusNode
.
dispose
();
super
.
dispose
();
}
void
_handleRadioChange
(
int
?
value
)
{
setState
(()
{
selectedOption
=
value
!;
});
// Auto-focus on part amount field when part payment is selected
if
(
value
==
2
)
{
Future
.
delayed
(
const
Duration
(
milliseconds:
100
),
()
{
partAmountFocusNode
.
requestFocus
();
});
}
else
{
// Clear focus when other options are selected
partAmountFocusNode
.
unfocus
();
}
}
void
_handleContinuePayment
()
{
// ✅ Validation
if
(
selectedOption
==
-
1
)
{
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
const
SnackBar
(
content:
Text
(
"Please select a payment option."
),
backgroundColor:
Colors
.
redAccent
,
),
);
return
;
}
if
(
selectedOption
==
2
)
{
if
(
partAmountController
.
text
.
isEmpty
)
{
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
const
SnackBar
(
content:
Text
(
"Please enter amount for part payment."
),
backgroundColor:
Colors
.
redAccent
,
),
);
return
;
}
final
amount
=
double
.
tryParse
(
partAmountController
.
text
.
trim
());
if
(
amount
==
null
||
amount
<=
0
)
{
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
const
SnackBar
(
content:
Text
(
"Please enter a valid amount."
),
backgroundColor:
Colors
.
redAccent
,
),
);
return
;
}
}
Navigator
.
pop
(
context
);
String
selectedText
=
selectedOption
==
0
?
"Pay Total ₹
${widget.payTotal}
"
:
selectedOption
==
1
?
"Pay Bill ₹
${widget.payBill}
"
:
"Part Payment ₹
${partAmountController.text}
"
;
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
SnackBar
(
content:
Text
(
"Selected:
$selectedText
"
),
backgroundColor:
Colors
.
green
,
),
);
}
@override
Widget
build
(
BuildContext
context
)
{
final
mediaQuery
=
MediaQuery
.
of
(
context
);
final
bottomPadding
=
mediaQuery
.
viewInsets
.
bottom
;
return
AnimatedPadding
(
padding:
EdgeInsets
.
only
(
bottom:
bottomPadding
),
duration:
const
Duration
(
milliseconds:
300
),
child:
SafeArea
(
child:
Container
(
child:
Container
(
decoration:
const
BoxDecoration
(
decoration:
const
BoxDecoration
(
color:
Colors
.
white
,
color:
Colors
.
white
,
...
@@ -851,13 +1004,15 @@ class _DashboardScreenState extends State<DashboardScreen> {
...
@@ -851,13 +1004,15 @@ class _DashboardScreenState extends State<DashboardScreen> {
topRight:
Radius
.
circular
(
24
),
topRight:
Radius
.
circular
(
24
),
),
),
),
),
child:
SingleChildScrollView
(
// Add this to make it scrollable when keyboard is open
child:
Padding
(
child:
Padding
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
16
,
vertical:
14
),
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
16
,
vertical:
14
),
child:
Column
(
child:
Column
(
mainAxisSize:
MainAxisSize
.
min
,
mainAxisSize:
MainAxisSize
.
min
,
crossAxisAlignment:
CrossAxisAlignment
.
start
,
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
children:
[
// Header
- Drag
handle
// Header handle
Center
(
Center
(
child:
Container
(
child:
Container
(
width:
40
,
width:
40
,
...
@@ -870,57 +1025,166 @@ class _DashboardScreenState extends State<DashboardScreen> {
...
@@ -870,57 +1025,166 @@ class _DashboardScreenState extends State<DashboardScreen> {
),
),
const
SizedBox
(
height:
20
),
const
SizedBox
(
height:
20
),
// Pay Amount Section
Text
(
"Balance Amount Bill"
,
style:
TextStyle
(
fontSize:
14
,
fontWeight:
FontWeight
.
w500
,
color:
AppColors
.
normalText
,
),
),
const
SizedBox
(
height:
16
),
const
Divider
(
height:
1
,
color:
Color
(
0xFFEEEEEE
)),
const
SizedBox
(
height:
16
),
// ====== PAY OPTIONS ======
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
// Pay Total
Row
(
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
children:
[
children:
[
const
Column
(
Row
(
children:
[
Radio
<
int
>(
value:
0
,
groupValue:
selectedOption
,
onChanged:
_handleRadioChange
,
activeColor:
const
Color
(
0xFF008CDE
),
),
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
children:
[
Text
(
const
Text
(
"Pay
"
,
"Pay Total
"
,
style:
TextStyle
(
style:
TextStyle
(
fontSize:
1
6
,
fontSize:
1
4
,
fontWeight:
FontWeight
.
w400
,
fontWeight:
FontWeight
.
w400
,
color:
Color
(
0xFF777777
)
,
color:
Color
s
.
black87
,
),
),
),
),
SizedBox
(
height:
4
),
Text
(
Text
(
"₹4218
"
,
"Avoid late payment fees.
"
,
style:
TextStyle
(
style:
TextStyle
(
fontSize:
24
,
fontSize:
13
,
fontWeight:
FontWeight
.
w600
,
fontWeight:
FontWeight
.
w400
,
color:
Colors
.
green
.
shade600
,
),
),
],
),
],
),
Text
(
"₹
${widget.payTotal}
"
,
style:
const
TextStyle
(
fontSize:
14
,
fontWeight:
FontWeight
.
w500
,
color:
Colors
.
black
,
color:
Colors
.
black
,
),
),
),
),
],
],
),
),
// Rent Amount Section
// Pay Bill
Column
(
if
(
widget
.
flag
==
true
)
crossAxisAlignment:
CrossAxisAlignment
.
end
,
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
children:
[
children:
[
Row
(
children:
[
Radio
<
int
>(
value:
1
,
groupValue:
selectedOption
,
onChanged:
_handleRadioChange
,
activeColor:
const
Color
(
0xFF008CDE
),
),
const
Text
(
const
Text
(
"Rent Amount
"
,
"Pay Bill
"
,
style:
TextStyle
(
style:
TextStyle
(
fontSize:
14
,
fontSize:
14
,
fontWeight:
FontWeight
.
w400
,
fontWeight:
FontWeight
.
w400
,
color:
Color
(
0xFF777777
)
,
color:
Color
s
.
black87
,
),
),
),
),
const
SizedBox
(
height:
4
),
],
GestureDetector
(
),
onTap:
()
{
Text
(
// Handle view bill details
"₹
${widget.payBill}
"
,
},
style:
const
TextStyle
(
child:
Text
(
fontSize:
14
,
"View Bill Details"
,
fontWeight:
FontWeight
.
w500
,
color:
Colors
.
black
,
),
),
],
),
// Part Payment
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
children:
[
Expanded
(
child:
Row
(
children:
[
Radio
<
int
>(
value:
2
,
groupValue:
selectedOption
,
onChanged:
_handleRadioChange
,
activeColor:
const
Color
(
0xFF008CDE
),
),
const
Text
(
"Part Payment"
,
style:
TextStyle
(
style:
TextStyle
(
fontSize:
14
,
fontWeight:
FontWeight
.
w400
,
color:
Colors
.
black87
,
),
),
const
SizedBox
(
width:
10
),
if
(
selectedOption
==
2
)
Expanded
(
child:
Container
(
height:
50
,
alignment:
Alignment
.
center
,
decoration:
BoxDecoration
(
color:
Colors
.
grey
.
shade100
,
borderRadius:
BorderRadius
.
circular
(
12
),
),
child:
TextField
(
controller:
partAmountController
,
focusNode:
partAmountFocusNode
,
keyboardType:
TextInputType
.
number
,
textAlign:
TextAlign
.
right
,
style:
const
TextStyle
(
fontSize:
14
,
fontSize:
14
,
fontWeight:
FontWeight
.
w500
,
fontWeight:
FontWeight
.
w500
,
color:
Color
(
0xFF008CDE
),
color:
Colors
.
black
,
),
decoration:
const
InputDecoration
(
hintText:
"Enter amount"
,
hintStyle:
TextStyle
(
color:
Colors
.
grey
,
fontSize:
14
,
),
border:
InputBorder
.
none
,
contentPadding:
EdgeInsets
.
symmetric
(
horizontal:
12
,
vertical:
8
),
prefixText:
"₹"
,
prefixStyle:
TextStyle
(
color:
Colors
.
black
,
fontSize:
14
,
fontWeight:
FontWeight
.
w500
,
),
),
onChanged:
(
value
)
{
// Optional: Add real-time validation if needed
setState
(()
{});
},
),
),
),
),
],
),
),
),
),
],
],
...
@@ -928,7 +1192,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
...
@@ -928,7 +1192,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
],
],
),
),
const
SizedBox
(
height:
1
0
),
const
SizedBox
(
height:
1
8
),
const
Divider
(
height:
1
,
color:
Color
(
0xFFEEEEEE
)),
const
Divider
(
height:
1
,
color:
Color
(
0xFFEEEEEE
)),
const
SizedBox
(
height:
18
),
const
SizedBox
(
height:
18
),
...
@@ -936,13 +1200,10 @@ class _DashboardScreenState extends State<DashboardScreen> {
...
@@ -936,13 +1200,10 @@ class _DashboardScreenState extends State<DashboardScreen> {
SizedBox
(
SizedBox
(
width:
double
.
infinity
,
width:
double
.
infinity
,
child:
ElevatedButton
(
child:
ElevatedButton
(
onPressed:
()
{
onPressed:
_handleContinuePayment
,
// Handle continue payment
},
style:
ElevatedButton
.
styleFrom
(
style:
ElevatedButton
.
styleFrom
(
backgroundColor:
const
Color
(
0xFF008CDE
),
backgroundColor:
const
Color
(
0xFF008CDE
),
foregroundColor:
Colors
.
white
,
foregroundColor:
Colors
.
white
,
disabledBackgroundColor:
const
Color
(
0xFF266E99
),
shape:
RoundedRectangleBorder
(
shape:
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
circular
(
30
),
borderRadius:
BorderRadius
.
circular
(
30
),
),
),
...
@@ -956,13 +1217,13 @@ class _DashboardScreenState extends State<DashboardScreen> {
...
@@ -956,13 +1217,13 @@ class _DashboardScreenState extends State<DashboardScreen> {
const
Text
(
const
Text
(
"Continue Payment"
,
"Continue Payment"
,
style:
TextStyle
(
style:
TextStyle
(
color:
Color
(
0xFFFFFFFF
)
,
color:
Color
s
.
white
,
fontSize:
16
,
fontSize:
16
,
),
),
),
),
SvgPicture
.
asset
(
SvgPicture
.
asset
(
"assets/svg/continue_ic.svg"
,
"assets/svg/continue_ic.svg"
,
color:
Color
(
0xFFFFFFFF
)
,
color:
Color
s
.
white
,
height:
25
,
height:
25
,
width:
25
,
width:
25
,
),
),
...
@@ -971,12 +1232,15 @@ class _DashboardScreenState extends State<DashboardScreen> {
...
@@ -971,12 +1232,15 @@ class _DashboardScreenState extends State<DashboardScreen> {
),
),
),
),
),
),
// Add extra space at bottom for better visibility
SizedBox
(
height:
mediaQuery
.
viewInsets
.
bottom
>
0
?
20
:
0
),
],
],
),
),
),
),
),
),
)
;
)
,
}
,
)
,
);
);
}
}
}
}
\ No newline at end of file
lib/Screens/HelpScreens/CreateTicketScreen.dart
View file @
5ac94fe2
...
@@ -17,6 +17,28 @@ class _HelpTicketScreenState extends State<HelpTicketScreen> {
...
@@ -17,6 +17,28 @@ class _HelpTicketScreenState extends State<HelpTicketScreen> {
List
<
String
>
_selectedImages
=
[];
List
<
String
>
_selectedImages
=
[];
String
_selectedReason
=
'Payment Issue'
;
String
_selectedReason
=
'Payment Issue'
;
// Dummy data for help - with proper null safety
final
List
<
Map
<
String
,
dynamic
>>
createNewTickets
=
[
{
'title'
:
'Payment Issues'
,
'description'
:
'Get help with payment related problems'
,
'icon'
:
"assets/svg/rupee_coin_ic.svg"
,
'color'
:
Color
(
0xFFFFEFBE
),
},
{
'title'
:
'Bill Related Issues'
,
'description'
:
'Resolve bill and invoice matters'
,
'icon'
:
"assets/svg/know_pay.svg"
,
'color'
:
Color
(
0xFFCEF9FF
),
},
{
'title'
:
'Other Issues'
,
'description'
:
'Any other support you need'
,
'icon'
:
'assets/svg/help_ic.svg'
,
'color'
:
Color
(
0xFFE4E5FF
),
},
];
@override
@override
void
initState
()
{
void
initState
()
{
super
.
initState
();
super
.
initState
();
...
@@ -27,7 +49,6 @@ class _HelpTicketScreenState extends State<HelpTicketScreen> {
...
@@ -27,7 +49,6 @@ class _HelpTicketScreenState extends State<HelpTicketScreen> {
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
final
isEditable
=
widget
.
reason
==
null
;
final
showOtherReasonField
=
_selectedReason
==
'Other Issues'
;
final
showOtherReasonField
=
_selectedReason
==
'Other Issues'
;
return
SafeArea
(
return
SafeArea
(
...
@@ -66,54 +87,49 @@ class _HelpTicketScreenState extends State<HelpTicketScreen> {
...
@@ -66,54 +87,49 @@ class _HelpTicketScreenState extends State<HelpTicketScreen> {
crossAxisAlignment:
CrossAxisAlignment
.
start
,
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
children:
[
/// Section Title
/// Section Title
const
SectionHeading
(
title:
'Create New Ticket'
),
Row
(
const
SizedBox
(
height:
12
),
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
children:
[
/// Reason Label
SectionHeading
(
title:
'Create New Ticket'
),
_fieldLabel
(
"Reason"
),
const
SizedBox
(
height:
6
),
/// Reason Dropdown
Container
(
Container
(
width:
double
.
infinity
,
padding:
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
1
6
,
vertical:
10
),
const
EdgeInsets
.
symmetric
(
horizontal:
1
4
,
vertical:
10
),
decoration:
BoxDecoration
(
decoration:
BoxDecoration
(
color:
Color
(
0xffE0E0E0
)
,
color:
Color
s
.
blue
.
shade50
,
borderRadius:
BorderRadius
.
circular
(
1
2
),
borderRadius:
BorderRadius
.
circular
(
1
0
),
),
),
child:
isEditable
?
DropdownButtonFormField
<
String
>(
value:
_selectedReason
,
items:
[
'Payment Issue'
,
'Bill Related Issues'
,
'Other Issues'
,
].
map
((
String
value
)
{
return
DropdownMenuItem
<
String
>(
value:
value
,
child:
Text
(
child:
Text
(
value
,
"order #1235"
,
style:
const
TextStyle
(
style:
TextStyle
(
fontSize:
14
,
fontSize:
14
,
fontFamily:
"Plus Jakarta Sans"
,
fontFamily:
"Plus Jakarta Sans"
,
fontWeight:
FontWeight
.
w500
,
fontWeight:
FontWeight
.
w500
,
color:
Colors
.
black87
,
color:
Colors
.
black87
,
),
),
),
),
);
}).
toList
(),
onChanged:
(
newValue
)
{
setState
(()
{
_selectedReason
=
newValue
!;
});
},
decoration:
const
InputDecoration
(
border:
InputBorder
.
none
,
contentPadding:
EdgeInsets
.
zero
,
),
),
)
],
:
Text
(
),
const
SizedBox
(
height:
12
),
/// Reason Label
_fieldLabel
(
"Reason"
),
const
SizedBox
(
height:
6
),
/// Reason Selection Button - Opens Bottom Sheet
GestureDetector
(
onTap:
_showReasonBottomSheet
,
child:
Container
(
width:
200
,
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
16
,
vertical:
12
),
decoration:
BoxDecoration
(
color:
Color
(
0xffFFF3D1
),
borderRadius:
BorderRadius
.
circular
(
12
),
),
child:
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
children:
[
Text
(
_selectedReason
,
_selectedReason
,
style:
const
TextStyle
(
style:
const
TextStyle
(
fontSize:
14
,
fontSize:
14
,
...
@@ -122,6 +138,13 @@ class _HelpTicketScreenState extends State<HelpTicketScreen> {
...
@@ -122,6 +138,13 @@ class _HelpTicketScreenState extends State<HelpTicketScreen> {
color:
Colors
.
black87
,
color:
Colors
.
black87
,
),
),
),
),
SvgPicture
.
asset
(
"assets/svg/edit_ic.svg"
,
height:
25
,
),
],
),
),
),
),
const
SizedBox
(
height:
16
),
const
SizedBox
(
height:
16
),
...
@@ -267,7 +290,7 @@ class _HelpTicketScreenState extends State<HelpTicketScreen> {
...
@@ -267,7 +290,7 @@ class _HelpTicketScreenState extends State<HelpTicketScreen> {
return
Text
(
return
Text
(
text
,
text
,
style:
TextStyle
(
style:
TextStyle
(
fontSize:
1
2
,
fontSize:
1
4
,
fontFamily:
"Plus Jakarta Sans"
,
fontFamily:
"Plus Jakarta Sans"
,
fontWeight:
FontWeight
.
w500
,
fontWeight:
FontWeight
.
w500
,
color:
Colors
.
grey
[
700
],
color:
Colors
.
grey
[
700
],
...
@@ -341,4 +364,120 @@ class _HelpTicketScreenState extends State<HelpTicketScreen> {
...
@@ -341,4 +364,120 @@ class _HelpTicketScreenState extends State<HelpTicketScreen> {
_otherReasonController
.
dispose
();
_otherReasonController
.
dispose
();
super
.
dispose
();
super
.
dispose
();
}
}
void
_showReasonBottomSheet
()
{
showModalBottomSheet
(
context:
context
,
backgroundColor:
Colors
.
white
,
shape:
const
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
only
(
topLeft:
Radius
.
circular
(
20
),
topRight:
Radius
.
circular
(
20
),
),
),
builder:
(
context
)
{
return
Container
(
padding:
const
EdgeInsets
.
all
(
16
),
child:
Column
(
mainAxisSize:
MainAxisSize
.
min
,
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
const
Text
(
"Select Your Reason"
,
style:
TextStyle
(
fontSize:
18
,
fontFamily:
"Plus Jakarta Sans"
,
fontWeight:
FontWeight
.
w600
,
color:
Colors
.
black87
,
),
),
const
SizedBox
(
height:
16
),
GridView
.
builder
(
shrinkWrap:
true
,
physics:
const
NeverScrollableScrollPhysics
(),
gridDelegate:
const
SliverGridDelegateWithFixedCrossAxisCount
(
crossAxisCount:
3
,
crossAxisSpacing:
12
,
mainAxisSpacing:
12
,
childAspectRatio:
0.99
,
),
itemCount:
createNewTickets
.
length
,
itemBuilder:
(
context
,
index
)
{
final
ticket
=
createNewTickets
[
index
];
final
String
title
=
ticket
[
'title'
]
??
'Unknown'
;
final
String
icon
=
ticket
[
'icon'
]
??
'assets/svg/help_ic.svg'
;
final
Color
color
=
ticket
[
'color'
]
??
Colors
.
grey
;
return
_buildReasonCard
(
title:
title
,
icon:
icon
,
color:
color
,
);
},
),
const
SizedBox
(
height:
24
),
],
),
);
},
);
}
Widget
_buildReasonCard
({
required
String
title
,
required
String
icon
,
required
Color
color
,
})
{
return
GestureDetector
(
onTap:
()
{
setState
(()
{
_selectedReason
=
title
;
});
Navigator
.
pop
(
context
);
// Close the bottom sheet
},
child:
Container
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
2
,
vertical:
1
),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
center
,
children:
[
// Icon container
Container
(
width:
88
,
height:
88
,
decoration:
BoxDecoration
(
color:
color
.
withOpacity
(
0.12
),
// Fixed opacity
borderRadius:
BorderRadius
.
circular
(
12
),
),
child:
Center
(
child:
SizedBox
(
height:
40
,
width:
40
,
child:
SvgPicture
.
asset
(
icon
,
fit:
BoxFit
.
fitWidth
,
),
),
),
),
const
SizedBox
(
height:
8
),
// Title
SizedBox
(
child:
Text
(
title
,
textAlign:
TextAlign
.
center
,
style:
TextStyle
(
color:
AppColors
.
nearDarkText
,
fontSize:
14
,
fontWeight:
FontWeight
.
w400
,
fontFamily:
"Plus Jakarta Sans"
,
),
),
),
const
SizedBox
(
height:
4
),
],
),
),
);
}
}
}
\ No newline at end of file
lib/Screens/HelpScreens/EnquiryScreen.dart
View file @
5ac94fe2
import
'package:flutter/material.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_svg/flutter_svg.dart'
;
import
'package:flutter_svg/flutter_svg.dart'
;
import
'package:provider/provider.dart'
;
import
'../../Notifier/HelpAndEnquiryProvider.dart'
;
import
'../../Utility/AppColors.dart'
;
import
'../../Utility/AppColors.dart'
;
import
'../../Utility/
Reusablewidgets
.dart'
;
import
'../../Utility/
CustomSnackbar
.dart'
;
class
EnquiryScreen
extends
StatefulWidget
{
class
EnquiryScreen
extends
StatefulWidget
{
const
EnquiryScreen
({
super
.
key
});
final
String
sessionId
;
final
String
accId
;
const
EnquiryScreen
({
super
.
key
,
required
this
.
sessionId
,
required
this
.
accId
,
});
@override
@override
State
<
EnquiryScreen
>
createState
()
=>
_EnquiryScreenState
();
State
<
EnquiryScreen
>
createState
()
=>
_EnquiryScreenState
();
}
}
class
_EnquiryScreenState
extends
State
<
EnquiryScreen
>
{
class
_EnquiryScreenState
extends
State
<
EnquiryScreen
>
{
final
_formKey
=
GlobalKey
<
FormState
>();
final
TextEditingController
nameController
=
TextEditingController
();
final
TextEditingController
nameController
=
TextEditingController
();
final
TextEditingController
emailController
=
TextEditingController
();
final
TextEditingController
emailController
=
TextEditingController
();
final
TextEditingController
phoneController
=
TextEditingController
();
final
TextEditingController
phoneController
=
TextEditingController
();
...
@@ -19,13 +30,15 @@ class _EnquiryScreenState extends State<EnquiryScreen> {
...
@@ -19,13 +30,15 @@ class _EnquiryScreenState extends State<EnquiryScreen> {
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
final
enquiryProvider
=
Provider
.
of
<
HelpAndEnquiryProvider
>(
context
);
return
SafeArea
(
return
SafeArea
(
top:
false
,
top:
false
,
child:
Scaffold
(
child:
Scaffold
(
backgroundColor:
App
Color
s
.
backgroundRegular
,
backgroundColor:
Color
(
0xFFffffff
)
,
appBar:
AppBar
(
appBar:
AppBar
(
automaticallyImplyLeading:
false
,
automaticallyImplyLeading:
false
,
backgroundColor:
Color
s
.
white
,
backgroundColor:
Color
(
0xFFFCFCFC
)
,
elevation:
0
,
elevation:
0
,
title:
Row
(
title:
Row
(
children:
[
children:
[
...
@@ -53,30 +66,69 @@ class _EnquiryScreenState extends State<EnquiryScreen> {
...
@@ -53,30 +66,69 @@ class _EnquiryScreenState extends State<EnquiryScreen> {
// Main Body
// Main Body
body:
SingleChildScrollView
(
body:
SingleChildScrollView
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
16
,
vertical:
20
),
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
16
,
vertical:
20
),
child:
Form
(
key:
_formKey
,
child:
Column
(
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
children:
[
_labelText
(
"Name"
),
_labelText
(
"Name"
),
_textField
(
nameController
,
"Enter Name"
),
_textField
(
controller:
nameController
,
hint:
"Enter Name"
,
fieldName:
"Name"
,
),
const
SizedBox
(
height:
16
),
const
SizedBox
(
height:
16
),
_labelText
(
"Email Id"
),
_labelText
(
"Email Id"
),
_textField
(
emailController
,
"Enter Email ID"
),
_textField
(
controller:
emailController
,
hint:
"Enter Email ID"
,
fieldName:
"Email"
,
keyboardType:
TextInputType
.
emailAddress
,
validator:
(
value
)
{
if
(
value
==
null
||
value
.
trim
().
isEmpty
)
{
return
"Please enter your email"
;
}
if
(!
RegExp
(
r'^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$'
)
.
hasMatch
(
value
.
trim
()))
{
return
"Enter a valid email"
;
}
return
null
;
},
),
const
SizedBox
(
height:
16
),
const
SizedBox
(
height:
16
),
_labelText
(
"Phone No."
),
_labelText
(
"Phone No."
),
_textField
(
phoneController
,
"Enter Phone Number"
,
_textField
(
keyboardType:
TextInputType
.
phone
),
controller:
phoneController
,
hint:
"Enter Phone Number"
,
fieldName:
"Phone Number"
,
keyboardType:
TextInputType
.
phone
,
validator:
(
value
)
{
if
(
value
==
null
||
value
.
trim
().
isEmpty
)
{
return
"Please enter your phone number"
;
}
if
(
value
.
trim
().
length
<
10
)
{
return
"Enter a valid phone number"
;
}
return
null
;
},
),
const
SizedBox
(
height:
16
),
const
SizedBox
(
height:
16
),
_labelText
(
"Requirement"
),
_labelText
(
"Requirement"
),
_textField
(
requirementController
,
"Enter Requirement"
),
_textField
(
controller:
requirementController
,
hint:
"Enter Requirement"
,
fieldName:
"Requirement"
,
),
const
SizedBox
(
height:
16
),
const
SizedBox
(
height:
16
),
_labelText
(
"Note"
),
_labelText
(
"Note"
),
_textField
(
_textField
(
noteController
,
controller:
noteController
,
"Write a short note"
,
hint:
"Write a short note"
,
fieldName:
"Note"
,
maxLines:
5
,
maxLines:
5
,
),
),
const
SizedBox
(
height:
32
),
const
SizedBox
(
height:
32
),
...
@@ -85,9 +137,45 @@ class _EnquiryScreenState extends State<EnquiryScreen> {
...
@@ -85,9 +137,45 @@ class _EnquiryScreenState extends State<EnquiryScreen> {
SizedBox
(
SizedBox
(
width:
double
.
infinity
,
width:
double
.
infinity
,
child:
ElevatedButton
(
child:
ElevatedButton
(
onPressed:
()
{
onPressed:
enquiryProvider
.
isLoading
// Submit action
?
null
:
()
async
{
FocusScope
.
of
(
context
).
unfocus
();
FocusScope
.
of
(
context
).
unfocus
();
if
(!
_formKey
.
currentState
!.
validate
())
return
;
final
success
=
await
enquiryProvider
.
submitEnquiry
(
sessionId:
widget
.
sessionId
,
accId:
widget
.
accId
,
name:
nameController
.
text
.
trim
(),
email:
emailController
.
text
.
trim
(),
mobile:
phoneController
.
text
.
trim
(),
requirement:
requirementController
.
text
.
trim
(),
note:
noteController
.
text
.
trim
(),
);
if
(!
mounted
)
return
;
if
(
success
)
{
CustomSnackBar
.
showSuccess
(
context:
context
,
message:
enquiryProvider
.
message
??
"Enquiry submitted successfully!"
,
);
_formKey
.
currentState
!.
reset
();
nameController
.
clear
();
emailController
.
clear
();
phoneController
.
clear
();
requirementController
.
clear
();
noteController
.
clear
();
}
else
{
CustomSnackBar
.
showError
(
context:
context
,
message:
enquiryProvider
.
message
??
"Failed to submit enquiry!"
,
);
}
},
},
style:
ElevatedButton
.
styleFrom
(
style:
ElevatedButton
.
styleFrom
(
backgroundColor:
AppColors
.
buttonColor
,
backgroundColor:
AppColors
.
buttonColor
,
...
@@ -98,7 +186,16 @@ class _EnquiryScreenState extends State<EnquiryScreen> {
...
@@ -98,7 +186,16 @@ class _EnquiryScreenState extends State<EnquiryScreen> {
),
),
elevation:
0
,
elevation:
0
,
),
),
child:
const
Text
(
child:
enquiryProvider
.
isLoading
?
const
SizedBox
(
height:
22
,
width:
22
,
child:
CircularProgressIndicator
(
strokeWidth:
2.5
,
color:
Colors
.
white
,
),
)
:
const
Text
(
"Submit"
,
"Submit"
,
style:
TextStyle
(
style:
TextStyle
(
fontSize:
16
,
fontSize:
16
,
...
@@ -108,11 +205,11 @@ class _EnquiryScreenState extends State<EnquiryScreen> {
...
@@ -108,11 +205,11 @@ class _EnquiryScreenState extends State<EnquiryScreen> {
),
),
),
),
),
),
const
SizedBox
(
height:
16
),
],
],
),
),
),
),
),
),
),
);
);
}
}
...
@@ -129,22 +226,41 @@ class _EnquiryScreenState extends State<EnquiryScreen> {
...
@@ -129,22 +226,41 @@ class _EnquiryScreenState extends State<EnquiryScreen> {
);
);
}
}
/// Rounded Input Field
/// Text Field with error message shown below box
Widget
_textField
(
Widget
_textField
({
TextEditingController
controller
,
required
TextEditingController
controller
,
String
hint
,
{
required
String
hint
,
required
String
fieldName
,
TextInputType
keyboardType
=
TextInputType
.
text
,
TextInputType
keyboardType
=
TextInputType
.
text
,
int
maxLines
=
1
,
int
maxLines
=
1
,
String
?
Function
(
String
?)?
validator
,
})
{
})
{
return
Container
(
return
FormField
<
String
>(
validator:
validator
??
(
value
)
{
if
(
controller
.
text
.
trim
().
isEmpty
)
{
return
'
$fieldName
is required'
;
}
return
null
;
},
builder:
(
field
)
{
return
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Container
(
decoration:
BoxDecoration
(
decoration:
BoxDecoration
(
color:
Color
s
.
white
,
color:
Color
(
0xffF6F6F8
)
,
borderRadius:
BorderRadius
.
circular
(
12
),
borderRadius:
BorderRadius
.
circular
(
12
),
// border: Border.all(
// color: field.hasError ? Colors.red : Colors.transparent,
// width: 1,
// ),
),
),
child:
TextFormField
(
child:
TextFormField
(
controller:
controller
,
controller:
controller
,
keyboardType:
keyboardType
,
keyboardType:
keyboardType
,
maxLines:
maxLines
,
maxLines:
maxLines
,
onChanged:
(
_
)
=>
field
.
didChange
(
controller
.
text
),
decoration:
InputDecoration
(
decoration:
InputDecoration
(
hintText:
hint
,
hintText:
hint
,
hintStyle:
TextStyle
(
hintStyle:
TextStyle
(
...
@@ -157,6 +273,22 @@ class _EnquiryScreenState extends State<EnquiryScreen> {
...
@@ -157,6 +273,22 @@ class _EnquiryScreenState extends State<EnquiryScreen> {
border:
InputBorder
.
none
,
border:
InputBorder
.
none
,
),
),
),
),
),
if
(
field
.
hasError
)
Padding
(
padding:
const
EdgeInsets
.
only
(
top:
5
,
left:
4
),
child:
Text
(
field
.
errorText
??
''
,
style:
const
TextStyle
(
color:
Colors
.
red
,
fontSize:
12
,
fontFamily:
"Plus Jakarta Sans"
,
),
),
),
],
);
},
);
);
}
}
...
...
lib/Screens/HelpScreens/HelpScreen.dart
View file @
5ac94fe2
import
'package:flutter/material.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_svg/flutter_svg.dart'
;
import
'package:flutter_svg/flutter_svg.dart'
;
import
'package:gen_rentals/Screens/HelpScreens/
CreateTicket
Screen.dart'
;
import
'package:gen_rentals/Screens/HelpScreens/
OrderHelp
Screen.dart'
;
import
'package:gen_rentals/Screens/HelpScreens/ProcessTicketScreen.dart'
;
import
'package:gen_rentals/Screens/HelpScreens/ProcessTicketScreen.dart'
;
import
'package:gen_rentals/Utility/Reusablewidgets.dart'
;
import
'package:gen_rentals/Utility/Reusablewidgets.dart'
;
import
'../../Notifier/HelpAndEnquiryProvider.dart'
;
import
'../../Utility/AppColors.dart'
;
import
'../../Utility/AppColors.dart'
;
import
'package:provider/provider.dart'
;
class
HelpScreen
extends
StatefulWidget
{
class
HelpScreen
extends
StatefulWidget
{
const
HelpScreen
({
super
.
key
});
final
String
sessionId
;
final
String
accId
;
HelpScreen
({
super
.
key
,
required
this
.
sessionId
,
required
this
.
accId
,
});
@override
@override
State
<
HelpScreen
>
createState
()
=>
_HelpScreenState
();
State
<
HelpScreen
>
createState
()
=>
_HelpScreenState
();
}
}
class
_HelpScreenState
extends
State
<
HelpScreen
>
{
class
_HelpScreenState
extends
State
<
HelpScreen
>
{
// Dummy data for help - with proper null safety
@override
void
initState
()
{
super
.
initState
();
/// ✅ Fetch ticket list on screen load
Future
.
microtask
(()
async
{
final
provider
=
Provider
.
of
<
HelpAndEnquiryProvider
>(
context
,
listen:
false
);
await
provider
.
fetchTicketList
(
sessionId:
widget
.
sessionId
,
accId:
widget
.
accId
,
);
});
}
// ✅ (unchanged)
final
List
<
Map
<
String
,
dynamic
>>
createNewTickets
=
[
final
List
<
Map
<
String
,
dynamic
>>
createNewTickets
=
[
{
{
'title'
:
'Payment Issues'
,
'title'
:
'Payment Issues'
,
...
@@ -36,16 +59,6 @@ class _HelpScreenState extends State<HelpScreen> {
...
@@ -36,16 +59,6 @@ class _HelpScreenState extends State<HelpScreen> {
},
},
];
];
final
List
<
Map
<
String
,
String
>>
processingTickets
=
[
{
'title'
:
'Payment Issue'
,
'date'
:
'25th Jan 2025'
,
'status'
:
'In Process'
},
];
final
List
<
Map
<
String
,
String
>>
closedTickets
=
[
{
'title'
:
'Bill Payments'
,
'date'
:
'25th Jan 2025'
},
{
'title'
:
'Others'
,
'date'
:
'25th Jan 2025'
},
{
'title'
:
'Payment Issue'
,
'date'
:
'25th Jan 2025'
},
];
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
return
SafeArea
(
return
SafeArea
(
...
@@ -65,7 +78,7 @@ class _HelpScreenState extends State<HelpScreen> {
...
@@ -65,7 +78,7 @@ class _HelpScreenState extends State<HelpScreen> {
),
),
),
),
const
SizedBox
(
width:
10
),
const
SizedBox
(
width:
10
),
Text
(
const
Text
(
"Help?"
,
"Help?"
,
style:
TextStyle
(
style:
TextStyle
(
fontSize:
16
,
fontSize:
16
,
...
@@ -77,14 +90,32 @@ class _HelpScreenState extends State<HelpScreen> {
...
@@ -77,14 +90,32 @@ class _HelpScreenState extends State<HelpScreen> {
],
],
),
),
),
),
// Main content
body:
SingleChildScrollView
(
// ✅ Provider Consumer used here
body:
Consumer
<
HelpAndEnquiryProvider
>(
builder:
(
context
,
provider
,
_
)
{
if
(
provider
.
isLoading
)
{
return
const
Center
(
child:
CircularProgressIndicator
());
}
if
(
provider
.
errorMessage
!=
null
)
{
return
Center
(
child:
Text
(
provider
.
errorMessage
!,
style:
const
TextStyle
(
color:
Colors
.
red
)),
);
}
final
ticketData
=
provider
.
ticketListResponse
?.
tickets
;
final
processingTickets
=
ticketData
?.
inProgress
??
[];
final
closedTickets
=
ticketData
?.
closed
??
[];
return
SingleChildScrollView
(
padding:
const
EdgeInsets
.
all
(
16
),
padding:
const
EdgeInsets
.
all
(
16
),
child:
Column
(
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
children:
[
// Create New Ticket Section
// Create New Ticket Section
SectionHeading
(
const
SectionHeading
(
title:
'Create New Ticket'
,
title:
'Create New Ticket'
,
padding:
EdgeInsets
.
symmetric
(
horizontal:
2
,
vertical:
4
),
padding:
EdgeInsets
.
symmetric
(
horizontal:
2
,
vertical:
4
),
),
),
...
@@ -93,129 +124,126 @@ class _HelpScreenState extends State<HelpScreen> {
...
@@ -93,129 +124,126 @@ class _HelpScreenState extends State<HelpScreen> {
const
SizedBox
(
height:
12
),
const
SizedBox
(
height:
12
),
// Processing Tickets Section
// Processing Tickets Section
SectionHeading
(
const
SectionHeading
(
title:
'Processing Tickets'
,
title:
'Processing Tickets'
,
padding:
EdgeInsets
.
symmetric
(
horizontal:
2
,
vertical:
4
),
padding:
EdgeInsets
.
symmetric
(
horizontal:
2
,
vertical:
4
),
),
),
const
SizedBox
(
height:
2
),
const
SizedBox
(
height:
2
),
_buildProcessingTicketsSection
(),
_buildProcessingTicketsSection
(
processingTickets
),
const
SizedBox
(
height:
10
),
const
SizedBox
(
height:
10
),
// Closed Tickets Section
// Closed Tickets Section
SectionHeading
(
const
SectionHeading
(
title:
'Closed Tickets'
,
title:
'Closed Tickets'
,
padding:
EdgeInsets
.
symmetric
(
horizontal:
2
,
vertical:
4
),
padding:
EdgeInsets
.
symmetric
(
horizontal:
2
,
vertical:
4
),
),
),
const
SizedBox
(
height:
2
),
const
SizedBox
(
height:
2
),
_buildClosedTicketsSection
(),
_buildClosedTicketsSection
(
closedTickets
),
],
],
),
),
);
},
),
),
),
),
);
);
}
}
Widget
_buildCreateNewTicketSection
()
{
Widget
_buildCreateNewTicketSection
()
{
return
GridView
.
builder
(
return
InkResponse
(
shrinkWrap:
true
,
physics:
const
NeverScrollableScrollPhysics
(),
gridDelegate:
const
SliverGridDelegateWithFixedCrossAxisCount
(
crossAxisCount:
3
,
crossAxisSpacing:
12
,
mainAxisSpacing:
12
,
childAspectRatio:
0.99
,
),
itemCount:
createNewTickets
.
length
,
itemBuilder:
(
context
,
index
)
{
final
ticket
=
createNewTickets
[
index
];
final
String
title
=
ticket
[
'title'
]
??
'Unknown'
;
final
String
description
=
ticket
[
'description'
]
??
''
;
final
String
icon
=
ticket
[
'icon'
]
??
'assets/svg/help_ic.svg'
;
final
Color
color
=
ticket
[
'color'
]
??
Colors
.
grey
;
return
_buildFeatureCard
(
title:
title
,
description:
description
,
icon:
icon
,
color:
color
,
);
},
);
}
Widget
_buildFeatureCard
({
required
String
title
,
required
String
description
,
required
String
icon
,
required
Color
color
,
})
{
return
GestureDetector
(
onTap:
()
{
onTap:
()
{
Navigator
.
push
(
Navigator
.
push
(
context
,
context
,
MaterialPageRoute
(
builder:
(
context
)
=>
HelpTicketScreen
(
reason:
title
,
))
MaterialPageRoute
(
builder:
(
context
)
=>
OrderHelpScreen
(
sessionId:
widget
.
sessionId
,
accId:
widget
.
accId
))
);
);
},
},
child:
Container
(
child:
Container
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
2
,
vertical:
1
),
padding:
const
EdgeInsets
.
symmetric
(
vertical:
16
,
horizontal:
14
),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
center
,
children:
[
// Icon container
Container
(
width:
88
,
height:
88
,
decoration:
BoxDecoration
(
decoration:
BoxDecoration
(
color:
c
olor
.
w
ithOpacity
(
0.7
)
,
color:
C
olor
s
.
w
hite
,
borderRadius:
BorderRadius
.
circular
(
1
2
),
borderRadius:
BorderRadius
.
circular
(
1
4
),
),
),
child:
Center
(
child:
Row
(
child:
SizedBox
(
children:
[
height:
40
,
SizedBox
(
width:
40
,
height:
42
,
width:
42
,
child:
SvgPicture
.
asset
(
child:
SvgPicture
.
asset
(
icon
,
"assets/svg/help_ic.svg"
,
fit:
BoxFit
.
fitWidth
,
height:
30
,
width:
30
,
fit:
BoxFit
.
contain
,
),
),
),
),
const
SizedBox
(
width:
12
),
const
Expanded
(
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Text
(
"Get help for an order"
,
overflow:
TextOverflow
.
ellipsis
,
style:
TextStyle
(
fontFamily:
"Poppins"
,
fontSize:
14
,
color:
Colors
.
black
,
),
),
),
),
const
SizedBox
(
height:
8
),
SizedBox
(
height:
4
),
// Title
Text
(
SizedBox
(
"Select an order"
,
child:
Text
(
title
,
textAlign:
TextAlign
.
center
,
style:
TextStyle
(
style:
TextStyle
(
color:
AppColors
.
nearDarkText
,
fontFamily:
"Poppins"
,
fontSize:
14
,
fontSize:
12
,
fontWeight:
FontWeight
.
w400
,
color:
Colors
.
grey
,
fontFamily:
"Plus Jakarta Sans"
,
),
),
),
),
],
),
),
SizedBox
(
height:
30
,
width:
30
,
child:
SvgPicture
.
asset
(
"assets/svg/continue_ic.svg"
,
color:
Color
(
0xFF000000
),
height:
18
,
width:
18
,
),
),
),
const
SizedBox
(
height:
4
),
],
],
),
),
),
),
);
);
}
}
Widget
_buildProcessingTicketsSection
()
{
/// ✅ Processing tickets from provider
Widget
_buildProcessingTicketsSection
(
List
<
dynamic
>
tickets
)
{
if
(
tickets
.
isEmpty
)
{
return
const
Center
(
child:
Text
(
"No processing tickets"
));
}
return
Container
(
return
Container
(
padding:
const
EdgeInsets
.
all
(
4
),
padding:
const
EdgeInsets
.
all
(
4
),
child:
Column
(
child:
Column
(
children:
processingT
ickets
.
map
((
ticket
)
{
children:
t
ickets
.
map
((
ticket
)
{
return
Padding
(
return
Padding
(
padding:
const
EdgeInsets
.
only
(
bottom:
4
),
padding:
const
EdgeInsets
.
only
(
bottom:
4
),
child:
CommonListItem
(
child:
CommonListItem
(
title:
ticket
[
'title'
]!,
orderId:
ticket
.
ticketNumber
??
''
,
date:
ticket
[
'date'
]!,
title:
ticket
.
type
??
'Untitled'
,
status:
ticket
[
'status'
]!,
date:
ticket
.
date
??
''
,
status:
'In Process'
,
onTap:
()
{
onTap:
()
{
Navigator
.
push
(
Navigator
.
push
(
context
,
context
,
MaterialPageRoute
(
builder:
(
context
)
=>
ProcessTicketChatScreen
())
MaterialPageRoute
(
builder:
(
context
)
=>
TicketChatScreen
(
sessionId:
widget
.
sessionId
,
accId:
widget
.
accId
,
ticketId:
ticket
.
id
,
status:
"In Process"
,
),
),
);
);
},
},
),
),
...
@@ -225,19 +253,35 @@ class _HelpScreenState extends State<HelpScreen> {
...
@@ -225,19 +253,35 @@ class _HelpScreenState extends State<HelpScreen> {
);
);
}
}
Widget
_buildClosedTicketsSection
()
{
/// ✅ Closed tickets from provider
Widget
_buildClosedTicketsSection
(
List
<
dynamic
>
tickets
)
{
if
(
tickets
.
isEmpty
)
{
return
const
Center
(
child:
Text
(
"No closed tickets"
));
}
return
Container
(
return
Container
(
padding:
const
EdgeInsets
.
all
(
4
),
padding:
const
EdgeInsets
.
all
(
4
),
child:
Column
(
child:
Column
(
children:
closedT
ickets
.
map
((
ticket
)
{
children:
t
ickets
.
map
((
ticket
)
{
return
Padding
(
return
Padding
(
padding:
const
EdgeInsets
.
only
(
bottom:
4
),
padding:
const
EdgeInsets
.
only
(
bottom:
4
),
child:
CommonListItem
(
child:
CommonListItem
(
title:
ticket
[
'title'
]!,
orderId:
ticket
.
ticketNumber
??
''
,
date:
ticket
[
'date'
]!,
title:
ticket
.
type
??
'Untitled'
,
status:
""
,
// Empty status for closed tickets
date:
ticket
.
date
??
''
,
status:
""
,
onTap:
()
{
onTap:
()
{
// Handle closed ticket tap
Navigator
.
push
(
context
,
MaterialPageRoute
(
builder:
(
context
)
=>
TicketChatScreen
(
sessionId:
widget
.
sessionId
,
accId:
widget
.
accId
,
ticketId:
ticket
.
id
,
status:
"Closed"
,
),
),
);
},
},
),
),
);
);
...
@@ -247,7 +291,9 @@ class _HelpScreenState extends State<HelpScreen> {
...
@@ -247,7 +291,9 @@ class _HelpScreenState extends State<HelpScreen> {
}
}
}
}
class
CommonListItem
extends
StatelessWidget
{
class
CommonListItem
extends
StatelessWidget
{
final
String
orderId
;
final
String
title
;
final
String
title
;
final
String
date
;
final
String
date
;
final
String
status
;
final
String
status
;
...
@@ -255,6 +301,7 @@ class CommonListItem extends StatelessWidget {
...
@@ -255,6 +301,7 @@ class CommonListItem extends StatelessWidget {
const
CommonListItem
({
const
CommonListItem
({
Key
?
key
,
Key
?
key
,
required
this
.
orderId
,
required
this
.
title
,
required
this
.
title
,
required
this
.
date
,
required
this
.
date
,
required
this
.
status
,
required
this
.
status
,
...
@@ -265,7 +312,7 @@ class CommonListItem extends StatelessWidget {
...
@@ -265,7 +312,7 @@ class CommonListItem extends StatelessWidget {
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
return
Column
(
return
Column
(
children:
[
children:
[
const
SizedBox
(
height:
4
),
const
SizedBox
(
height:
6
),
Material
(
Material
(
color:
Colors
.
transparent
,
color:
Colors
.
transparent
,
child:
InkWell
(
child:
InkWell
(
...
@@ -276,10 +323,20 @@ class CommonListItem extends StatelessWidget {
...
@@ -276,10 +323,20 @@ class CommonListItem extends StatelessWidget {
color:
Colors
.
white
,
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
circular
(
12
),
borderRadius:
BorderRadius
.
circular
(
12
),
),
),
padding:
const
EdgeInsets
.
symmetric
(
vertical:
1
4
,
horizontal:
1
6
),
padding:
const
EdgeInsets
.
symmetric
(
vertical:
1
6
,
horizontal:
1
7
),
child:
Row
(
child:
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
children:
[
children:
[
Text
(
"#
${orderId}
"
,
style:
const
TextStyle
(
fontSize:
12
,
fontFamily:
"Plus Jakarta Sans"
,
fontWeight:
FontWeight
.
w400
,
color:
AppColors
.
subtitleText
,
),
),
SizedBox
(
width:
10
,),
Expanded
(
Expanded
(
child:
Column
(
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
crossAxisAlignment:
CrossAxisAlignment
.
start
,
...
@@ -294,6 +351,7 @@ class CommonListItem extends StatelessWidget {
...
@@ -294,6 +351,7 @@ class CommonListItem extends StatelessWidget {
),
),
),
),
const
SizedBox
(
height:
4
),
const
SizedBox
(
height:
4
),
if
(
status
.
isNotEmpty
)
Text
(
Text
(
date
,
date
,
style:
TextStyle
(
style:
TextStyle
(
...
@@ -323,6 +381,16 @@ class CommonListItem extends StatelessWidget {
...
@@ -323,6 +381,16 @@ class CommonListItem extends StatelessWidget {
),
),
),
),
),
),
if
(
status
.
isEmpty
)
Text
(
date
,
style:
TextStyle
(
fontSize:
12
,
fontFamily:
"Plus Jakarta Sans"
,
fontWeight:
FontWeight
.
w400
,
color:
Colors
.
grey
[
600
],
),
),
],
],
),
),
),
),
...
...
lib/Screens/HelpScreens/OrderHelpScreen.dart
0 → 100644
View file @
5ac94fe2
import
'package:flutter/material.dart'
;
import
'package:flutter_svg/flutter_svg.dart'
;
import
'package:gen_rentals/Screens/HelpScreens/ProcessTicketScreen.dart'
;
import
'package:gen_rentals/Utility/Reusablewidgets.dart'
;
import
'../../Models/DashboardResponse.dart'
;
import
'../../Notifier/DashboardProvider.dart'
;
import
'../../Notifier/HelpAndEnquiryProvider.dart'
;
import
'../../Utility/AppColors.dart'
;
import
'package:provider/provider.dart'
;
import
'../ProductsDetailScreen.dart'
;
import
'CreateTicketScreen.dart'
;
class
OrderHelpScreen
extends
StatefulWidget
{
final
String
sessionId
;
final
String
accId
;
OrderHelpScreen
({
super
.
key
,
required
this
.
sessionId
,
required
this
.
accId
,
});
@override
State
<
OrderHelpScreen
>
createState
()
=>
_OrderHelpScreenState
();
}
class
_OrderHelpScreenState
extends
State
<
OrderHelpScreen
>
{
@override
void
initState
()
{
super
.
initState
();
/// ✅ Fetch ticket list on screen load
Future
.
microtask
(()
async
{
final
provider
=
Provider
.
of
<
HelpAndEnquiryProvider
>(
context
,
listen:
false
);
await
provider
.
fetchTicketList
(
sessionId:
widget
.
sessionId
,
accId:
widget
.
accId
,
);
});
}
// ✅ (unchanged)
final
List
<
Map
<
String
,
dynamic
>>
createNewTickets
=
[
{
'title'
:
'Payment Issues'
,
'description'
:
'Get help with payment related problems'
,
'icon'
:
"assets/svg/rupee_coin_ic.svg"
,
'color'
:
Color
(
0xFFFFEFBE
),
},
{
'title'
:
'Bill Related Issues'
,
'description'
:
'Resolve bill and invoice matters'
,
'icon'
:
"assets/svg/know_pay.svg"
,
'color'
:
Color
(
0xFFCEF9FF
),
},
{
'title'
:
'Other Issues'
,
'description'
:
'Any other support you need'
,
'icon'
:
'assets/svg/help_ic.svg'
,
'color'
:
Color
(
0xFFE4E5FF
),
},
];
@override
Widget
build
(
BuildContext
context
)
{
final
dashboardProvider
=
Provider
.
of
<
DashboardProvider
>(
context
);
final
dashboardData
=
dashboardProvider
.
dashboardData
;
double
screenWidth
=
MediaQuery
.
of
(
context
).
size
.
width
;
double
screenHeight
=
MediaQuery
.
of
(
context
).
size
.
height
;
double
bottomPadding
=
MediaQuery
.
of
(
context
).
padding
.
bottom
;
return
SafeArea
(
top:
false
,
child:
Scaffold
(
backgroundColor:
AppColors
.
backgroundRegular
,
appBar:
AppBar
(
automaticallyImplyLeading:
false
,
backgroundColor:
Colors
.
white
,
title:
Row
(
children:
[
InkResponse
(
onTap:
()
=>
Navigator
.
pop
(
context
,
true
),
child:
SvgPicture
.
asset
(
"assets/svg/continue_left_ic.svg"
,
height:
25
,
),
),
const
SizedBox
(
width:
10
),
const
Text
(
"Help?"
,
style:
TextStyle
(
fontSize:
16
,
fontFamily:
"Plus Jakarta Sans"
,
fontWeight:
FontWeight
.
w600
,
color:
Colors
.
black87
,
),
),
],
),
),
// ✅ Provider Consumer used here
body:
Consumer
<
HelpAndEnquiryProvider
>(
builder:
(
context
,
provider
,
_
)
{
if
(
provider
.
isLoading
)
{
return
const
Center
(
child:
CircularProgressIndicator
());
}
if
(
provider
.
errorMessage
!=
null
)
{
return
Center
(
child:
Text
(
provider
.
errorMessage
!,
style:
const
TextStyle
(
color:
Colors
.
red
)),
);
}
final
ticketData
=
provider
.
ticketListResponse
?.
tickets
;
final
processingTickets
=
ticketData
?.
inProgress
??
[];
final
closedTickets
=
ticketData
?.
closed
??
[];
return
SingleChildScrollView
(
padding:
const
EdgeInsets
.
all
(
16
),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
// Create New Ticket Section
const
SectionHeading
(
title:
'Select the order you are having issues with'
,
padding:
EdgeInsets
.
symmetric
(
horizontal:
2
,
vertical:
4
),
),
Container
(
width:
double
.
infinity
,
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
2
,
vertical:
10
),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
// Section Title
// Text(
// "Subscribed Orders",
// style: TextStyle(
// fontFamily: "Poppins",
// color: Colors.grey.shade800,
// fontSize: 18,
// fontWeight: FontWeight.w600,
// ),
// ),
const
SizedBox
(
height:
16
),
// Show loading or products list
if
(
dashboardProvider
.
isLoading
&&
dashboardData
==
null
)
const
Center
(
child:
CircularProgressIndicator
(),
)
else
if
(
dashboardData
?.
orders
==
null
||
dashboardData
!.
orders
!.
isEmpty
)
const
Text
(
"No products subscribed"
,
style:
TextStyle
(
fontFamily:
"Poppins"
,
color:
Colors
.
grey
,
fontSize:
14
,
),
)
else
// List of subscribed products from API
Column
(
children:
dashboardData
!.
orders
!.
map
((
product
)
{
return
Column
(
children:
[
InkResponse
(
onTap:
()
=>
_showReasonBottomSheet
(),
child:
_buildProductItemFromApi
(
product
),
),
const
SizedBox
(
height:
16
),
],
);
}).
toList
(),
),
],
),
),
],
),
);
},
),
),
);
}
// Helper widget for product item from API data
Widget
_buildProductItemFromApi
(
Orders
product
)
{
final
bool
hasPending
=
product
.
hasPendingPayment
==
true
;
final
productList
=
product
.
products
??
[];
return
Container
(
margin:
const
EdgeInsets
.
symmetric
(
vertical:
6
),
decoration:
BoxDecoration
(
borderRadius:
BorderRadius
.
circular
(
15
),
boxShadow:
[
BoxShadow
(
color:
Colors
.
grey
.
withOpacity
(
0.15
),
blurRadius:
6
,
offset:
const
Offset
(
0
,
2
),
),
],
),
child:
Stack
(
children:
[
// ===== Red Strip (Behind Card) =====
if
(
hasPending
)
Positioned
.
fill
(
top:
null
,
child:
Align
(
alignment:
Alignment
.
bottomCenter
,
child:
Container
(
height:
45
,
decoration:
const
BoxDecoration
(
color:
Color
(
0xFFFFE2E0
),
borderRadius:
BorderRadius
.
only
(
bottomLeft:
Radius
.
circular
(
15
),
bottomRight:
Radius
.
circular
(
15
),
),
),
child:
Row
(
children:
[
const
SizedBox
(
width:
12
),
const
Icon
(
Icons
.
info_outline
,
color:
Colors
.
red
,
size:
18
),
const
SizedBox
(
width:
6
),
Expanded
(
child:
Text
(
product
.
pendingPaymentText
??
"Payment Pending. Please Pay before incurring fines."
,
style:
const
TextStyle
(
fontFamily:
"Poppins"
,
color:
Colors
.
red
,
fontSize:
12
,
fontWeight:
FontWeight
.
w400
,
),
),
),
const
SizedBox
(
width:
12
),
],
),
),
),
),
// ===== Main White Card =====
Container
(
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
circular
(
15
),
),
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
10
,
vertical:
10
),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
/// Header Row (image, order id, date, badge)
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
children:
[
Row
(
children:
[
Container
(
padding:
const
EdgeInsets
.
all
(
14
),
decoration:
BoxDecoration
(
color:
const
Color
(
0xffF2F2F2
),
borderRadius:
BorderRadius
.
circular
(
16
),
),
child:
Image
.
network
(
product
.
productImage
??
""
,
height:
40
,
width:
40
,
fit:
BoxFit
.
contain
,
errorBuilder:
(
context
,
error
,
stack
)
=>
Image
.
asset
(
'assets/images/gene_png.png'
,
height:
40
,
width:
40
),
),
),
const
SizedBox
(
width:
8
),
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Text
(
"#
${product.orderid ?? "0"}
"
,
style:
const
TextStyle
(
fontFamily:
"Poppins"
,
color:
Color
(
0xFF008CDE
),
fontSize:
16
,
fontWeight:
FontWeight
.
w500
,
height:
1.2
,
),
),
Text
(
product
.
rentedDate
??
"Rented date not available"
,
style:
TextStyle
(
fontFamily:
"Poppins"
,
color:
Colors
.
grey
.
shade600
,
fontSize:
12
,
),
),
],
),
],
),
// ✅ Gradient expiry badge
if
(
product
.
expiringText
!=
null
&&
product
.
expiringText
!.
isNotEmpty
)
Container
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
10
,
vertical:
6
),
decoration:
BoxDecoration
(
gradient:
_getGradientByColor
(
product
.
expiringInColor
),
borderRadius:
BorderRadius
.
circular
(
8
),
),
child:
Text
(
product
.
expiringText
!,
style:
const
TextStyle
(
color:
Colors
.
black87
,
fontSize:
12
,
fontWeight:
FontWeight
.
w500
,
),
),
),
],
),
const
SizedBox
(
height:
6
),
const
Divider
(),
/// ===== Product List (with +3 More on same line) =====
Builder
(
builder:
(
context
)
{
final
visibleItems
=
productList
.
take
(
2
).
toList
();
final
remaining
=
productList
.
length
-
visibleItems
.
length
;
return
Row
(
crossAxisAlignment:
CrossAxisAlignment
.
center
,
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
children:
[
// Left side → Product list (bulleted)
Expanded
(
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
for
(
int
i
=
0
;
i
<
visibleItems
.
length
;
i
++)
Padding
(
padding:
const
EdgeInsets
.
only
(
bottom:
4
),
child:
Row
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
const
SizedBox
(
width:
8
),
const
Text
(
"• "
,
style:
TextStyle
(
color:
Colors
.
black
,
fontSize:
16
),
),
Expanded
(
child:
Text
(
visibleItems
[
i
],
style:
const
TextStyle
(
color:
Colors
.
black
,
fontSize:
16
,
fontWeight:
FontWeight
.
w400
,
),
),
),
],
),
),
],
),
),
// Right side → +x More (vertically centered)
if
(
remaining
>
0
)
Padding
(
padding:
const
EdgeInsets
.
only
(
left:
8
,
right:
4
),
child:
Align
(
alignment:
Alignment
.
center
,
child:
Text
(
"+
$remaining
More"
,
style:
const
TextStyle
(
fontFamily:
"Poppins"
,
color:
Color
(
0xFF008CDE
),
fontSize:
14
,
fontWeight:
FontWeight
.
w500
,
),
),
),
),
],
);
},
),
],
),
),
],
),
);
}
// Gradient helper
LinearGradient
_getGradientByColor
(
String
?
color
)
{
switch
(
color
)
{
case
"Red"
:
return
const
LinearGradient
(
colors:
[
Color
(
0xFFFFE0E0
),
Color
(
0xFFFFC0C0
)],
begin:
Alignment
.
topLeft
,
end:
Alignment
.
bottomRight
,
);
case
"Green"
:
default
:
return
const
LinearGradient
(
colors:
[
Color
(
0xFFE9FFDD
),
Color
(
0xFFB5FFD1
)],
begin:
Alignment
.
topLeft
,
end:
Alignment
.
bottomRight
,
);
}
}
void
_showReasonBottomSheet
()
{
// Your existing bottom sheet implementation
showModalBottomSheet
(
context:
context
,
backgroundColor:
Colors
.
white
,
shape:
const
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
only
(
topLeft:
Radius
.
circular
(
20
),
topRight:
Radius
.
circular
(
20
),
),
),
builder:
(
context
)
{
return
Container
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
10
,
vertical:
14
),
child:
Column
(
mainAxisSize:
MainAxisSize
.
min
,
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
const
Text
(
"Select Your Reason"
,
style:
TextStyle
(
fontSize:
18
,
fontFamily:
"Poppins"
,
fontWeight:
FontWeight
.
w600
,
color:
Colors
.
black87
,
),
),
const
SizedBox
(
height:
24
),
GridView
.
builder
(
shrinkWrap:
true
,
physics:
const
NeverScrollableScrollPhysics
(),
gridDelegate:
const
SliverGridDelegateWithFixedCrossAxisCount
(
crossAxisCount:
3
,
crossAxisSpacing:
12
,
mainAxisSpacing:
12
,
childAspectRatio:
0.99
,
),
itemCount:
createNewTickets
.
length
,
itemBuilder:
(
context
,
index
)
{
final
ticket
=
createNewTickets
[
index
];
final
String
title
=
ticket
[
'title'
]
??
'Unknown'
;
final
String
description
=
ticket
[
'description'
]
??
''
;
final
String
icon
=
ticket
[
'icon'
]
??
'assets/svg/help_ic.svg'
;
final
Color
color
=
ticket
[
'color'
]
??
Colors
.
grey
;
return
_buildFeatureCard
(
title:
title
,
description:
description
,
icon:
icon
,
color:
color
,
);
},
),
const
SizedBox
(
height:
24
),
],
),
);
},
);
}
Widget
_buildFeatureCard
({
required
String
title
,
required
String
description
,
required
String
icon
,
required
Color
color
,
})
{
return
GestureDetector
(
onTap:
()
{
Navigator
.
push
(
context
,
MaterialPageRoute
(
builder:
(
context
)
=>
HelpTicketScreen
(
reason:
title
,))
);
},
child:
Container
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
2
,
vertical:
1
),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
center
,
children:
[
// Icon container
Container
(
width:
88
,
height:
88
,
decoration:
BoxDecoration
(
color:
color
.
withOpacity
(
0.7
),
borderRadius:
BorderRadius
.
circular
(
12
),
),
child:
Center
(
child:
SizedBox
(
height:
40
,
width:
40
,
child:
SvgPicture
.
asset
(
icon
,
fit:
BoxFit
.
fitWidth
,
),
),
),
),
const
SizedBox
(
height:
8
),
// Title
SizedBox
(
child:
Text
(
title
,
textAlign:
TextAlign
.
center
,
style:
TextStyle
(
color:
AppColors
.
nearDarkText
,
fontSize:
14
,
fontWeight:
FontWeight
.
w400
,
fontFamily:
"Plus Jakarta Sans"
,
),
),
),
const
SizedBox
(
height:
4
),
],
),
),
);
}
}
lib/Screens/HelpScreens/ProcessTicketScreen.dart
View file @
5ac94fe2
This diff is collapsed.
Click to expand it.
lib/Screens/ProductsDetailScreen.dart
View file @
5ac94fe2
...
@@ -100,7 +100,40 @@ class _ProductsDetailScreenState extends State<ProductsDetailScreen> {
...
@@ -100,7 +100,40 @@ class _ProductsDetailScreenState extends State<ProductsDetailScreen> {
],
],
),
),
),
),
backgroundColor:
AppColors
.
backgroundRegular
,
body:
_buildBody
(
provider
,
screenHeight
,
bottomPadding
),
body:
_buildBody
(
provider
,
screenHeight
,
bottomPadding
),
bottomNavigationBar:
Container
(
height:
80
,
padding:
EdgeInsets
.
symmetric
(
horizontal:
14
,
vertical:
10
),
width:
double
.
infinity
,
child:
ElevatedButton
(
onPressed:
()
{
Navigator
.
push
(
context
,
MaterialPageRoute
(
builder:
(
context
)
=>
BillDetailListScreen
())
);
// Handle view bill action
FocusScope
.
of
(
context
).
unfocus
();
},
style:
ElevatedButton
.
styleFrom
(
backgroundColor:
AppColors
.
buttonColor
,
foregroundColor:
Colors
.
white
,
padding:
const
EdgeInsets
.
symmetric
(
vertical:
16
),
shape:
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
circular
(
28
),
),
elevation:
0
,
),
child:
const
Text
(
"View Bill"
,
style:
TextStyle
(
fontSize:
16
,
fontFamily:
"Plus Jakarta Sans"
,
fontWeight:
FontWeight
.
w600
,
),
),
),
),
),
),
);
);
},
},
...
@@ -153,7 +186,7 @@ class _ProductsDetailScreenState extends State<ProductsDetailScreen> {
...
@@ -153,7 +186,7 @@ class _ProductsDetailScreenState extends State<ProductsDetailScreen> {
final
order
=
provider
.
orderDetails
!;
final
order
=
provider
.
orderDetails
!;
return
Container
(
return
Container
(
color:
const
Color
(
0xFFF3F3F3
)
,
color:
AppColors
.
backgroundRegular
,
height:
screenHeight
,
height:
screenHeight
,
child:
SingleChildScrollView
(
child:
SingleChildScrollView
(
child:
Column
(
child:
Column
(
...
@@ -192,19 +225,20 @@ class _ProductsDetailScreenState extends State<ProductsDetailScreen> {
...
@@ -192,19 +225,20 @@ class _ProductsDetailScreenState extends State<ProductsDetailScreen> {
const
SizedBox
(
height:
8
),
const
SizedBox
(
height:
8
),
Text
(
Text
(
order
.
rentedDate
??
'Date not available'
,
order
.
rentedDate
??
'Date not available'
,
style:
const
TextStyle
(
style:
TextStyle
(
fontSize:
14
,
fontSize:
14
,
fontFamily:
"Poppins"
,
fontFamily:
"Poppins"
,
fontWeight:
FontWeight
.
w400
,
fontWeight:
FontWeight
.
w400
,
color:
Colors
.
grey
,
color:
App
Colors
.
subtitleText
,
),
),
),
),
const
SizedBox
(
height:
14
),
const
SizedBox
(
height:
14
),
Container
(
Container
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
12
,
vertical:
6
),
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
10
,
vertical:
6
),
decoration:
BoxDecoration
(
decoration:
BoxDecoration
(
color:
_getExpiring
Color
(
order
.
expiringInColor
),
gradient:
_getGradientBy
Color
(
order
.
expiringInColor
),
borderRadius:
BorderRadius
.
circular
(
16
),
borderRadius:
BorderRadius
.
circular
(
8
),
),
),
child:
Text
(
child:
Text
(
order
.
expiringText
??
'Expiring info not available'
,
order
.
expiringText
??
'Expiring info not available'
,
...
@@ -212,7 +246,7 @@ class _ProductsDetailScreenState extends State<ProductsDetailScreen> {
...
@@ -212,7 +246,7 @@ class _ProductsDetailScreenState extends State<ProductsDetailScreen> {
fontSize:
12
,
fontSize:
12
,
fontFamily:
"Poppins"
,
fontFamily:
"Poppins"
,
fontWeight:
FontWeight
.
w500
,
fontWeight:
FontWeight
.
w500
,
color:
Colors
.
white
,
color:
Colors
.
black87
,
),
),
),
),
),
),
...
@@ -264,51 +298,29 @@ class _ProductsDetailScreenState extends State<ProductsDetailScreen> {
...
@@ -264,51 +298,29 @@ class _ProductsDetailScreenState extends State<ProductsDetailScreen> {
onTap:
()
=>
_showReasonBottomSheet
(),
onTap:
()
=>
_showReasonBottomSheet
(),
child:
Row
(
child:
Row
(
children:
[
children:
[
SvgPicture
.
asset
(
"assets/svg/have_compaints.svg"
,
height:
30
,
width:
30
,
),
SizedBox
(
width:
8
,),
Text
(
Text
(
"Need help with this order?"
,
"Need help with this order?"
,
style:
TextStyle
(
style:
TextStyle
(
fontSize:
14
,
fontSize:
14
,
fontFamily:
"Poppins"
,
fontFamily:
"Poppins"
,
fontWeight:
FontWeight
.
w
5
00
,
fontWeight:
FontWeight
.
w
4
00
,
color:
Colors
.
grey
,
color:
App
Colors
.
amountText
,
),
),
),
),
],
],
),
),
),
),
const
SizedBox
(
height:
16
),
const
SizedBox
(
height:
16
),
// View Bill button
// View Bill button
SizedBox
(
width:
double
.
infinity
,
child:
ElevatedButton
(
onPressed:
()
{
Navigator
.
push
(
context
,
MaterialPageRoute
(
builder:
(
context
)
=>
BillDetailListScreen
())
);
// Handle view bill action
FocusScope
.
of
(
context
).
unfocus
();
},
style:
ElevatedButton
.
styleFrom
(
backgroundColor:
AppColors
.
buttonColor
,
foregroundColor:
Colors
.
white
,
padding:
const
EdgeInsets
.
symmetric
(
vertical:
16
),
shape:
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
circular
(
28
),
),
elevation:
0
,
),
child:
const
Text
(
"View Bill"
,
style:
TextStyle
(
fontSize:
16
,
fontFamily:
"Plus Jakarta Sans"
,
fontWeight:
FontWeight
.
w600
,
),
),
),
),
],
],
),
),
)
)
...
@@ -347,12 +359,12 @@ class _ProductsDetailScreenState extends State<ProductsDetailScreen> {
...
@@ -347,12 +359,12 @@ class _ProductsDetailScreenState extends State<ProductsDetailScreen> {
children:
[
children:
[
// Product ID and Name
// Product ID and Name
Text
(
Text
(
product
.
idName
??
product
.
id
??
'N/A'
,
"#
${
product.idName
}
"
,
style:
const
TextStyle
(
style:
TextStyle
(
fontSize:
14
,
fontSize:
14
,
fontFamily:
"Poppins"
,
fontFamily:
"Poppins"
,
fontWeight:
FontWeight
.
w500
,
fontWeight:
FontWeight
.
w500
,
color:
Colors
.
grey
,
color:
App
Colors
.
amountText
,
),
),
),
),
const
SizedBox
(
height:
4
),
const
SizedBox
(
height:
4
),
...
@@ -381,11 +393,11 @@ class _ProductsDetailScreenState extends State<ProductsDetailScreen> {
...
@@ -381,11 +393,11 @@ class _ProductsDetailScreenState extends State<ProductsDetailScreen> {
product
.
dispatchDate
!=
null
?
product
.
dispatchDate
!=
null
?
"Dispatched On
${product.dispatchDate!}
"
:
"Dispatched On
${product.dispatchDate!}
"
:
"Dispatch date not available"
,
"Dispatch date not available"
,
style:
const
TextStyle
(
style:
TextStyle
(
fontSize:
12
,
fontSize:
12
,
fontFamily:
"Poppins"
,
fontFamily:
"Poppins"
,
fontWeight:
FontWeight
.
w400
,
fontWeight:
FontWeight
.
w400
,
color:
Colors
.
grey
,
color:
App
Colors
.
subtitleText
,
),
),
),
),
),
),
...
@@ -445,16 +457,21 @@ class _ProductsDetailScreenState extends State<ProductsDetailScreen> {
...
@@ -445,16 +457,21 @@ class _ProductsDetailScreenState extends State<ProductsDetailScreen> {
);
);
}
}
Color
_getExpiringColor
(
String
?
colorString
)
{
LinearGradient
_getGradientByColor
(
String
?
color
)
{
if
(
colorString
==
null
||
colorString
.
isEmpty
)
{
switch
(
color
)
{
return
const
Color
(
0xFFFFEBEB
);
// Default color
case
"Red"
:
}
return
const
LinearGradient
(
colors:
[
Color
(
0xFFFFE0E0
),
Color
(
0xFFFFC0C0
)],
try
{
begin:
Alignment
.
topLeft
,
// Assuming colorString is in format like "FFFF5757"
end:
Alignment
.
bottomRight
,
return
Color
(
int
.
parse
(
'FF
$colorString
'
,
radix:
16
));
);
}
catch
(
e
)
{
case
"Green"
:
return
const
Color
(
0xFFFFEFEF
);
// Default color on error
default
:
return
const
LinearGradient
(
colors:
[
Color
(
0xFFE9FFDD
),
Color
(
0xFFB5FFD1
)],
begin:
Alignment
.
topLeft
,
end:
Alignment
.
bottomRight
,
);
}
}
}
}
...
...
Prev
1
2
Next
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