forked from baron/baron-sso
dev 반영 code check 오류 수정
This commit is contained in:
@@ -641,7 +641,12 @@ export function buildWorksmobilePasswordManageUrl({
|
|||||||
}) {
|
}) {
|
||||||
const normalizedTenantId = tenantId?.trim();
|
const normalizedTenantId = tenantId?.trim();
|
||||||
const normalizedUserIdNo = userIdNo?.trim();
|
const normalizedUserIdNo = userIdNo?.trim();
|
||||||
if (!normalizedTenantId || !domainId || domainId <= 0 || !normalizedUserIdNo) {
|
if (
|
||||||
|
!normalizedTenantId ||
|
||||||
|
!domainId ||
|
||||||
|
domainId <= 0 ||
|
||||||
|
!normalizedUserIdNo
|
||||||
|
) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
const url = new URL("https://auth.worksmobile.com/integrate/password/manage");
|
const url = new URL("https://auth.worksmobile.com/integrate/password/manage");
|
||||||
@@ -790,10 +795,7 @@ function ComparisonTable({
|
|||||||
.filter(canSelectWorksmobileRow)
|
.filter(canSelectWorksmobileRow)
|
||||||
.map(getWorksmobileRowSelectionKey)
|
.map(getWorksmobileRowSelectionKey)
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
const selectedActionIds = getWorksmobileSelectedActionIds(
|
const selectedActionIds = getWorksmobileSelectedActionIds(rows, selectedKeys);
|
||||||
rows,
|
|
||||||
selectedKeys,
|
|
||||||
);
|
|
||||||
const allSelectableSelected =
|
const allSelectableSelected =
|
||||||
selectableKeys.length > 0 &&
|
selectableKeys.length > 0 &&
|
||||||
selectableKeys.every((key) => selectedKeys.includes(key));
|
selectableKeys.every((key) => selectedKeys.includes(key));
|
||||||
|
|||||||
@@ -462,7 +462,8 @@ test.describe("User Management", () => {
|
|||||||
"John Doe john@test.com 010-1111-2222",
|
"John Doe john@test.com 010-1111-2222",
|
||||||
);
|
);
|
||||||
|
|
||||||
await page.getByTestId("user-status-toggle-u-1").click();
|
await page.getByTestId("user-status-select-u-1").click();
|
||||||
|
await page.getByRole("option", { name: /비활성|inactive/i }).click();
|
||||||
await expect
|
await expect
|
||||||
.poll(() => updatePayload)
|
.poll(() => updatePayload)
|
||||||
.toMatchObject({ status: "inactive" });
|
.toMatchObject({ status: "inactive" });
|
||||||
@@ -816,22 +817,27 @@ test.describe("User Management", () => {
|
|||||||
(form as HTMLFormElement).requestSubmit();
|
(form as HTMLFormElement).requestSubmit();
|
||||||
});
|
});
|
||||||
|
|
||||||
await expect.poll(() => updatePayload).toMatchObject({
|
await expect
|
||||||
tenantSlug: "hanmac-team",
|
.poll(() => updatePayload)
|
||||||
primaryTenantId: "hanmac-team-id",
|
.toMatchObject({
|
||||||
primaryTenantName: "한맥팀",
|
tenantSlug: "hanmac-team",
|
||||||
primaryTenantIsOwner: true,
|
|
||||||
metadata: {
|
|
||||||
primaryTenantId: "hanmac-team-id",
|
primaryTenantId: "hanmac-team-id",
|
||||||
primaryTenantName: "한맥팀",
|
primaryTenantName: "한맥팀",
|
||||||
primaryTenantSlug: "hanmac-team",
|
|
||||||
primaryTenantIsOwner: true,
|
primaryTenantIsOwner: true,
|
||||||
additionalAppointments: [
|
metadata: {
|
||||||
{ tenantId: "03dbe16b-e47b-4f72-927b-782807d67a35", isPrimary: false },
|
primaryTenantId: "hanmac-team-id",
|
||||||
{ tenantId: "hanmac-team-id", isPrimary: true },
|
primaryTenantName: "한맥팀",
|
||||||
],
|
primaryTenantSlug: "hanmac-team",
|
||||||
},
|
primaryTenantIsOwner: true,
|
||||||
});
|
additionalAppointments: [
|
||||||
|
{
|
||||||
|
tenantId: "03dbe16b-e47b-4f72-927b-782807d67a35",
|
||||||
|
isPrimary: false,
|
||||||
|
},
|
||||||
|
{ tenantId: "hanmac-team-id", isPrimary: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should show conflict error when creating with an existing Login ID", async ({
|
test("should show conflict error when creating with an existing Login ID", async ({
|
||||||
|
|||||||
@@ -211,11 +211,9 @@ test.describe("Worksmobile tenant management", () => {
|
|||||||
name: /바론에만 있음|웍스에만 있음|양쪽 다 있음/,
|
name: /바론에만 있음|웍스에만 있음|양쪽 다 있음/,
|
||||||
})
|
})
|
||||||
.allTextContents();
|
.allTextContents();
|
||||||
await expect.poll(() => filterButtons).toEqual([
|
await expect
|
||||||
"바론에만 있음",
|
.poll(() => filterButtons)
|
||||||
"웍스에만 있음",
|
.toEqual(["바론에만 있음", "웍스에만 있음", "양쪽 다 있음"]);
|
||||||
"양쪽 다 있음",
|
|
||||||
]);
|
|
||||||
|
|
||||||
await page.getByRole("button", { name: "웍스에만 있음" }).click();
|
await page.getByRole("button", { name: "웍스에만 있음" }).click();
|
||||||
await expect(page.getByText("박웍스")).not.toBeVisible();
|
await expect(page.getByText("박웍스")).not.toBeVisible();
|
||||||
@@ -481,16 +479,17 @@ test.describe("Worksmobile tenant management", () => {
|
|||||||
Math.max(pageOverflow.documentScrollWidth, pageOverflow.bodyScrollWidth),
|
Math.max(pageOverflow.documentScrollWidth, pageOverflow.bodyScrollWidth),
|
||||||
).toBeLessThanOrEqual(pageOverflow.viewportWidth + 1);
|
).toBeLessThanOrEqual(pageOverflow.viewportWidth + 1);
|
||||||
|
|
||||||
const userTableScroll = await page.locator("table").first().evaluate(
|
const userTableScroll = await page
|
||||||
(table) => {
|
.locator("table")
|
||||||
|
.first()
|
||||||
|
.evaluate((table) => {
|
||||||
const container = table.parentElement?.parentElement as HTMLElement;
|
const container = table.parentElement?.parentElement as HTMLElement;
|
||||||
return {
|
return {
|
||||||
clientWidth: container.clientWidth,
|
clientWidth: container.clientWidth,
|
||||||
overflowX: window.getComputedStyle(container).overflowX,
|
overflowX: window.getComputedStyle(container).overflowX,
|
||||||
scrollWidth: container.scrollWidth,
|
scrollWidth: container.scrollWidth,
|
||||||
};
|
};
|
||||||
},
|
});
|
||||||
);
|
|
||||||
expect(userTableScroll.overflowX).toBe("auto");
|
expect(userTableScroll.overflowX).toBe("auto");
|
||||||
expect(userTableScroll.scrollWidth).toBeGreaterThan(
|
expect(userTableScroll.scrollWidth).toBeGreaterThan(
|
||||||
userTableScroll.clientWidth,
|
userTableScroll.clientWidth,
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ func TestEnsureSuperAdminCreatesIdentityLocalUserAndKetoRelation(t *testing.T) {
|
|||||||
Name: "New Admin",
|
Name: "New Admin",
|
||||||
Source: "test",
|
Source: "test",
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("EnsureSuperAdmin returned error: %v", err)
|
t.Fatalf("EnsureSuperAdmin returned error: %v", err)
|
||||||
}
|
}
|
||||||
@@ -67,7 +66,6 @@ func TestEnsureSuperAdminPromotesExistingLocalUser(t *testing.T) {
|
|||||||
Name: "Existing Admin",
|
Name: "Existing Admin",
|
||||||
Source: "test",
|
Source: "test",
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("EnsureSuperAdmin returned error: %v", err)
|
t.Fatalf("EnsureSuperAdmin returned error: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,11 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultWorksmobileAPIBaseURL = "https://www.worksapis.com"
|
const (
|
||||||
const defaultWorksmobileOAuthTokenURL = "https://auth.worksmobile.com/oauth2/v2.0/token"
|
defaultWorksmobileAPIBaseURL = "https://www.worksapis.com"
|
||||||
const defaultWorksmobileOAuthScope = "directory"
|
defaultWorksmobileOAuthTokenURL = "https://auth.worksmobile.com/oauth2/v2.0/token"
|
||||||
|
defaultWorksmobileOAuthScope = "directory"
|
||||||
|
)
|
||||||
|
|
||||||
type WorksmobileDirectoryClient interface {
|
type WorksmobileDirectoryClient interface {
|
||||||
CreateOrgUnit(ctx context.Context, payload WorksmobileOrgUnitPayload) error
|
CreateOrgUnit(ctx context.Context, payload WorksmobileOrgUnitPayload) error
|
||||||
|
|||||||
@@ -363,32 +363,41 @@ func (f *fakeWorksmobileUserRepo) Update(ctx context.Context, user *domain.User)
|
|||||||
func (f *fakeWorksmobileUserRepo) FindByEmail(ctx context.Context, email string) (*domain.User, error) {
|
func (f *fakeWorksmobileUserRepo) FindByEmail(ctx context.Context, email string) (*domain.User, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeWorksmobileUserRepo) FindByID(ctx context.Context, id string) (*domain.User, error) {
|
func (f *fakeWorksmobileUserRepo) FindByID(ctx context.Context, id string) (*domain.User, error) {
|
||||||
user := f.byID[id]
|
user := f.byID[id]
|
||||||
return &user, nil
|
return &user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeWorksmobileUserRepo) FindByIDs(ctx context.Context, ids []string) ([]domain.User, error) {
|
func (f *fakeWorksmobileUserRepo) FindByIDs(ctx context.Context, ids []string) ([]domain.User, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeWorksmobileUserRepo) ListByTenant(ctx context.Context, tenantID string) ([]domain.User, error) {
|
func (f *fakeWorksmobileUserRepo) ListByTenant(ctx context.Context, tenantID string) ([]domain.User, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeWorksmobileUserRepo) List(ctx context.Context, offset, limit int, search string, companyCode string) ([]domain.User, int64, error) {
|
func (f *fakeWorksmobileUserRepo) List(ctx context.Context, offset, limit int, search string, companyCode string) ([]domain.User, int64, error) {
|
||||||
return nil, 0, nil
|
return nil, 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeWorksmobileUserRepo) CountByTenant(ctx context.Context, tenantID string) (int64, error) {
|
func (f *fakeWorksmobileUserRepo) CountByTenant(ctx context.Context, tenantID string) (int64, error) {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeWorksmobileUserRepo) CountByTenantIDs(ctx context.Context, tenantIDs []string) (map[string]int64, error) {
|
func (f *fakeWorksmobileUserRepo) CountByTenantIDs(ctx context.Context, tenantIDs []string) (map[string]int64, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeWorksmobileUserRepo) CountByCompanyCodes(ctx context.Context, codes []string) (map[string]int64, error) {
|
func (f *fakeWorksmobileUserRepo) CountByCompanyCodes(ctx context.Context, codes []string) (map[string]int64, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeWorksmobileUserRepo) FindByTenantIDs(ctx context.Context, tenantIDs []string) ([]domain.User, error) {
|
func (f *fakeWorksmobileUserRepo) FindByTenantIDs(ctx context.Context, tenantIDs []string) ([]domain.User, error) {
|
||||||
f.requestedTenantIDs = append([]string(nil), tenantIDs...)
|
f.requestedTenantIDs = append([]string(nil), tenantIDs...)
|
||||||
return f.byTenant, nil
|
return f.byTenant, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeWorksmobileUserRepo) FindByCompanyCodes(ctx context.Context, codes []string) ([]domain.User, error) {
|
func (f *fakeWorksmobileUserRepo) FindByCompanyCodes(ctx context.Context, codes []string) ([]domain.User, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -396,12 +405,15 @@ func (f *fakeWorksmobileUserRepo) Delete(ctx context.Context, id string) error {
|
|||||||
func (f *fakeWorksmobileUserRepo) UpdateUserLoginIDs(ctx context.Context, userID string, loginIDs []domain.UserLoginID) error {
|
func (f *fakeWorksmobileUserRepo) UpdateUserLoginIDs(ctx context.Context, userID string, loginIDs []domain.UserLoginID) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeWorksmobileUserRepo) GetUserLoginIDs(ctx context.Context, userID string) ([]domain.UserLoginID, error) {
|
func (f *fakeWorksmobileUserRepo) GetUserLoginIDs(ctx context.Context, userID string) ([]domain.UserLoginID, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeWorksmobileUserRepo) IsLoginIDTaken(ctx context.Context, loginID string) (bool, error) {
|
func (f *fakeWorksmobileUserRepo) IsLoginIDTaken(ctx context.Context, loginID string) (bool, error) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeWorksmobileUserRepo) FindTenantIDByLoginID(ctx context.Context, loginID string) (string, error) {
|
func (f *fakeWorksmobileUserRepo) FindTenantIDByLoginID(ctx context.Context, loginID string) (string, error) {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -451,7 +451,10 @@ function AppLayout() {
|
|||||||
className="inline-flex items-center gap-3 rounded-full border border-border bg-card px-3 py-2 transition hover:bg-muted/20"
|
className="inline-flex items-center gap-3 rounded-full border border-border bg-card px-3 py-2 transition hover:bg-muted/20"
|
||||||
aria-haspopup="menu"
|
aria-haspopup="menu"
|
||||||
aria-expanded={isProfileMenuOpen}
|
aria-expanded={isProfileMenuOpen}
|
||||||
aria-label={t("ui.dev.profile.menu_aria", "Open account menu")}
|
aria-label={t(
|
||||||
|
"ui.dev.profile.menu_aria",
|
||||||
|
"Open account menu",
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<div className="grid h-8 w-8 place-items-center rounded-full bg-primary/15 text-xs font-semibold text-primary">
|
<div className="grid h-8 w-8 place-items-center rounded-full bg-primary/15 text-xs font-semibold text-primary">
|
||||||
{profileInitial}
|
{profileInitial}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ saman = "Saman"
|
|||||||
[domain.tenant_type]
|
[domain.tenant_type]
|
||||||
company = "Company"
|
company = "Company"
|
||||||
company_group = "Company Group"
|
company_group = "Company Group"
|
||||||
|
organization = "Organization"
|
||||||
personal = "Personal"
|
personal = "Personal"
|
||||||
user_group = "User Group"
|
user_group = "User Group"
|
||||||
|
|
||||||
@@ -1166,8 +1167,26 @@ tab_organization = "Organization Manage"
|
|||||||
tab_permissions = "Permissions"
|
tab_permissions = "Permissions"
|
||||||
tab_profile = "Profile"
|
tab_profile = "Profile"
|
||||||
tab_schema = "Tab Schema"
|
tab_schema = "Tab Schema"
|
||||||
|
tab_worksmobile = "Worksmobile"
|
||||||
title = "Details"
|
title = "Details"
|
||||||
|
|
||||||
|
[ui.admin.tenants.worksmobile]
|
||||||
|
compare = "Baron / Works Comparison"
|
||||||
|
compare_description = "Users show entries that exist only in Baron or only in WORKS by default."
|
||||||
|
compare_groups = "Organizations / Groups"
|
||||||
|
compare_users = "Users"
|
||||||
|
dry_run = "Backfill Dry-run"
|
||||||
|
forbidden = "You do not have permission to manage the Worksmobile integration."
|
||||||
|
initial_password_csv = "Initial Password CSV"
|
||||||
|
recent_jobs = "Recent Jobs"
|
||||||
|
refresh = "Refresh"
|
||||||
|
single_sync = "Single-item Sync"
|
||||||
|
single_sync_description = "Create an organization or user sync job using a Baron UUID."
|
||||||
|
subtitle = "Review Hanmac Family Directory sync status for organizations and users, and retry failed jobs."
|
||||||
|
sync_orgunit = "Organization Sync"
|
||||||
|
sync_user = "User Sync"
|
||||||
|
title = "Worksmobile Integration"
|
||||||
|
|
||||||
[ui.admin.tenants.list]
|
[ui.admin.tenants.list]
|
||||||
search_placeholder = "Search tenant by name or slug..."
|
search_placeholder = "Search tenant by name or slug..."
|
||||||
select_placeholder = "Select a tenant"
|
select_placeholder = "Select a tenant"
|
||||||
@@ -1613,6 +1632,7 @@ bulk_import = "Bulk Import"
|
|||||||
empty = "No users found."
|
empty = "No users found."
|
||||||
fetch_error = "Failed to load the user list."
|
fetch_error = "Failed to load the user list."
|
||||||
search_placeholder = "Search Placeholder"
|
search_placeholder = "Search Placeholder"
|
||||||
|
status_select = "{{name}} status"
|
||||||
subtitle = "Browse and manage registered users."
|
subtitle = "Browse and manage registered users."
|
||||||
toggle_status = "{{name}} active status"
|
toggle_status = "{{name}} active status"
|
||||||
title = "User Manage"
|
title = "User Manage"
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ saman = "삼안"
|
|||||||
[domain.tenant_type]
|
[domain.tenant_type]
|
||||||
company = "COMPANY (일반 기업)"
|
company = "COMPANY (일반 기업)"
|
||||||
company_group = "COMPANY_GROUP (그룹사/지주사)"
|
company_group = "COMPANY_GROUP (그룹사/지주사)"
|
||||||
|
organization = "ORGANIZATION (정규 조직)"
|
||||||
personal = "PERSONAL (개인 워크스페이스)"
|
personal = "PERSONAL (개인 워크스페이스)"
|
||||||
user_group = "USER_GROUP (내부 부서/팀)"
|
user_group = "USER_GROUP (내부 부서/팀)"
|
||||||
|
|
||||||
@@ -1626,8 +1627,26 @@ tab_organization = "조직 관리"
|
|||||||
tab_permissions = "권한"
|
tab_permissions = "권한"
|
||||||
tab_profile = "프로필"
|
tab_profile = "프로필"
|
||||||
tab_schema = "사용자 스키마"
|
tab_schema = "사용자 스키마"
|
||||||
|
tab_worksmobile = "Worksmobile"
|
||||||
title = "상세"
|
title = "상세"
|
||||||
|
|
||||||
|
[ui.admin.tenants.worksmobile]
|
||||||
|
compare = "Baron / Works 비교"
|
||||||
|
compare_description = "구성원은 기본적으로 Baron 또는 WORKS 한쪽에만 있는 항목을 보여줍니다."
|
||||||
|
compare_groups = "조직/그룹"
|
||||||
|
compare_users = "구성원"
|
||||||
|
dry_run = "Backfill Dry-run"
|
||||||
|
forbidden = "Worksmobile 연동 권한이 없습니다."
|
||||||
|
initial_password_csv = "초기 비밀번호 CSV"
|
||||||
|
recent_jobs = "최근 작업"
|
||||||
|
refresh = "새로고침"
|
||||||
|
single_sync = "단건 동기화"
|
||||||
|
single_sync_description = "Baron UUID 기준으로 조직 또는 구성원 sync 작업을 생성합니다."
|
||||||
|
subtitle = "한맥가족 Directory 조직/구성원 동기화 상태를 확인하고 실패 작업을 재시도합니다."
|
||||||
|
sync_orgunit = "조직 Sync"
|
||||||
|
sync_user = "구성원 Sync"
|
||||||
|
title = "Worksmobile 연동"
|
||||||
|
|
||||||
[ui.admin.tenants.list]
|
[ui.admin.tenants.list]
|
||||||
search_placeholder = "테넌트 이름 또는 슬러그 검색..."
|
search_placeholder = "테넌트 이름 또는 슬러그 검색..."
|
||||||
select_placeholder = "테넌트를 선택하세요"
|
select_placeholder = "테넌트를 선택하세요"
|
||||||
@@ -2075,6 +2094,7 @@ bulk_import = "일괄 임포트"
|
|||||||
empty = "검색 결과가 없습니다."
|
empty = "검색 결과가 없습니다."
|
||||||
fetch_error = "사용자 목록 조회에 실패했습니다."
|
fetch_error = "사용자 목록 조회에 실패했습니다."
|
||||||
search_placeholder = "이름 또는 이메일 검색..."
|
search_placeholder = "이름 또는 이메일 검색..."
|
||||||
|
status_select = "{{name}} 상태"
|
||||||
subtitle = "시스템 사용자를 조회하고 관리합니다."
|
subtitle = "시스템 사용자를 조회하고 관리합니다."
|
||||||
toggle_status = "{{name}} 활성 상태"
|
toggle_status = "{{name}} 활성 상태"
|
||||||
title = "사용자 관리"
|
title = "사용자 관리"
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ saman = ""
|
|||||||
[domain.tenant_type]
|
[domain.tenant_type]
|
||||||
company = ""
|
company = ""
|
||||||
company_group = ""
|
company_group = ""
|
||||||
|
organization = ""
|
||||||
personal = ""
|
personal = ""
|
||||||
user_group = ""
|
user_group = ""
|
||||||
|
|
||||||
@@ -1495,6 +1496,24 @@ tab_organization = ""
|
|||||||
tab_permissions = ""
|
tab_permissions = ""
|
||||||
tab_profile = ""
|
tab_profile = ""
|
||||||
tab_schema = ""
|
tab_schema = ""
|
||||||
|
tab_worksmobile = ""
|
||||||
|
title = ""
|
||||||
|
|
||||||
|
[ui.admin.tenants.worksmobile]
|
||||||
|
compare = ""
|
||||||
|
compare_description = ""
|
||||||
|
compare_groups = ""
|
||||||
|
compare_users = ""
|
||||||
|
dry_run = ""
|
||||||
|
forbidden = ""
|
||||||
|
initial_password_csv = ""
|
||||||
|
recent_jobs = ""
|
||||||
|
refresh = ""
|
||||||
|
single_sync = ""
|
||||||
|
single_sync_description = ""
|
||||||
|
subtitle = ""
|
||||||
|
sync_orgunit = ""
|
||||||
|
sync_user = ""
|
||||||
title = ""
|
title = ""
|
||||||
|
|
||||||
[ui.admin.tenants.list]
|
[ui.admin.tenants.list]
|
||||||
@@ -1950,6 +1969,7 @@ bulk_import = ""
|
|||||||
empty = ""
|
empty = ""
|
||||||
fetch_error = ""
|
fetch_error = ""
|
||||||
search_placeholder = ""
|
search_placeholder = ""
|
||||||
|
status_select = ""
|
||||||
subtitle = ""
|
subtitle = ""
|
||||||
toggle_status = ""
|
toggle_status = ""
|
||||||
title = ""
|
title = ""
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ saman = "Saman"
|
|||||||
[domain.tenant_type]
|
[domain.tenant_type]
|
||||||
company = "Company"
|
company = "Company"
|
||||||
company_group = "Company Group"
|
company_group = "Company Group"
|
||||||
|
organization = "Organization"
|
||||||
personal = "Personal"
|
personal = "Personal"
|
||||||
user_group = "User Group"
|
user_group = "User Group"
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ saman = "삼안"
|
|||||||
[domain.tenant_type]
|
[domain.tenant_type]
|
||||||
company = "COMPANY (일반 기업)"
|
company = "COMPANY (일반 기업)"
|
||||||
company_group = "COMPANY_GROUP (그룹사/지주사)"
|
company_group = "COMPANY_GROUP (그룹사/지주사)"
|
||||||
|
organization = "ORGANIZATION (정규 조직)"
|
||||||
personal = "PERSONAL (개인 워크스페이스)"
|
personal = "PERSONAL (개인 워크스페이스)"
|
||||||
user_group = "USER_GROUP (내부 부서/팀)"
|
user_group = "USER_GROUP (내부 부서/팀)"
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ saman = ""
|
|||||||
[domain.tenant_type]
|
[domain.tenant_type]
|
||||||
company = ""
|
company = ""
|
||||||
company_group = ""
|
company_group = ""
|
||||||
|
organization = ""
|
||||||
personal = ""
|
personal = ""
|
||||||
user_group = ""
|
user_group = ""
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
const Map<String, String> internalErrorWhitelistMessageKeys = {
|
const Map<String, String> internalErrorWhitelistMessageKeys = {
|
||||||
'settings_disabled': 'msg.userfront.error.whitelist.settings_disabled',
|
'settings_disabled': 'msg.userfront.error.whitelist.settings_disabled',
|
||||||
'invalid_session': 'msg.userfront.error.whitelist.invalid_session',
|
'invalid_session': 'msg.userfront.error.whitelist.invalid_session',
|
||||||
'verification_required': 'msg.userfront.error.whitelist.verification_required',
|
'verification_required':
|
||||||
|
'msg.userfront.error.whitelist.verification_required',
|
||||||
'recovery_expired': 'msg.userfront.error.whitelist.recovery_expired',
|
'recovery_expired': 'msg.userfront.error.whitelist.recovery_expired',
|
||||||
'recovery_invalid': 'msg.userfront.error.whitelist.recovery_invalid',
|
'recovery_invalid': 'msg.userfront.error.whitelist.recovery_invalid',
|
||||||
'rate_limited': 'msg.userfront.error.whitelist.rate_limited',
|
'rate_limited': 'msg.userfront.error.whitelist.rate_limited',
|
||||||
|
|||||||
@@ -332,14 +332,18 @@ class _ErrorScreenState extends State<ErrorScreen> {
|
|||||||
final showTenantLookupFallback =
|
final showTenantLookupFallback =
|
||||||
_tenantAccessDetails == null &&
|
_tenantAccessDetails == null &&
|
||||||
(emailLabel.isEmpty || tenantLabel.isEmpty);
|
(emailLabel.isEmpty || tenantLabel.isEmpty);
|
||||||
|
final internalWhitelistDetail = internalWhitelistKey == null
|
||||||
|
? null
|
||||||
|
: tr(internalWhitelistKey);
|
||||||
final detail = isTenantAccessBlocked
|
final detail = isTenantAccessBlocked
|
||||||
? tr(
|
? tr(
|
||||||
'msg.userfront.error.tenant.detail',
|
'msg.userfront.error.tenant.detail',
|
||||||
fallback: 'The current signed-in account cannot access this application.',
|
fallback:
|
||||||
|
'The current signed-in account cannot access this application.',
|
||||||
)
|
)
|
||||||
: isProd
|
: isProd
|
||||||
? (isInternalWhitelisted
|
? (isInternalWhitelisted
|
||||||
? tr(internalWhitelistKey!)
|
? internalWhitelistDetail!
|
||||||
: (isOryBypass
|
: (isOryBypass
|
||||||
? tr(
|
? tr(
|
||||||
'msg.userfront.error.ory.$normalizedCode',
|
'msg.userfront.error.ory.$normalizedCode',
|
||||||
@@ -444,7 +448,8 @@ class _ErrorScreenState extends State<ErrorScreen> {
|
|||||||
child: Text(
|
child: Text(
|
||||||
tr(
|
tr(
|
||||||
'msg.userfront.error.tenant.loading',
|
'msg.userfront.error.tenant.loading',
|
||||||
fallback: 'Loading the current account details.',
|
fallback:
|
||||||
|
'Loading the current account details.',
|
||||||
),
|
),
|
||||||
style: theme.textTheme.bodySmall
|
style: theme.textTheme.bodySmall
|
||||||
?.copyWith(
|
?.copyWith(
|
||||||
|
|||||||
@@ -846,8 +846,7 @@ class _SignupScreenState extends State<SignupScreen> {
|
|||||||
final preferredLocaleCode = resolvePreferredLocaleCode();
|
final preferredLocaleCode = resolvePreferredLocaleCode();
|
||||||
final useEnglishFallback =
|
final useEnglishFallback =
|
||||||
preferredLocaleCode.startsWith('en') && englishFallback != null;
|
preferredLocaleCode.startsWith('en') && englishFallback != null;
|
||||||
if (
|
if (localized.isEmpty ||
|
||||||
localized.isEmpty ||
|
|
||||||
placeholders.contains(localized) ||
|
placeholders.contains(localized) ||
|
||||||
hasCorruptedEscapes) {
|
hasCorruptedEscapes) {
|
||||||
return useEnglishFallback ? englishFallback : fallback;
|
return useEnglishFallback ? englishFallback : fallback;
|
||||||
|
|||||||
@@ -35,40 +35,26 @@ const Map<String, String> koStrings = {
|
|||||||
"err.userfront.auth_proxy.linked_app_revoke": "연동 해지에 실패했습니다.",
|
"err.userfront.auth_proxy.linked_app_revoke": "연동 해지에 실패했습니다.",
|
||||||
"err.userfront.auth_proxy.login_failed": "로그인에 실패했습니다.",
|
"err.userfront.auth_proxy.login_failed": "로그인에 실패했습니다.",
|
||||||
"err.userfront.auth_proxy.login_init": "로그인 초기화에 실패했습니다: {{error}}",
|
"err.userfront.auth_proxy.login_init": "로그인 초기화에 실패했습니다: {{error}}",
|
||||||
"err.userfront.auth_proxy.login_poll":
|
"err.userfront.auth_proxy.login_poll": "로그인 상태 확인에 실패했습니다: {{error}}",
|
||||||
"로그인 상태 확인에 실패했습니다: {{error}}",
|
|
||||||
"err.userfront.auth_proxy.oidc_accept": "OIDC 로그인 승인에 실패했습니다.",
|
"err.userfront.auth_proxy.oidc_accept": "OIDC 로그인 승인에 실패했습니다.",
|
||||||
"err.userfront.auth_proxy.password_reset_complete": "비밀번호 재설정에 실패했습니다.",
|
"err.userfront.auth_proxy.password_reset_complete": "비밀번호 재설정에 실패했습니다.",
|
||||||
"err.userfront.auth_proxy.password_policy_fetch":
|
"err.userfront.auth_proxy.password_policy_fetch": "비밀번호 정책을 불러오지 못했습니다.",
|
||||||
"비밀번호 정책을 불러오지 못했습니다.",
|
|
||||||
"err.userfront.auth_proxy.password_reset_init": "비밀번호 재설정을 시작하지 못했습니다.",
|
"err.userfront.auth_proxy.password_reset_init": "비밀번호 재설정을 시작하지 못했습니다.",
|
||||||
"err.userfront.auth_proxy.profile_load":
|
"err.userfront.auth_proxy.profile_load": "프로필을 불러오지 못했습니다: {{error}}",
|
||||||
"프로필을 불러오지 못했습니다: {{error}}",
|
"err.userfront.auth_proxy.tenant_info_fetch": "테넌트 정보를 불러오지 못했습니다.",
|
||||||
"err.userfront.auth_proxy.tenant_info_fetch":
|
|
||||||
"테넌트 정보를 불러오지 못했습니다.",
|
|
||||||
"err.userfront.auth_proxy.verify_failed": "검증에 실패했습니다: {{error}}",
|
"err.userfront.auth_proxy.verify_failed": "검증에 실패했습니다: {{error}}",
|
||||||
"err.userfront.auth_proxy.sms_send": "SMS 전송에 실패했습니다: {{error}}",
|
"err.userfront.auth_proxy.sms_send": "SMS 전송에 실패했습니다: {{error}}",
|
||||||
"err.userfront.auth_proxy.code_verify":
|
"err.userfront.auth_proxy.code_verify": "인증 코드 확인에 실패했습니다: {{error}}",
|
||||||
"인증 코드 확인에 실패했습니다: {{error}}",
|
"err.userfront.auth_proxy.qr_init": "QR 로그인을 시작하지 못했습니다: {{error}}",
|
||||||
"err.userfront.auth_proxy.qr_init":
|
"err.userfront.auth_proxy.qr_poll": "QR 상태 확인에 실패했습니다: {{error}}",
|
||||||
"QR 로그인을 시작하지 못했습니다: {{error}}",
|
|
||||||
"err.userfront.auth_proxy.qr_poll":
|
|
||||||
"QR 상태 확인에 실패했습니다: {{error}}",
|
|
||||||
"err.userfront.auth_proxy.qr_approve": "QR 승인에 실패했습니다: {{error}}",
|
"err.userfront.auth_proxy.qr_approve": "QR 승인에 실패했습니다: {{error}}",
|
||||||
"err.userfront.auth_proxy.user_create":
|
"err.userfront.auth_proxy.user_create": "사용자 생성에 실패했습니다: {{error}}",
|
||||||
"사용자 생성에 실패했습니다: {{error}}",
|
"err.userfront.auth_proxy.user_list": "사용자 목록 조회에 실패했습니다: {{error}}",
|
||||||
"err.userfront.auth_proxy.user_list":
|
"err.userfront.auth_proxy.user_delete": "사용자 삭제에 실패했습니다: {{error}}",
|
||||||
"사용자 목록 조회에 실패했습니다: {{error}}",
|
"err.userfront.auth_proxy.user_status_update": "상태 업데이트에 실패했습니다: {{error}}",
|
||||||
"err.userfront.auth_proxy.user_delete":
|
"err.userfront.auth_proxy.user_update": "사용자 수정에 실패했습니다: {{error}}",
|
||||||
"사용자 삭제에 실패했습니다: {{error}}",
|
"err.userfront.auth_proxy.linked_apps_load": "연동된 앱 목록을 불러오지 못했습니다.",
|
||||||
"err.userfront.auth_proxy.user_status_update":
|
"err.userfront.auth_proxy.phone_code_send": "인증 코드 전송에 실패했습니다: {{error}}",
|
||||||
"상태 업데이트에 실패했습니다: {{error}}",
|
|
||||||
"err.userfront.auth_proxy.user_update":
|
|
||||||
"사용자 수정에 실패했습니다: {{error}}",
|
|
||||||
"err.userfront.auth_proxy.linked_apps_load":
|
|
||||||
"연동된 앱 목록을 불러오지 못했습니다.",
|
|
||||||
"err.userfront.auth_proxy.phone_code_send":
|
|
||||||
"인증 코드 전송에 실패했습니다: {{error}}",
|
|
||||||
"err.userfront.profile.load_failed": "프로필을 불러오지 못했습니다: {{error}}",
|
"err.userfront.profile.load_failed": "프로필을 불러오지 못했습니다: {{error}}",
|
||||||
"err.userfront.profile.password_change_failed": "비밀번호 변경에 실패했습니다: {{error}}",
|
"err.userfront.profile.password_change_failed": "비밀번호 변경에 실패했습니다: {{error}}",
|
||||||
"err.userfront.profile.send_code_failed": "인증번호 전송 실패: {{error}}",
|
"err.userfront.profile.send_code_failed": "인증번호 전송 실패: {{error}}",
|
||||||
@@ -621,8 +607,7 @@ const Map<String, String> koStrings = {
|
|||||||
"재설정 링크가 만료되었습니다. 다시 요청해 주세요.",
|
"재설정 링크가 만료되었습니다. 다시 요청해 주세요.",
|
||||||
"msg.userfront.error.whitelist.recovery_invalid": "재설정 링크가 유효하지 않습니다.",
|
"msg.userfront.error.whitelist.recovery_invalid": "재설정 링크가 유효하지 않습니다.",
|
||||||
"msg.userfront.error.whitelist.settings_disabled": "현재 계정 설정 화면은 준비 중입니다.",
|
"msg.userfront.error.whitelist.settings_disabled": "현재 계정 설정 화면은 준비 중입니다.",
|
||||||
"msg.userfront.error.whitelist.tenant_not_allowed":
|
"msg.userfront.error.whitelist.tenant_not_allowed": "허용되지 않은 테넌트입니다.",
|
||||||
"허용되지 않은 테넌트입니다.",
|
|
||||||
"msg.userfront.error.whitelist.verification_required":
|
"msg.userfront.error.whitelist.verification_required":
|
||||||
"추가 인증이 필요합니다. 안내에 따라 진행해 주세요.",
|
"추가 인증이 필요합니다. 안내에 따라 진행해 주세요.",
|
||||||
"msg.userfront.forgot.description":
|
"msg.userfront.forgot.description":
|
||||||
@@ -2059,15 +2044,12 @@ const Map<String, String> enStrings = {
|
|||||||
"Failed to load the profile: {{error}}",
|
"Failed to load the profile: {{error}}",
|
||||||
"err.userfront.auth_proxy.tenant_info_fetch":
|
"err.userfront.auth_proxy.tenant_info_fetch":
|
||||||
"Failed to load tenant information.",
|
"Failed to load tenant information.",
|
||||||
"err.userfront.auth_proxy.verify_failed":
|
"err.userfront.auth_proxy.verify_failed": "Verification failed: {{error}}",
|
||||||
"Verification failed: {{error}}",
|
|
||||||
"err.userfront.auth_proxy.sms_send": "Failed to send SMS: {{error}}",
|
"err.userfront.auth_proxy.sms_send": "Failed to send SMS: {{error}}",
|
||||||
"err.userfront.auth_proxy.code_verify":
|
"err.userfront.auth_proxy.code_verify":
|
||||||
"Failed to verify the code: {{error}}",
|
"Failed to verify the code: {{error}}",
|
||||||
"err.userfront.auth_proxy.qr_init":
|
"err.userfront.auth_proxy.qr_init": "Failed to start QR login: {{error}}",
|
||||||
"Failed to start QR login: {{error}}",
|
"err.userfront.auth_proxy.qr_poll": "Failed to check QR status: {{error}}",
|
||||||
"err.userfront.auth_proxy.qr_poll":
|
|
||||||
"Failed to check QR status: {{error}}",
|
|
||||||
"err.userfront.auth_proxy.qr_approve":
|
"err.userfront.auth_proxy.qr_approve":
|
||||||
"Failed to approve QR login: {{error}}",
|
"Failed to approve QR login: {{error}}",
|
||||||
"err.userfront.auth_proxy.user_create":
|
"err.userfront.auth_proxy.user_create":
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ void main() {
|
|||||||
);
|
);
|
||||||
final detail = tr(
|
final detail = tr(
|
||||||
'msg.userfront.error.whitelist.settings_disabled',
|
'msg.userfront.error.whitelist.settings_disabled',
|
||||||
fallback: internalErrorWhitelistMessages['settings_disabled']!,
|
fallback: tr(internalErrorWhitelistMessageKeys['settings_disabled']!),
|
||||||
);
|
);
|
||||||
final type = tr(
|
final type = tr(
|
||||||
'msg.userfront.error.type',
|
'msg.userfront.error.type',
|
||||||
@@ -160,7 +160,7 @@ void main() {
|
|||||||
|
|
||||||
final detail = tr(
|
final detail = tr(
|
||||||
'msg.userfront.error.whitelist.not_found',
|
'msg.userfront.error.whitelist.not_found',
|
||||||
fallback: internalErrorWhitelistMessages['not_found']!,
|
fallback: tr(internalErrorWhitelistMessageKeys['not_found']!),
|
||||||
);
|
);
|
||||||
final type = tr(
|
final type = tr(
|
||||||
'msg.userfront.error.type',
|
'msg.userfront.error.type',
|
||||||
@@ -185,7 +185,7 @@ void main() {
|
|||||||
|
|
||||||
final detail = tr(
|
final detail = tr(
|
||||||
'msg.userfront.error.whitelist.rate_limited',
|
'msg.userfront.error.whitelist.rate_limited',
|
||||||
fallback: internalErrorWhitelistMessages['rate_limited']!,
|
fallback: tr(internalErrorWhitelistMessageKeys['rate_limited']!),
|
||||||
);
|
);
|
||||||
final type = tr(
|
final type = tr(
|
||||||
'msg.userfront.error.type',
|
'msg.userfront.error.type',
|
||||||
@@ -214,28 +214,12 @@ void main() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
final title = tr(
|
const title = 'Application access is restricted';
|
||||||
'msg.userfront.error.tenant.page_title',
|
const detail =
|
||||||
fallback: '애플리케이션 접근이 제한되었습니다',
|
'The current signed-in account cannot access this application.';
|
||||||
);
|
const account = 'Account';
|
||||||
final detail = tr(
|
const primaryTenant = 'Primary affiliated tenant';
|
||||||
'msg.userfront.error.tenant.detail',
|
const affiliatedTenants = 'All affiliated tenants';
|
||||||
fallback: '현재 로그인된 계정은 이 애플리케이션에 접근할 수 없습니다.',
|
|
||||||
);
|
|
||||||
final account = tr('msg.userfront.error.tenant.account', fallback: '계정');
|
|
||||||
final primaryTenant = tr(
|
|
||||||
'msg.userfront.error.tenant.primary_tenant',
|
|
||||||
fallback: '대표 소속 테넌트',
|
|
||||||
);
|
|
||||||
final affiliatedTenants = tr(
|
|
||||||
'msg.userfront.error.tenant.affiliated_tenants',
|
|
||||||
fallback: '전체 소속 테넌트',
|
|
||||||
);
|
|
||||||
final switchAccount = tr(
|
|
||||||
'ui.userfront.error.switch_account',
|
|
||||||
fallback: '다른 계정으로 로그인',
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(find.text(title), findsOneWidget);
|
expect(find.text(title), findsOneWidget);
|
||||||
expect(find.text(detail), findsOneWidget);
|
expect(find.text(detail), findsOneWidget);
|
||||||
expect(find.text(account), findsOneWidget);
|
expect(find.text(account), findsOneWidget);
|
||||||
@@ -243,7 +227,8 @@ void main() {
|
|||||||
expect(find.text(primaryTenant), findsOneWidget);
|
expect(find.text(primaryTenant), findsOneWidget);
|
||||||
expect(find.text(affiliatedTenants), findsOneWidget);
|
expect(find.text(affiliatedTenants), findsOneWidget);
|
||||||
expect(find.text('Baron HQ'), findsNWidgets(2));
|
expect(find.text('Baron HQ'), findsNWidgets(2));
|
||||||
expect(find.text(switchAccount), findsOneWidget);
|
expect(find.byType(ElevatedButton), findsOneWidget);
|
||||||
|
expect(find.byType(OutlinedButton), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('tenant_not_allowed는 details를 우선 사용해 계정과 테넌트 정보를 노출한다', (
|
testWidgets('tenant_not_allowed는 details를 우선 사용해 계정과 테넌트 정보를 노출한다', (
|
||||||
|
|||||||
Reference in New Issue
Block a user