This commit is contained in:
bala 2025-11-25 00:36:35 +05:30
parent a3cf294070
commit 33c15b1a5f
14 changed files with 988 additions and 67 deletions

View File

@ -13,4 +13,8 @@ class ApiEndpoints {
static const signup = '/auth/signup';
static const forgotPassword = '/auth/forgot-password';
static const userDetails = '/auth/users/';
///Turn14
static const turn14Save = '/auth/turn14/save';
static const turn14Status = '/auth/turn14/status';
}

View File

@ -3,6 +3,9 @@ import 'package:autos/presentation/screens/auth/forgot_password_screen.dart';
import 'package:autos/presentation/screens/auth/login_screen.dart';
import 'package:autos/presentation/screens/auth/sign_up_screen.dart';
import 'package:autos/presentation/screens/dashboard/dashboard_screen.dart';
import 'package:autos/presentation/screens/ebay/ebay_screen.dart';
import 'package:autos/presentation/screens/store/create_location_screen.dart';
import 'package:autos/presentation/screens/store/store.dart';
import 'package:autos/presentation/screens/turn14_screen/turn14_screen.dart';
import 'package:flutter/material.dart';
@ -45,6 +48,15 @@ class AppRouter {
case AppRoutePaths.turn14:
return slideRoute(const Turn14Screen());
case AppRoutePaths.ebay:
return slideRoute(EbayScreen());
case AppRoutePaths.store:
return slideRoute(StoreScreen());
case AppRoutePaths.createStoreLocation:
return slideRoute(CreateLocationScreen());
default:
return _defaultFallback(settings);
}

View File

@ -4,4 +4,7 @@ class AppRoutePaths {
static const forgotPassword = '/forgotPassword';
static const dashboard = '/dashboard';
static const turn14 = '/turn14';
static const ebay = '/ebay';
static const store = '/store';
static const createStoreLocation = '/createStoreLocation';
}

View File

