forked from baron/baron-sso
232 lines
7.6 KiB
Dart
232 lines
7.6 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import '../../../../core/services/auth_proxy_service.dart';
|
|
|
|
class CreateUserScreen extends StatefulWidget {
|
|
const CreateUserScreen({super.key});
|
|
|
|
@override
|
|
State<CreateUserScreen> createState() => _CreateUserScreenState();
|
|
}
|
|
|
|
class _CreateUserScreenState extends State<CreateUserScreen> {
|
|
final _formKey = GlobalKey<FormState>();
|
|
final TextEditingController _loginIdController = TextEditingController();
|
|
final TextEditingController _emailController = TextEditingController();
|
|
final TextEditingController _phoneController = TextEditingController();
|
|
final TextEditingController _nameController = TextEditingController();
|
|
|
|
bool _isLoading = false;
|
|
bool _isAuthorized = false;
|
|
String? _verifiedAdminPassword;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
WidgetsBinding.instance.addPostFrameCallback((_) => _verifyAccess());
|
|
}
|
|
|
|
Future<void> _verifyAccess() async {
|
|
final passwordController = TextEditingController();
|
|
|
|
// Show blocking dialog
|
|
final String? inputPassword = await showDialog<String>(
|
|
context: context,
|
|
barrierDismissible: false, // User must enter password or leave
|
|
builder: (context) => AlertDialog(
|
|
title: const Text("Admin Authentication Required"),
|
|
content: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
const Text("Please enter the admin password to access this page."),
|
|
const SizedBox(height: 16),
|
|
TextField(
|
|
controller: passwordController,
|
|
obscureText: true,
|
|
decoration: const InputDecoration(
|
|
labelText: "Password",
|
|
border: OutlineInputBorder(),
|
|
),
|
|
autofocus: true,
|
|
onSubmitted: (value) => Navigator.pop(context, value),
|
|
),
|
|
],
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context, null), // Cancel
|
|
child: const Text("Cancel"),
|
|
),
|
|
FilledButton(
|
|
onPressed: () => Navigator.pop(context, passwordController.text),
|
|
child: const Text("Enter"),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
|
|
// If cancelled or empty
|
|
if (inputPassword == null || inputPassword.isEmpty) {
|
|
if (mounted) context.go('/dashboard'); // Kick out
|
|
return;
|
|
}
|
|
|
|
// Verify against Backend
|
|
setState(() => _isLoading = true);
|
|
final isValid = await AuthProxyService.checkAdminAuth(inputPassword);
|
|
setState(() => _isLoading = false);
|
|
|
|
if (isValid) {
|
|
if (mounted) {
|
|
setState(() {
|
|
_isAuthorized = true;
|
|
_verifiedAdminPassword = inputPassword;
|
|
});
|
|
}
|
|
} else {
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text('Invalid Password. Access Denied.'), backgroundColor: Colors.red),
|
|
);
|
|
context.go('/dashboard'); // Kick out
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_loginIdController.dispose();
|
|
_emailController.dispose();
|
|
_phoneController.dispose();
|
|
_nameController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
Future<void> _submit() async {
|
|
if (!_formKey.currentState!.validate()) return;
|
|
if (_verifiedAdminPassword == null) return; // Should not happen
|
|
|
|
setState(() => _isLoading = true);
|
|
|
|
try {
|
|
await AuthProxyService.createUser(
|
|
loginId: _loginIdController.text.trim(),
|
|
adminPassword: _verifiedAdminPassword!,
|
|
email: _emailController.text.trim().isEmpty ? null : _emailController.text.trim(),
|
|
phone: _phoneController.text.trim().isEmpty ? null : _phoneController.text.trim(),
|
|
displayName: _nameController.text.trim().isEmpty ? null : _nameController.text.trim(),
|
|
);
|
|
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text('User created successfully!'), backgroundColor: Colors.green),
|
|
);
|
|
_formKey.currentState!.reset();
|
|
_loginIdController.clear();
|
|
_emailController.clear();
|
|
_phoneController.clear();
|
|
_nameController.clear();
|
|
}
|
|
} catch (e) {
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text('Error: $e'), backgroundColor: Colors.red),
|
|
);
|
|
}
|
|
} finally {
|
|
if (mounted) setState(() => _isLoading = false);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
// Hide content until authorized
|
|
if (!_isAuthorized) {
|
|
return const Scaffold(
|
|
body: Center(child: CircularProgressIndicator()),
|
|
);
|
|
}
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text('Create User'),
|
|
leading: IconButton(
|
|
icon: const Icon(Icons.arrow_back),
|
|
onPressed: () => context.go('/dashboard'),
|
|
),
|
|
),
|
|
body: Center(
|
|
child: Container(
|
|
constraints: const BoxConstraints(maxWidth: 600),
|
|
padding: const EdgeInsets.all(24),
|
|
child: Form(
|
|
key: _formKey,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
const Text(
|
|
"Create New User",
|
|
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
const SizedBox(height: 32),
|
|
|
|
TextFormField(
|
|
controller: _loginIdController,
|
|
decoration: const InputDecoration(
|
|
labelText: "Login ID (Required)",
|
|
border: OutlineInputBorder(),
|
|
helperText: "Unique identifier (Email or Phone)",
|
|
),
|
|
validator: (value) => value == null || value.isEmpty ? 'Please enter Login ID' : null,
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
TextFormField(
|
|
controller: _nameController,
|
|
decoration: const InputDecoration(
|
|
labelText: "Display Name",
|
|
border: OutlineInputBorder(),
|
|
prefixIcon: Icon(Icons.person),
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
TextFormField(
|
|
controller: _emailController,
|
|
decoration: const InputDecoration(
|
|
labelText: "Email",
|
|
border: OutlineInputBorder(),
|
|
prefixIcon: Icon(Icons.email),
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
TextFormField(
|
|
controller: _phoneController,
|
|
decoration: const InputDecoration(
|
|
labelText: "Phone Number",
|
|
border: OutlineInputBorder(),
|
|
prefixIcon: Icon(Icons.phone),
|
|
helperText: "Start with 010 (e.g., 010-1234-5678)",
|
|
),
|
|
),
|
|
const SizedBox(height: 32),
|
|
|
|
FilledButton(
|
|
onPressed: _isLoading ? null : _submit,
|
|
style: FilledButton.styleFrom(
|
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
child: _isLoading
|
|
? const CircularProgressIndicator(color: Colors.white)
|
|
: const Text("Create User"),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
} |