forked from baron/baron-sso
feat: dynamic frontend tenant dropdown
This commit is contained in:
@@ -567,6 +567,7 @@ func main() {
|
|||||||
|
|
||||||
// Signup Routes
|
// Signup Routes
|
||||||
signup := auth.Group("/signup")
|
signup := auth.Group("/signup")
|
||||||
|
signup.Get("/tenants", authHandler.GetActiveTenants)
|
||||||
signup.Post("/check-email", authHandler.CheckEmail)
|
signup.Post("/check-email", authHandler.CheckEmail)
|
||||||
signup.Post("/check-login-id", authHandler.CheckLoginID)
|
signup.Post("/check-login-id", authHandler.CheckLoginID)
|
||||||
signup.Post("/send-email-code", authHandler.SendSignupEmailCode)
|
signup.Post("/send-email-code", authHandler.SendSignupEmailCode)
|
||||||
|
|||||||
@@ -463,6 +463,45 @@ func (h *AuthHandler) VerifySignupCode(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Signup - Finalize registration
|
// Signup - Finalize registration
|
||||||
|
func (h *AuthHandler) GetActiveTenants(c *fiber.Ctx) error {
|
||||||
|
if h.TenantService == nil {
|
||||||
|
return errorJSON(c, fiber.StatusServiceUnavailable, "Tenant service unavailable")
|
||||||
|
}
|
||||||
|
|
||||||
|
// List all tenants (we use a large limit for now to get all affiliates)
|
||||||
|
tenants, _, err := h.TenantService.ListTenants(c.Context(), 1000, 0, "")
|
||||||
|
if err != nil {
|
||||||
|
return errorJSON(c, fiber.StatusInternalServerError, "Failed to fetch tenants")
|
||||||
|
}
|
||||||
|
|
||||||
|
type tenantResp struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Domains []string `json:"domains"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []tenantResp
|
||||||
|
for _, t := range tenants {
|
||||||
|
if t.Status == domain.TenantStatusActive && (t.Type == domain.TenantTypeCompany || t.Type == domain.TenantTypeCompanyGroup) {
|
||||||
|
var domains []string
|
||||||
|
for _, d := range t.Domains {
|
||||||
|
domains = append(domains, d.Domain)
|
||||||
|
}
|
||||||
|
results = append(results, tenantResp{
|
||||||
|
ID: t.ID,
|
||||||
|
Name: t.Name,
|
||||||
|
Slug: t.Slug,
|
||||||
|
Type: t.Type,
|
||||||
|
Domains: domains,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(results)
|
||||||
|
}
|
||||||
|
|
||||||
func (h *AuthHandler) Signup(c *fiber.Ctx) error {
|
func (h *AuthHandler) Signup(c *fiber.Ctx) error {
|
||||||
var req domain.SignupRequest
|
var req domain.SignupRequest
|
||||||
if err := c.BodyParser(&req); err != nil {
|
if err := c.BodyParser(&req); err != nil {
|
||||||
|
|||||||
@@ -923,6 +923,17 @@ class AuthProxyService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<List<Map<String, dynamic>>> getActiveTenants() async {
|
||||||
|
final url = Uri.parse('$_baseUrl/api/v1/auth/signup/tenants');
|
||||||
|
final response = await http.get(url);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final List<dynamic> data = jsonDecode(response.body);
|
||||||
|
return data.cast<Map<String, dynamic>>();
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
static Future<void> sendSignupCode(String target, String type) async {
|
static Future<void> sendSignupCode(String target, String type) async {
|
||||||
final path = type == 'email' ? 'send-email-code' : 'send-sms-code';
|
final path = type == 'email' ? 'send-email-code' : 'send-sms-code';
|
||||||
final url = Uri.parse('$_baseUrl/api/v1/auth/signup/$path');
|
final url = Uri.parse('$_baseUrl/api/v1/auth/signup/$path');
|
||||||
|
|||||||
@@ -54,6 +54,10 @@ class _SignupScreenState extends State<SignupScreen> {
|
|||||||
bool _isPasswordObscured = true;
|
bool _isPasswordObscured = true;
|
||||||
bool _isConfirmPasswordObscured = true;
|
bool _isConfirmPasswordObscured = true;
|
||||||
|
|
||||||
|
// Dynamic Tenants
|
||||||
|
List<Map<String, dynamic>> _tenants = [];
|
||||||
|
final Map<String, String> _affiliateDomains = {};
|
||||||
|
|
||||||
// Inline Errors
|
// Inline Errors
|
||||||
String? _emailError;
|
String? _emailError;
|
||||||
String? _phoneError;
|
String? _phoneError;
|
||||||
@@ -66,20 +70,32 @@ class _SignupScreenState extends State<SignupScreen> {
|
|||||||
Timer? _phoneTimer;
|
Timer? _phoneTimer;
|
||||||
int _phoneSeconds = 0;
|
int _phoneSeconds = 0;
|
||||||
|
|
||||||
// 가족사 도메인 맵
|
|
||||||
final Map<String, String> _affiliateDomains = {
|
|
||||||
'hanmaceng.co.kr': 'HANMAC',
|
|
||||||
'samaneng.com': 'SAMAN',
|
|
||||||
'jangheon.co.kr': 'JANGHEON',
|
|
||||||
'hallasanup.com': 'HALLA',
|
|
||||||
'pre-cast.co.kr': 'PTC',
|
|
||||||
'baroncs.co.kr': 'BARON',
|
|
||||||
};
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_loadPolicy();
|
_loadPolicy();
|
||||||
|
_fetchTenants();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _fetchTenants() async {
|
||||||
|
try {
|
||||||
|
final tenants = await AuthProxyService.getActiveTenants();
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_tenants = tenants;
|
||||||
|
_affiliateDomains.clear();
|
||||||
|
for (var t in tenants) {
|
||||||
|
if (t['domains'] != null) {
|
||||||
|
for (var d in (t['domains'] as List)) {
|
||||||
|
_affiliateDomains[d.toString().toLowerCase()] = t['slug'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Failed to load tenants: $e');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadPolicy() async {
|
Future<void> _loadPolicy() async {
|
||||||
@@ -1505,47 +1521,12 @@ class _SignupScreenState extends State<SignupScreen> {
|
|||||||
),
|
),
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
items: [
|
items: _tenants.map((t) {
|
||||||
DropdownMenuItem(
|
return DropdownMenuItem<String>(
|
||||||
value: 'HANMAC',
|
value: t['slug'],
|
||||||
child: Text(
|
child: Text(t['name'] ?? t['slug']),
|
||||||
tr('domain.company.hanmac'),
|
);
|
||||||
),
|
}).toList(),
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: 'SAMAN',
|
|
||||||
child: Text(
|
|
||||||
tr('domain.company.saman'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: 'PTC',
|
|
||||||
child: Text(
|
|
||||||
tr(
|
|
||||||
'domain.company.ptc',
|
|
||||||
fallback: 'PTC',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: 'JANGHEON',
|
|
||||||
child: Text(
|
|
||||||
tr('domain.company.jangheon'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: 'BARON',
|
|
||||||
child: Text(
|
|
||||||
tr('domain.company.baron'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: 'HALLA',
|
|
||||||
child: Text(
|
|
||||||
tr('domain.company.halla'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
onChanged: _isAffiliateEmail
|
onChanged: _isAffiliateEmail
|
||||||
? null
|
? null
|
||||||
: (val) => setState(
|
: (val) => setState(
|
||||||
|
|||||||
Reference in New Issue
Block a user