@ -59,8 +59,8 @@ class SideMenu extends ConsumerWidget {
// --- INTEGRATIONS ---
_sectionHeader("INTEGRATIONS"),
_menuItem(context, "", "Turn14", AppRoutePaths.turn14),
_menuItem(context, "🛍️", "eBay", 'ebay'),
_menuItem(context, "🛒", "Store", 'store'),
_menuItem(context, "🛍️", "eBay", AppRoutePaths.ebay),
_menuItem(context, "🛒", "Store", AppRoutePaths.store),
// --- MANAGE ---
_sectionHeader("MANAGE"),

View File

@ -0,0 +1,98 @@
import 'dart:convert';
import 'package:autos/domain/entities/turn14.dart';
class Turn14Response {
final String code;
final String message;
final String userId;
final String accessToken;
const Turn14Response({
required this.code,
required this.message,
required this.userId,
required this.accessToken,
});
factory Turn14Response.fromJson(Map<String, dynamic> json) {
return Turn14Response(
code: json['code'] ?? '',
message: json['message'] ?? '',
userId: json['userid'] ?? '',
accessToken: json['access_token'] ?? '',
);
}
Map<String, dynamic> toJson() {
return {
'code': code,
'message': message,
'userid': userId,
'access_token': accessToken,
};
}
/// Convert model raw JSON string
String toRawJson() => jsonEncode(toJson());
/// Convert raw JSON string model
factory Turn14Response.fromRawJson(String raw) =>
Turn14Response.fromJson(jsonDecode(raw));
/// Convert Response Domain Entity
Turn14Entity toEntity() {
return Turn14Entity(
code: code,
message: message,
userId: userId,
accessToken: accessToken,
);
}
@override
String toString() {
return 'Turn14Response(code: $code, message: $message, userId: $userId, accessToken: $accessToken)';
}
}
class Turn14StatusModel {
final String userId;
final bool hasCredentials;
final String? clientId;
final String? clientSecret;
final String? accessToken;
final String? expiresIn;
Turn14StatusModel({
required this.userId,
required this.hasCredentials,
this.clientId,
this.clientSecret,
this.accessToken,
this.expiresIn,
});
factory Turn14StatusModel.fromJson(Map<String, dynamic> json) {
return Turn14StatusModel(
userId: json["userid"],
hasCredentials: json["hasCredentials"] ?? false,
clientId: json["credentials"]?["turn14clientid"],
clientSecret: json["credentials"]?["turn14clientsecret"],
accessToken: json["tokenInfo"]?["access_token"],
expiresIn: json["tokenInfo"]?["expires_in"],
);
}
Turn14StatusEntity toEntity() {
return Turn14StatusEntity(
userId: userId,
hasCredentials: hasCredentials,
clientId: clientId,
clientSecret: clientSecret,
accessToken: accessToken,
expiresIn: expiresIn,
);
}
}

View File

@ -0,0 +1,77 @@
import 'package:autos/core/constants/api_endpoints.dart';
import 'package:autos/data/models/turn14_model.dart';
import 'package:autos/data/sources/remote/api_service.dart';
import 'package:autos/domain/entities/turn14.dart';
import 'package:dio/dio.dart';
abstract class Turn14Repository {
/// Save Turn14 credentials
Future<Turn14Entity> save({
required String userId,
required String clientId,
required String clientSecret,
});
}
class Turn14RepositoryImpl implements Turn14Repository {
final ApiService _apiService;
Turn14RepositoryImpl(this._apiService);
@override
Future<Turn14Entity> save({
required String userId,
required String clientId,
required String clientSecret,
}) async {
try {
final response = await _apiService.post(ApiEndpoints.turn14Save, {
"userid": userId,
"turn14clientid": clientId,
"turn14clientsecret": clientSecret,
});
// Check status
if (response.statusCode == 200) {
final data = response.data;
if (data['code'] == "TURN14_SAVED") {
/// Convert Response Entity
return Turn14Response.fromJson(data).toEntity();
} else {
throw Exception(
data['message'] ?? "Failed to save Turn14 credentials",
);
}
} else {
throw Exception("Server error: ${response.statusCode}");
}
} on DioException catch (e) {
throw Exception("Network error: ${e.message}");
} catch (e) {
throw Exception("Unexpected error: $e");
}
}
Future<Turn14StatusEntity> status(String userId) async {
try {
final response = await _apiService.post(ApiEndpoints.turn14Status, {
"userid": userId,
});
if (response.statusCode == 200) {
final data = response.data;
if (data["code"] == "TURN14_STATUS") {
return Turn14StatusModel.fromJson(data).toEntity();
} else {
throw Exception(data["message"] ?? "Failed to fetch Turn14 status");
}
} else {
throw Exception("Server error: ${response.statusCode}");
}
} catch (e) {
throw Exception("Network/Unexpected error: $e");
}
}
}

View File

@ -0,0 +1,31 @@
class Turn14Entity {
final String code;
final String message;
final String userId;
final String accessToken;
const Turn14Entity({
required this.code,
required this.message,
required this.userId,
required this.accessToken,
});
}
class Turn14StatusEntity {
final String userId;
final bool hasCredentials;
final String? clientId;
final String? clientSecret;
final String? accessToken;
final String? expiresIn;
Turn14StatusEntity({
required this.userId,
required this.hasCredentials,
this.clientId,
this.clientSecret,
this.accessToken,
this.expiresIn,
});
}

View File

@ -0,0 +1,91 @@
import 'dart:convert';
import 'package:autos/data/models/turn14_model.dart';
import 'package:autos/data/repositories/turn14_repository_impl.dart';
import 'package:autos/data/sources/remote/api_service.dart';
import 'package:autos/domain/entities/turn14.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_riverpod/legacy.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
/// ------------------------------------------------------------
/// Service Providers
/// ------------------------------------------------------------
final turn14ApiServiceProvider =
Provider<ApiService>((ref) => ApiService());
final turn14RepositoryProvider = Provider<Turn14RepositoryImpl>((ref) {
return Turn14RepositoryImpl(ref.read(turn14ApiServiceProvider));
});
/// ------------------------------------------------------------
/// Turn14 Notifier
/// ------------------------------------------------------------
class Turn14Notifier extends StateNotifier<AsyncValue<Turn14Entity?>> {
final Turn14RepositoryImpl repository;
final _storage = const FlutterSecureStorage();
static const _turn14StorageKey = "turn14_credentials";
Turn14Notifier(this.repository) : super(const AsyncValue.data(null));
/// Save Turn14 credentials
Future<void> saveCredentials({
required String userId,
required String clientId,
required String clientSecret,
}) async {
state = const AsyncValue.loading();
try {
final response = await repository.save(
userId: userId,
clientId: clientId,
clientSecret: clientSecret,
);
// if (response is Turn14Response) {
// await _storage.write(
// key: _turn14StorageKey,
// value: response.toRawJson(),
// );
// }
state = AsyncValue.data(response);
} catch (e, st) {
state = AsyncValue.error(e, st);
}
}
/// Load saved Turn14 credentials
Future<void> loadSavedCredentials() async {
final saved = await _storage.read(key: _turn14StorageKey);
if (saved == null) return;
final decoded = jsonDecode(saved);
final model = Turn14Response.fromJson(decoded);
state = AsyncValue.data(model as Turn14Entity?);
}
/// Clear saved Turn14 data
Future<void> clear() async {
await _storage.delete(key: _turn14StorageKey);
state = const AsyncValue.data(null);
}
}
/// ------------------------------------------------------------
/// Riverpod Provider
/// ------------------------------------------------------------
final turn14Provider =
StateNotifierProvider<Turn14Notifier, AsyncValue<Turn14Entity?>>((ref) {
final repository = ref.read(turn14RepositoryProvider);
return Turn14Notifier(repository);
});

View File

@ -0,0 +1,124 @@
import 'package:flutter/material.dart';
import 'package:autos/core/widgets/hamburger_button.dart';
import 'package:autos/core/widgets/side_menu.dart';
class EbayScreen extends StatefulWidget {
const EbayScreen({super.key});
@override
State<EbayScreen> createState() => _EbayScreenState();
}
class _EbayScreenState extends State<EbayScreen> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
String selected = "ebay";
@override
Widget build(BuildContext context) {
final double topPadding = MediaQuery.of(context).padding.top + 16;
return Scaffold(
key: _scaffoldKey,
drawer: SideMenu(
selected: selected,
onItemSelected: (key) {
setState(() => selected = key);
},
),
// backgroundColor: const Color(0xFFEFFAFF),
body: Stack(
children: [
/// TITLE
Positioned(
top: topPadding,
left: 0,
right: 0,
child: Center(
child: Text(
"eBay Settings",
style: const TextStyle(
fontSize: 26,
fontWeight: FontWeight.w700,
),
),
),
),
/// MAIN BOX UI
Center(
child: Container(
width: MediaQuery.of(context).size.width * 0.90,
padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 40),
margin: EdgeInsets.only(top: topPadding + 60),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(25),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 25,
offset: const Offset(0, 12),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
/// Description
Text(
"Connect your eBay store to enable product sync, inventory updates, and order flow.",
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 15,
color: Colors.black54,
height: 1.5,
),
),
const SizedBox(height: 30),
/// BUTTON
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
// TODO: Add eBay authorization flow
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF00CFFF),
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
child: const Text(
"Connect your eBay store",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 16,
),
),
),
),
const SizedBox(height: 20),
Text(
"You'll be redirected to eBay to authorize access, then returned here.",
style: const TextStyle(fontSize: 13, color: Colors.black45),
textAlign: TextAlign.center,
),
],
),
),
),
/// HAMBURGER BUTTON
HamburgerButton(scaffoldKey: _scaffoldKey),
],
),
);
}
}

