forked from baron/baron-sso
feat: dynamic frontend tenant dropdown
This commit is contained in:
@@ -567,6 +567,7 @@ func main() {
|
||||
|
||||
// Signup Routes
|
||||
signup := auth.Group("/signup")
|
||||
signup.Get("/tenants", authHandler.GetActiveTenants)
|
||||
signup.Post("/check-email", authHandler.CheckEmail)
|
||||
signup.Post("/check-login-id", authHandler.CheckLoginID)
|
||||
signup.Post("/send-email-code", authHandler.SendSignupEmailCode)
|
||||
|
||||
@@ -463,6 +463,45 @@ func (h *AuthHandler) VerifySignupCode(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
var req domain.SignupRequest
|
||||
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 {
|
||||
final path = type == 'email' ? 'send-email-code' : 'send-sms-code';
|
||||
final url = Uri.parse('$_baseUrl/api/v1/auth/signup/$path');
|
||||
|
||||
@@ -54,6 +54,10 @@ class _SignupScreenState extends State<SignupScreen> {
|
||||
bool _isPasswordObscured = true;
|
||||
bool _isConfirmPasswordObscured = true;
|
||||
|
||||
// Dynamic Tenants
|
||||
List<Map<String, dynamic>> _tenants = [];
|
||||
final Map<String, String> _affiliateDomains = {};
|
||||
|
||||
// Inline Errors
|
||||
String? _emailError;
|
||||
String? _phoneError;
|
||||
@@ -66,20 +70,32 @@ class _SignupScreenState extends State<SignupScreen> {
|
||||
Timer? _phoneTimer;
|
||||
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
|
||||
void initState() {
|
||||
super.initState();
|
||||
_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 {
|
||||
@@ -1505,47 +1521,12 @@ class _SignupScreenState extends State<SignupScreen> {
|
||||
),
|
||||
border: const OutlineInputBorder(),
|
||||
),
|
||||
items: [
|
||||
DropdownMenuItem(
|
||||
value: 'HANMAC',
|
||||
child: Text(
|
||||
tr('domain.company.hanmac'),
|
||||
),
|
||||
),
|
||||
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'),
|
||||
),
|
||||
),
|
||||
],
|
||||
items: _tenants.map((t) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: t['slug'],
|
||||
child: Text(t['name'] ?? t['slug']),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: _isAffiliateEmail
|
||||
? null
|
||||
: (val) => setState(
|
||||
|
||||
Reference in New Issue
Block a user