feat(ai-character): 캐릭터 관리 기능 구현
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
import type { CreateCharacterParams, UpdateCharacterParams } from "@/features/characters/api/character-api";
|
||||
import type { CharacterDetail } from "@/features/characters/model/types";
|
||||
import type { BackgroundDraft, CharacterOptionalFieldsValue, MemoryDraft, RelationshipDraft, TraitDraft } from "@/features/characters/components/CharacterOptionalFields";
|
||||
|
||||
type RelationshipRequest = NonNullable<CreateCharacterParams["request"]["relationships"]>[number];
|
||||
type TraitRequest = NonNullable<CreateCharacterParams["request"]["personalities"]>[number];
|
||||
type BackgroundRequest = NonNullable<CreateCharacterParams["request"]["backgrounds"]>[number];
|
||||
type MemoryRequest = NonNullable<CreateCharacterParams["request"]["memories"]>[number];
|
||||
type CreateOptionalRequest = Partial<CreateCharacterParams["request"]>;
|
||||
type MutableCreateOptionalRequest = { -readonly [Key in keyof CreateOptionalRequest]: CreateOptionalRequest[Key] };
|
||||
|
||||
function emptyRelationship(): RelationshipDraft {
|
||||
return { personName: "", relationshipName: "", description: "", importance: "", relationshipType: "", currentStatus: "" };
|
||||
}
|
||||
|
||||
function emptyTrait(): TraitDraft {
|
||||
return { trait: "", description: "" };
|
||||
}
|
||||
|
||||
function emptyBackground(): BackgroundDraft {
|
||||
return { topic: "", description: "" };
|
||||
}
|
||||
|
||||
function emptyMemory(): MemoryDraft {
|
||||
return { title: "", content: "", emotion: "" };
|
||||
}
|
||||
|
||||
export function createEmptyCharacterOptionalFields(): CharacterOptionalFieldsValue {
|
||||
return { age: "", gender: "", mbti: "", speechPattern: "", speechStyle: "", appearance: "", region: "", originalTitle: "", originalLink: "", originalTitleTouched: false, originalLinkTouched: false, characterType: "", tags: "", hobbies: "", values: "", goals: "", relationships: [emptyRelationship()], personalities: [emptyTrait()], backgrounds: [emptyBackground()], memories: [emptyMemory()] };
|
||||
}
|
||||
|
||||
export function characterOptionalFieldsFromDetail(character: CharacterDetail): CharacterOptionalFieldsValue {
|
||||
return {
|
||||
age: character.age === null ? "" : String(character.age), gender: character.gender ?? "", mbti: character.mbti ?? "", speechPattern: character.speechPattern ?? "", speechStyle: character.speechStyle ?? "", appearance: character.appearance ?? "", region: character.region, originalTitle: "", originalLink: "", originalTitleTouched: false, originalLinkTouched: false, characterType: character.characterType,
|
||||
tags: character.tags.join("\n"), hobbies: character.hobbies.join("\n"), values: character.values.join("\n"), goals: character.goals.join("\n"),
|
||||
relationships: character.relationships.length === 0 ? [emptyRelationship()] : character.relationships.map((row) => ({ ...row, importance: String(row.importance) })), personalities: character.personalities.length === 0 ? [emptyTrait()] : character.personalities, backgrounds: character.backgrounds.length === 0 ? [emptyBackground()] : character.backgrounds, memories: character.memories.length === 0 ? [emptyMemory()] : character.memories,
|
||||
};
|
||||
}
|
||||
|
||||
function trimOptional(value: string): string | undefined {
|
||||
const trimmed = value.trim();
|
||||
return trimmed.length === 0 ? undefined : trimmed;
|
||||
}
|
||||
|
||||
function trimNullable(value: string): string | null {
|
||||
return trimOptional(value) ?? null;
|
||||
}
|
||||
|
||||
function splitList(value: string): readonly string[] {
|
||||
return value.split(/[\n,]/).map((item) => item.trim()).filter((item) => item.length > 0);
|
||||
}
|
||||
|
||||
function hasRelationship(row: RelationshipDraft): boolean {
|
||||
return [row.personName, row.relationshipName, row.description, row.importance, row.relationshipType, row.currentStatus].some((value) => value.trim().length > 0);
|
||||
}
|
||||
|
||||
function relationships(rows: readonly RelationshipDraft[]): readonly RelationshipRequest[] {
|
||||
return rows.filter(hasRelationship).map((row) => {
|
||||
const importance = Number.parseInt(row.importance.trim(), 10);
|
||||
return {
|
||||
personName: row.personName.trim(),
|
||||
relationshipName: row.relationshipName.trim(),
|
||||
description: row.description.trim(),
|
||||
importance: Number.isNaN(importance) ? 0 : importance,
|
||||
relationshipType: row.relationshipType.trim(),
|
||||
currentStatus: row.currentStatus.trim(),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function traits(rows: readonly TraitDraft[]): readonly TraitRequest[] {
|
||||
return rows.filter((row) => row.trait.trim().length > 0 || row.description.trim().length > 0).map((row) => ({ trait: row.trait.trim(), description: row.description.trim() }));
|
||||
}
|
||||
|
||||
function backgrounds(rows: readonly BackgroundDraft[]): readonly BackgroundRequest[] {
|
||||
return rows.filter((row) => row.topic.trim().length > 0 || row.description.trim().length > 0).map((row) => ({ topic: row.topic.trim(), description: row.description.trim() }));
|
||||
}
|
||||
|
||||
function memories(rows: readonly MemoryDraft[]): readonly MemoryRequest[] {
|
||||
return rows.filter((row) => row.title.trim().length > 0 || row.content.trim().length > 0 || row.emotion.trim().length > 0).map((row) => ({ title: row.title.trim(), content: row.content.trim(), emotion: row.emotion.trim() }));
|
||||
}
|
||||
|
||||
export function toCreateCharacterOptionalRequest(value: CharacterOptionalFieldsValue): CreateOptionalRequest {
|
||||
const request: MutableCreateOptionalRequest = {};
|
||||
const age = trimOptional(value.age);
|
||||
const gender = trimOptional(value.gender);
|
||||
const mbti = trimOptional(value.mbti);
|
||||
const speechPattern = trimOptional(value.speechPattern);
|
||||
const speechStyle = trimOptional(value.speechStyle);
|
||||
const appearance = trimOptional(value.appearance);
|
||||
const region = trimOptional(value.region);
|
||||
const originalTitle = trimOptional(value.originalTitle);
|
||||
const originalLink = trimOptional(value.originalLink);
|
||||
const characterType = trimOptional(value.characterType);
|
||||
const tags = splitList(value.tags);
|
||||
const hobbies = splitList(value.hobbies);
|
||||
const values = splitList(value.values);
|
||||
const goals = splitList(value.goals);
|
||||
const relationshipRows = relationships(value.relationships);
|
||||
const personalityRows = traits(value.personalities);
|
||||
const backgroundRows = backgrounds(value.backgrounds);
|
||||
const memoryRows = memories(value.memories);
|
||||
|
||||
if (age !== undefined) request.age = age;
|
||||
if (gender !== undefined) request.gender = gender;
|
||||
if (mbti !== undefined) request.mbti = mbti;
|
||||
if (speechPattern !== undefined) request.speechPattern = speechPattern;
|
||||
if (speechStyle !== undefined) request.speechStyle = speechStyle;
|
||||
if (appearance !== undefined) request.appearance = appearance;
|
||||
if (region !== undefined) request.region = region;
|
||||
if (originalTitle !== undefined) request.originalTitle = originalTitle;
|
||||
if (originalLink !== undefined) request.originalLink = originalLink;
|
||||
if (characterType !== undefined) request.characterType = characterType;
|
||||
if (tags.length > 0) request.tags = tags;
|
||||
if (hobbies.length > 0) request.hobbies = hobbies;
|
||||
if (values.length > 0) request.values = values;
|
||||
if (goals.length > 0) request.goals = goals;
|
||||
if (relationshipRows.length > 0) request.relationships = relationshipRows;
|
||||
if (personalityRows.length > 0) request.personalities = personalityRows;
|
||||
if (backgroundRows.length > 0) request.backgrounds = backgroundRows;
|
||||
if (memoryRows.length > 0) request.memories = memoryRows;
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
export function toUpdateCharacterOptionalRequest(value: CharacterOptionalFieldsValue): UpdateCharacterParams["request"] {
|
||||
const tags = splitList(value.tags);
|
||||
const hobbies = splitList(value.hobbies);
|
||||
const values = splitList(value.values);
|
||||
const goals = splitList(value.goals);
|
||||
const relationshipRows = relationships(value.relationships);
|
||||
const personalityRows = traits(value.personalities);
|
||||
const backgroundRows = backgrounds(value.backgrounds);
|
||||
const memoryRows = memories(value.memories);
|
||||
|
||||
return {
|
||||
age: trimNullable(value.age), gender: trimNullable(value.gender), mbti: trimNullable(value.mbti), speechPattern: trimNullable(value.speechPattern), speechStyle: trimNullable(value.speechStyle), appearance: trimNullable(value.appearance), ...(value.originalTitleTouched ? { originalTitle: trimNullable(value.originalTitle) } : {}), ...(value.originalLinkTouched ? { originalLink: trimNullable(value.originalLink) } : {}), characterType: trimNullable(value.characterType),
|
||||
tags: tags.length === 0 ? null : tags, hobbies: hobbies.length === 0 ? null : hobbies, values: values.length === 0 ? null : values, goals: goals.length === 0 ? null : goals,
|
||||
relationships: relationshipRows.length === 0 ? null : relationshipRows, personalities: personalityRows.length === 0 ? null : personalityRows, backgrounds: backgroundRows.length === 0 ? null : backgroundRows, memories: memoryRows.length === 0 ? null : memoryRows,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user