View File

@ -0,0 +1,215 @@
import 'package:flutter/material.dart';
class CreateLocationScreen extends StatefulWidget {
const CreateLocationScreen({super.key});
@override
State<CreateLocationScreen> createState() => _CreateLocationScreenState();
}
class _CreateLocationScreenState extends State<CreateLocationScreen> {
// Controllers
final TextEditingController storeName = TextEditingController();
final TextEditingController phone = TextEditingController();
final TextEditingController address1 = TextEditingController();
final TextEditingController city = TextEditingController();
final TextEditingController stateCtrl = TextEditingController();
final TextEditingController postalCode = TextEditingController();
final TextEditingController country = TextEditingController();
final TextEditingController timeZone = TextEditingController(text: "America/New_York");
TimeOfDay openTime = const TimeOfDay(hour: 9, minute: 0);
TimeOfDay closeTime = const TimeOfDay(hour: 18, minute: 0);
Future<void> pickTime({required bool isOpen}) async {
final TimeOfDay? picked = await showTimePicker(
context: context,
initialTime: isOpen ? openTime : closeTime,
);
if (picked != null) {
setState(() {
if (isOpen) openTime = picked;
else closeTime = picked;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFEFFAFF),
appBar: AppBar(
title: const Text("Create New Location"),
backgroundColor: Colors.white,
elevation: 0,
foregroundColor: Colors.black,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Container(
padding: const EdgeInsets.all(30),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 20,
offset: const Offset(0, 10),
)
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_input("Store Name *", "Enter store name", storeName),
_input("Phone *", "Enter phone number", phone),
_input("Address Line 1 *", "Enter address", address1),
Row(
children: [
Expanded(child: _input("City *", "City", city)),
const SizedBox(width: 12),
Expanded(child: _input("State *", "State", stateCtrl)),
],
),
Row(
children: [
Expanded(child: _input("Postal Code *", "Postal Code", postalCode)),
const SizedBox(width: 12),
Expanded(child: _input("Country *", "Country Code (e.g. US)", country)),
],
),
_input("Time Zone *", "America/New_York", timeZone),
const SizedBox(height: 15),
Row(
children: [
Expanded(
child: _timePicker(
label: "Open Time *",
time: openTime,
onTap: () => pickTime(isOpen: true),
),
),
const SizedBox(width: 12),
Expanded(
child: _timePicker(
label: "Close Time *",
time: closeTime,
onTap: () => pickTime(isOpen: false),
),
),
],
),
const SizedBox(height: 25),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
final json = {
"store_name": storeName.text,
"phone": phone.text,
"address1": address1.text,
"city": city.text,
"state": stateCtrl.text,
"postal_code": postalCode.text,
"country": country.text,
"timezone": timeZone.text,
"open_time": openTime.format(context),
"close_time": closeTime.format(context),
};
print("📦 LOCATION JSON → $json");
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF00CFFF),
padding: const EdgeInsets.symmetric(vertical: 15),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
child: const Text(
"Save & Console JSON",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 16,
),
),
),
),
],
),
),
),
);
}
// Reusable TextField
Widget _input(String label, String hint, TextEditingController controller) {
return Padding(
padding: const EdgeInsets.only(bottom: 18),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label,
style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 14)),
const SizedBox(height: 8),
TextField(
controller: controller,
decoration: InputDecoration(
hintText: hint,
filled: true,
fillColor: const Color(0xFFF0F6FF),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
),
),
],
),
);
}
// Time Picker Widget
Widget _timePicker({
required String label,
required TimeOfDay time,
required VoidCallback onTap,
}) {
return GestureDetector(
onTap: onTap,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label,
style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 14)),
const SizedBox(height: 8),
Container(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 18),
decoration: BoxDecoration(
color: const Color(0xFFF0F6FF),
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
Text(time.format(context),
style: const TextStyle(fontSize: 16)),
const Spacer(),
const Icon(Icons.access_time, size: 20),
],
),
)
],
),
);
}
}

