forked from baron/baron-sso
Merge branch 'dev' into feature/issue-919-audit-logs-e2e
This commit is contained in:
@@ -662,6 +662,7 @@ function UserCreatePage() {
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="absolute right-1 top-1 h-8 text-xs font-bold"
|
||||
data-testid="add-sub-email-btn"
|
||||
onClick={() => {
|
||||
const value = newSubEmail.trim().replace(/,/g, "");
|
||||
if (
|
||||
@@ -678,7 +679,7 @@ function UserCreatePage() {
|
||||
}}
|
||||
>
|
||||
{t("ui.common.add", "추가")}
|
||||
</Button>
|
||||
</Button>{" "}
|
||||
</div>
|
||||
<p className="text-[10px] text-muted-foreground mt-1">
|
||||
* 여러 개 입력 가능. 입력 후 엔터를 눌러 추가하세요.
|
||||
@@ -877,6 +878,7 @@ function UserCreatePage() {
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={addAppointment}
|
||||
data-testid="add-appointment-btn"
|
||||
>
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
{t("ui.common.add", "추가")}
|
||||
|
||||
@@ -94,8 +94,8 @@ import { resolvePersonalTenant } from "./utils/personalTenant";
|
||||
|
||||
type UserFormValues = Omit<UserUpdateRequest, "metadata"> & {
|
||||
email: string;
|
||||
metadata: Record<string, Record<string, string | number | boolean>> & {
|
||||
sub_email?: string[];
|
||||
metadata: Record<string, unknown> & {
|
||||
sub_email?: string | string[];
|
||||
};
|
||||
};
|
||||
type UserCategory = "hanmac" | "external" | "personal";
|
||||
@@ -108,6 +108,44 @@ type AppointmentDraft = UserAppointment & {
|
||||
|
||||
const PASSWORD_RESET_MIN_LENGTH = 12;
|
||||
|
||||
function isMetadataRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||||
}
|
||||
|
||||
function cleanMetadataValue(value: unknown): unknown {
|
||||
if (Array.isArray(value)) {
|
||||
return value
|
||||
.filter((item): item is string => typeof item === "string")
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
if (isMetadataRecord(value)) {
|
||||
return Object.fromEntries(
|
||||
Object.entries(value).filter(
|
||||
([_, fieldValue]) =>
|
||||
fieldValue !== undefined && fieldValue !== null && fieldValue !== "",
|
||||
),
|
||||
);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function normalizeSubEmails(value: unknown): string[] {
|
||||
if (Array.isArray(value)) {
|
||||
return value
|
||||
.filter((item): item is string => typeof item === "string")
|
||||
.map((item) => item.trim())
|
||||
.filter((item) => item.includes("@"));
|
||||
}
|
||||
if (typeof value === "string" && value.trim() !== "") {
|
||||
return value
|
||||
.split(/[;,\n\r\t]/)
|
||||
.map((email) => email.trim())
|
||||
.filter((email) => email.includes("@"));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function createDraftId() {
|
||||
return globalThis.crypto?.randomUUID?.() ?? `appointment-${Date.now()}`;
|
||||
}
|
||||
@@ -773,15 +811,17 @@ function UserDetailPage() {
|
||||
});
|
||||
|
||||
const onSubmit = async (data: UserFormValues) => {
|
||||
// Filter out undefined/null/empty strings from metadata
|
||||
const cleanMetadata = Object.fromEntries(
|
||||
Object.entries(data.metadata).map(([tenantId, fields]) => {
|
||||
const cleanFields = Object.fromEntries(
|
||||
Object.entries(fields).filter(
|
||||
([_, v]) => v !== undefined && v !== null && v !== "",
|
||||
),
|
||||
);
|
||||
return [tenantId, cleanFields];
|
||||
Object.entries(data.metadata ?? {}).flatMap(([key, value]) => {
|
||||
const cleanedValue = cleanMetadataValue(value);
|
||||
if (
|
||||
cleanedValue === undefined ||
|
||||
cleanedValue === null ||
|
||||
cleanedValue === ""
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
return [[key, cleanedValue]];
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -791,22 +831,11 @@ function UserDetailPage() {
|
||||
sub_email: rawSubEmail,
|
||||
...safeMetadata
|
||||
} = cleanMetadata;
|
||||
|
||||
// Parse sub_email
|
||||
let sub_email: string[] = [];
|
||||
if (
|
||||
typeof rawSubEmail === "string" &&
|
||||
(rawSubEmail as string).trim() !== ""
|
||||
) {
|
||||
sub_email = (rawSubEmail as string)
|
||||
.split(/[;,\n\r\t]/)
|
||||
.map((e: string) => e.trim())
|
||||
.filter((e: string) => e.includes("@"));
|
||||
}
|
||||
const subEmail = normalizeSubEmails(rawSubEmail);
|
||||
|
||||
const metadata: Record<string, unknown> = {
|
||||
...safeMetadata,
|
||||
...(sub_email.length > 0 ? { sub_email } : { sub_email: [] }),
|
||||
...(subEmail.length > 0 ? { sub_email: subEmail } : { sub_email: [] }),
|
||||
};
|
||||
|
||||
const payload: UserUpdateRequest = {
|
||||
@@ -991,16 +1020,14 @@ function UserDetailPage() {
|
||||
<Mail size={14} className="text-primary/70" />
|
||||
{user.email}
|
||||
</div>
|
||||
{!!user.metadata?.sub_email &&
|
||||
Array.isArray(user.metadata.sub_email) &&
|
||||
(user.metadata.sub_email as unknown[]).length > 0 && (
|
||||
<div className="flex items-center gap-1.5 bg-background px-2.5 py-1 rounded-full border">
|
||||
<Mail size={14} className="text-primary/40" />
|
||||
<span className="text-[10px] font-bold">
|
||||
+{(user.metadata.sub_email as unknown[]).length}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{normalizeSubEmails(user.metadata?.sub_email).length > 0 && (
|
||||
<div className="flex items-center gap-1.5 bg-background px-2.5 py-1 rounded-full border">
|
||||
<Mail size={14} className="text-primary/40" />
|
||||
<span className="text-[10px] font-bold">
|
||||
+{normalizeSubEmails(user.metadata?.sub_email).length}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{user.phone && (
|
||||
<div className="flex items-center gap-1.5 bg-background px-2.5 py-1 rounded-full border">
|
||||
<Shield size={14} className="text-primary/70" />
|
||||
@@ -1166,6 +1193,7 @@ function UserDetailPage() {
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="absolute right-1 top-1 h-9 text-xs font-bold"
|
||||
data-testid="add-sub-email-btn"
|
||||
onClick={() => {
|
||||
const value = newSubEmail.trim().replace(/,/g, "");
|
||||
if (
|
||||
@@ -1319,6 +1347,7 @@ function UserDetailPage() {
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={addAppointment}
|
||||
data-testid="add-appointment-btn"
|
||||
>
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
{t("ui.common.add", "추가")}
|
||||
|
||||
@@ -354,10 +354,12 @@ function applySecondaryEmailMetadata(
|
||||
value: string,
|
||||
) {
|
||||
const emails = splitEmailTokens(value);
|
||||
item.metadata.sub_email = uniqueEmails([
|
||||
...metadataEmailList(item.metadata.sub_email),
|
||||
const uniqueSecondaryEmails = uniqueEmails([
|
||||
...metadataEmailList(item.metadata.secondary_emails),
|
||||
...emails,
|
||||
]);
|
||||
item.metadata.sub_email = value;
|
||||
item.metadata.secondary_emails = uniqueSecondaryEmails;
|
||||
addWorksmobileAliasEmails(item, emails);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user