View File

@ -0,0 +1,131 @@
import 'package:autos/core/routing/route_paths.dart';
import 'package:flutter/material.dart';
import 'package:autos/core/widgets/hamburger_button.dart';
import 'package:autos/core/widgets/side_menu.dart';
class StoreScreen extends StatefulWidget {
const StoreScreen({super.key});
@override
State<StoreScreen> createState() => _StoreScreenState();
}
class _StoreScreenState extends State<StoreScreen> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
String selected = "ebay";
@override
Widget build(BuildContext context) {
final double topPadding = MediaQuery.of(context).padding.top + 16;
return Scaffold(
key: _scaffoldKey,
drawer: SideMenu(
selected: selected,
onItemSelected: (key) {
setState(() => selected = key);
},
),
body: Stack(
children: [
/// TITLE
Positioned(
top: topPadding,
left: 0,
right: 0,
child: const Center(
child: Text(
"eBay Locations",
style: TextStyle(fontSize: 26, fontWeight: FontWeight.w700),
),
),
),
/// MAIN WITH BUTTONS
Center(
child: Container(
width: MediaQuery.of(context).size.width * 0.90,
padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 40),
margin: EdgeInsets.only(top: topPadding + 60),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(25),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 25,
offset: const Offset(0, 12),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
/// SAVE SELECTED BUTTON
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
// TODO: Save Selected Flow
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF00CFFF),
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
child: const Text(
"Save Selected",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 16,
),
),
),
),
const SizedBox(height: 20),
/// CREATE NEW LOCATION BUTTON
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
Navigator.pushNamed(
context,
AppRoutePaths.createStoreLocation,
);
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF00CFFF),
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
child: const Text(
"Create New Location",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 16,
),
),
),
),
],
),
),
),
/// HAMBURGER BUTTON
HamburgerButton(scaffoldKey: _scaffoldKey),
],
),
);
}
}

View File

@ -2,8 +2,10 @@ import 'package:autos/core/theme/app_typography.dart';
import 'package:autos/core/widgets/hamburger_button.dart';
import 'package:autos/core/widgets/side_menu.dart';
import 'package:autos/presentation/providers/user_provider.dart';
import 'package:autos/presentation/providers/turn14_provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:fluttertoast/fluttertoast.dart';
class Turn14Screen extends ConsumerStatefulWidget {
const Turn14Screen({super.key});
@ -16,9 +18,16 @@ class _Turn14ScreenState extends ConsumerState<Turn14Screen> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
String selected = 'turn14';
// controllers
final TextEditingController clientIdController = TextEditingController();
final TextEditingController clientSecretController = TextEditingController();
@override
Widget build(BuildContext context) {
final user = ref.watch(userDetailsProvider);
final asyncUser = ref.watch(userDetailsProvider);
final turn14State = ref.watch(turn14Provider);
final user = asyncUser.value;
final double topPadding = MediaQuery.of(context).padding.top + 16;
return Scaffold(
@ -46,13 +55,13 @@ class _Turn14ScreenState extends ConsumerState<Turn14Screen> {
),
),
/// Main Scrollable UI
SingleChildScrollView(
physics: const BouncingScrollPhysics(),
padding: EdgeInsets.fromLTRB(16, topPadding + 55, 16, 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Description Row
Row(
children: [
Text("", style: const TextStyle(fontSize: 28)),
@ -70,21 +79,16 @@ class _Turn14ScreenState extends ConsumerState<Turn14Screen> {
),
const SizedBox(height: 20),
// Client ID Input
_inputField(
label: "Client ID",
controller: TextEditingController(),
),
_inputField(label: "Client ID", controller: clientIdController),
const SizedBox(height: 20),
// Secret Key Input With Eye Icon
_passwordField(
label: "Secret Key",
controller: TextEditingController(),
controller: clientSecretController,
),
const SizedBox(height: 25),
// Save Button
/// SAVE BUTTON
SizedBox(
width: double.infinity,
child: ElevatedButton(
@ -95,65 +99,70 @@ class _Turn14ScreenState extends ConsumerState<Turn14Screen> {
),
backgroundColor: const Color(0xFF00C9FF),
),
onPressed: () {},
child: const Text(
"Save Credentials",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
onPressed: turn14State.isLoading
? null
: () async {
if (user == null) {
Fluttertoast.showToast(
msg: "⚠️ User not found",
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.BOTTOM,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0,
);
return;
}
if (clientIdController.text.trim().isEmpty ||
clientSecretController.text.trim().isEmpty) {
Fluttertoast.showToast(
msg: "⚠️ Please fill all fields",
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.BOTTOM,
backgroundColor: Colors.orange,
textColor: Colors.white,
fontSize: 16.0,
);
return;
}
await ref
.read(turn14Provider.notifier)
.saveCredentials(
userId: user.id,
clientId: clientIdController.text.trim(),
clientSecret:
clientSecretController.text.trim(),
);
Fluttertoast.showToast(
msg: "✅ Turn14 Credentials Saved Successfully",
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.BOTTOM,
backgroundColor: Colors.green,
textColor: Colors.white,
fontSize: 16.0,
);
},
child: turn14State.isLoading
? const CircularProgressIndicator(color: Colors.white)
: const Text(
"Save Credentials",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
),
const SizedBox(height: 20),
// Info Box
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFFE8F1FF),
borderRadius: BorderRadius.circular(10),
),
child: Row(
children: const [
Icon(Icons.info, color: Colors.blue),
SizedBox(width: 10),
Text(
"No credentials saved yet.",
style: TextStyle(color: Colors.black87),
),
],
),
),
_infoBox(),
const SizedBox(height: 20),
// Tips Box
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFFE8FCFF),
borderRadius: BorderRadius.circular(10),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
"💡 Connection Tips",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
SizedBox(height: 10),
Text("• Ensure your credentials are valid and active."),
Text("• Credentials are encrypted before saving."),
Text("• Contact Turn14 support for API setup help."),
],
),
),
_tipsBox(),
],
),
),
@ -164,6 +173,7 @@ class _Turn14ScreenState extends ConsumerState<Turn14Screen> {
);
}
/// UI COMPONENTS
Widget _inputField({
required String label,
required TextEditingController controller,
@ -224,4 +234,49 @@ class _Turn14ScreenState extends ConsumerState<Turn14Screen> {
},
);
}
Widget _infoBox() {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFFE8F1FF),
borderRadius: BorderRadius.circular(10),
),
child: Row(
children: const [
Icon(Icons.info, color: Colors.blue),
SizedBox(width: 10),
Text(
"No credentials saved yet.",
style: TextStyle(color: Colors.black87),
),
],
),
);
}
Widget _tipsBox() {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFFE8FCFF),
borderRadius: BorderRadius.circular(10),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
"💡 Connection Tips",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
SizedBox(height: 10),
Text("• Ensure your credentials are valid and active."),
Text("• Credentials are encrypted before saving."),
Text("• Contact Turn14 support for API setup help."),
],
),
);
}
}

View File

@ -17,6 +17,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.7.1"
archive:
dependency: transitive
description:
name: archive
sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd"
url: "https://pub.dev"
source: hosted
version: "4.0.7"
args:
dependency: transitive
description:
@ -49,6 +57,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f"
url: "https://pub.dev"
source: hosted
version: "2.0.4"
cli_config:
dependency: transitive
description:
@ -57,6 +73,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.2.0"
cli_util:
dependency: transitive
description:
name: cli_util
sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c
url: "https://pub.dev"
source: hosted
version: "0.4.2"
clock:
dependency: transitive
description:
@ -214,6 +238,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.6.0"
flutter_launcher_icons:
dependency: "direct main"
description:
name: flutter_launcher_icons
sha256: "10f13781741a2e3972126fae08393d3c4e01fa4cd7473326b94b72cf594195e7"
url: "https://pub.dev"
source: hosted
version: "0.14.4"
flutter_lints:
dependency: "direct dev"
description:
@ -328,6 +360,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.1.2"
image:
dependency: transitive
description:
name: image
sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928"
url: "https://pub.dev"
source: hosted
version: "4.5.4"
io:
dependency: transitive
description:
@ -344,6 +384,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.6.7"
json_annotation:
dependency: transitive
description:
name: json_annotation
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
url: "https://pub.dev"
source: hosted
version: "4.9.0"
leak_tracker:
dependency: transitive
description:
@ -488,6 +536,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.0"
petitparser:
dependency: transitive
description:
name: petitparser
sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1"
url: "https://pub.dev"
source: hosted
version: "7.0.1"
platform:
dependency: transitive
description:
@ -512,6 +568,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.5.2"
posix:
dependency: transitive
description:
name: posix
sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61"
url: "https://pub.dev"
source: hosted
version: "6.0.3"
pub_semver:
dependency: transitive
description:
@ -733,6 +797,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.0"
xml:
dependency: transitive
description:
name: xml
sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025"
url: "https://pub.dev"
source: hosted
version: "6.6.1"
yaml:
dependency: transitive
description:

View File

@ -20,6 +20,7 @@ dependencies:
fluttertoast: ^9.0.0
flutter_secure_storage: ^9.2.4
youtube_player_flutter: ^9.1.3
flutter_launcher_icons: ^0.14.4
dev_dependencies:
flutter_test:
@ -48,4 +49,11 @@ flutter:
- asset: assets/fonts/Nunito-SemiBold.ttf
weight: 600
- asset: assets/fonts/Nunito-Bold.ttf
weight: 700
weight: 700
flutter_launcher_icons:
android: true
ios: true
image_path: "assets/auth/autos_transp.png"
adaptive_icon_background: "#FFFFFF"
adaptive_icon_foreground: "assets/auth/autos_transp.png"