오류 수정
CI / test (push) Failing after 5m41s
CI / e2e (push) Has been skipped
CI / pr_and_merge (push) Has been skipped

This commit is contained in:
2025-12-12 18:04:31 +09:00
parent 6804665b95
commit 4840a530b6
205 changed files with 1802 additions and 9887 deletions
@@ -25,7 +25,7 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
<h3 className="text-[11px] font-semibold text-slate-800 dark:text-slate-200">{m.sectionTitle}</h3>
<label className="flex flex-col gap-1">
<span className="text-slate-500 dark:text-slate-400"> </span>
<span className="text-slate-500 dark:text-slate-400">{m.groupTitleTypeLabel}</span>
<select
className="w-full rounded border border-slate-300 bg-white px-2 py-1 text-[11px] text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
value={radioProps.groupLabelMode ?? "text"}
@@ -35,13 +35,13 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
} as any)
}
>
<option value="text"></option>
<option value="image"></option>
<option value="text">{m.groupTitleTypeOptionText}</option>
<option value="image">{m.groupTitleTypeOptionImage}</option>
</select>
</label>
<label className="flex flex-col gap-1">
<span className="text-slate-500 dark:text-slate-400"> </span>
<span className="text-slate-500 dark:text-slate-400">{m.groupTitleDisplayLabel}</span>
<select
className="w-full rounded border border-slate-300 bg-white px-2 py-1 text-[11px] text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
value={groupLabelDisplay}
@@ -51,14 +51,14 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
} as any)
}
>
<option value="visible"> ()</option>
<option value="hidden"></option>
<option value="visible">{m.groupTitleDisplayOptionVisible}</option>
<option value="hidden">{m.groupTitleDisplayOptionHidden}</option>
</select>
</label>
{groupLabelDisplay === "visible" && (
<label className="flex flex-col gap-1 text-[11px] text-slate-500 dark:text-slate-400">
<span> </span>
<span>{m.layoutLabel}</span>
<select
className="w-full rounded border border-slate-300 bg-white px-2 py-1 text-[11px] text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
value={radioProps.labelLayout ?? "stacked"}
@@ -68,14 +68,14 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
} as any)
}
>
<option value="stacked"> ()</option>
<option value="inline"> ()</option>
<option value="stacked">{m.layoutOptionStacked}</option>
<option value="inline">{m.layoutOptionInline}</option>
</select>
</label>
)}
<label className="flex flex-col gap-1 text-[11px] text-slate-500 dark:text-slate-400">
<span> </span>
<span>{m.optionLayoutLabel}</span>
<select
className="w-full rounded border border-slate-300 bg-white px-2 py-1 text-[11px] text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
value={radioProps.optionLayout ?? "stacked"}
@@ -85,24 +85,24 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
} as any)
}
>
<option value="stacked"> ()</option>
<option value="inline"> ()</option>
<option value="stacked">{m.optionLayoutOptionStacked}</option>
<option value="inline">{m.optionLayoutOptionInline}</option>
</select>
</label>
{groupLabelDisplay === "visible" && (radioProps.labelLayout ?? "stacked") === "inline" && (
<NumericPropertyControl
label="라벨/필드 간격 (px)"
label={m.labelGapLabel}
unitLabel="(px)"
value={typeof radioProps.labelGapPx === "number" ? radioProps.labelGapPx : 8}
min={0}
max={80}
step={2}
presets={[
{ id: "tight", label: "타이트", value: 4 },
{ id: "normal", label: "보통", value: 8 },
{ id: "relaxed", label: "느슨", value: 12 },
{ id: "extra", label: "넓게", value: 16 },
{ id: "tight", label: "Tight", value: 4 },
{ id: "normal", label: "Normal", value: 8 },
{ id: "relaxed", label: "Relaxed", value: 12 },
{ id: "extra", label: "Extra", value: 16 },
]}
onChangeValue={(v) =>
updateBlock(selectedBlockId, {
@@ -113,7 +113,7 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
)}
<label className="flex flex-col gap-1">
<span className="text-slate-500 dark:text-slate-400"> </span>
<span className="text-slate-500 dark:text-slate-400">{m.groupTitleLabel}</span>
<input
className="w-full rounded border border-slate-300 bg-white px-2 py-1 text-xs text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
value={radioProps.groupLabel ?? ""}
@@ -135,7 +135,7 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
return (
<div className="space-y-1">
<label className="flex flex-col gap-1">
<span className="text-slate-400"> </span>
<span className="text-slate-400">{m.groupTitleImageSourceLabel}</span>
<select
className="w-full rounded border border-slate-300 bg-white px-2 py-1 text-[11px] text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
value={source}
@@ -145,14 +145,14 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
} as any)
}
>
<option value="url">URL</option>
<option value="upload"> </option>
<option value="url">{m.groupTitleImageSourceOptionUrl}</option>
<option value="upload">{m.groupTitleImageSourceOptionUpload}</option>
</select>
</label>
{source === "url" && (
<label className="flex flex-col gap-1">
<span className="text-slate-400"> URL</span>
<span className="text-slate-400">{m.groupTitleImageUrlLabel}</span>
<input
className="w-full rounded border border-slate-300 bg-white px-2 py-1 text-xs text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
value={radioProps.groupLabelImageUrl ?? ""}
@@ -167,12 +167,12 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
{source === "upload" && (
<label className="flex flex-col gap-1">
<span className="text-slate-400"> </span>
<span className="text-slate-400">{m.groupTitleImageUploadLabel}</span>
<input
type="file"
accept="image/*"
className="w-full text-[11px] text-slate-300 file:mr-2 file:rounded file:border file:border-slate-700 file:bg-slate-800 file:px-2 file:py-1 file:text-[11px] file:text-slate-100 hover:file:bg-slate-700"
aria-label="그룹 타이틀 이미지 파일 업로드"
aria-label={m.groupTitleImageUploadAria}
onChange={async (event) => {
const file = event.target.files?.[0];
if (!file) return;
@@ -212,7 +212,7 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
})()}
<label className="flex flex-col gap-1">
<span className="text-slate-500 dark:text-slate-400"> </span>
<span className="text-slate-500 dark:text-slate-400">{m.submitKeyLabel}</span>
<input
className="w-full rounded border border-slate-300 bg-white px-2 py-1 text-xs text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
value={radioProps.formFieldName ?? ""}
@@ -226,7 +226,7 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
<div className="space-y-2">
<div className="flex items-center justify-between">
<span className="text-slate-500 dark:text-slate-400"></span>
<span className="text-slate-500 dark:text-slate-400">{m.optionsLabel}</span>
<button
type="button"
className="rounded border border-slate-300 bg-white px-2 py-0.5 text-[10px] text-slate-900 hover:bg-slate-100 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100 dark:hover:bg-red-900/60 dark:hover:border-red-700"
@@ -235,7 +235,7 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
? [...(radioProps as any).options]
: [];
next.push({
label: "새 옵션",
label: "New option",
value: `option_${next.length + 1}`,
});
updateBlock(selectedBlockId, {
@@ -243,12 +243,12 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
} as any);
}}
>
{m.addOptionButtonLabel}
</button>
</div>
<div className="space-y-2">
<label className="flex flex-col gap-1">
<span className="text-slate-500 dark:text-slate-400"> </span>
<span className="text-slate-500 dark:text-slate-400">{m.optionImageSourceLabel}</span>
<select
className="w-full rounded border border-slate-300 bg-white px-2 py-1 text-[11px] text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
value={(() => {
@@ -263,8 +263,8 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
} as any)
}
>
<option value="url">URL</option>
<option value="upload"> </option>
<option value="url">{m.optionImageSourceOptionUrl}</option>
<option value="upload">{m.optionImageSourceOptionUpload}</option>
</select>
</label>
@@ -273,7 +273,7 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
<div className="grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto] items-center gap-1">
<input
className="flex-1 rounded border border-slate-300 bg-white px-2 py-1 text-[11px] text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
placeholder="라벨"
placeholder={m.optionLabelPlaceholder}
value={opt.label ?? ""}
onChange={(e) => {
const next = [...(((radioProps as any).options ?? []) as any[])];
@@ -288,7 +288,7 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
/>
<input
className="flex-1 rounded border border-slate-300 bg-white px-2 py-1 text-[11px] text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
placeholder="값(value)"
placeholder={m.optionValuePlaceholder}
value={opt.value ?? ""}
onChange={(e) => {
const next = [...(((radioProps as any).options ?? []) as any[])];
@@ -325,7 +325,7 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
{source === "url" && (
<input
className="w-full rounded border border-slate-300 bg-white px-2 py-1 text-[11px] text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
placeholder="옵션 이미지 URL (선택)"
placeholder={m.optionImageUrlPlaceholder}
value={opt.labelImageUrl ?? ""}
onChange={(e) => {
const next = [...(((radioProps as any).options ?? []) as any[])];
@@ -341,12 +341,12 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
)}
{source === "upload" && (
<label className="flex flex-col gap-1">
<span className="text-slate-500 dark:text-slate-400"> </span>
<span className="text-slate-500 dark:text-slate-400">{m.optionImageUploadLabel}</span>
<input
type="file"
accept="image/*"
className="w-full text-[11px] text-slate-300 file:mr-2 file:rounded file:border file:border-slate-700 file:bg-slate-800 file:px-2 file:py-1 file:text-[11px] file:text-slate-100 hover:file:bg-slate-700"
aria-label="옵션 이미지 파일 업로드"
aria-label={m.optionImageUploadAria}
onChange={async (event) => {
const file = event.target.files?.[0];
if (!file) return;
@@ -442,9 +442,9 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
max={60}
step={1}
presets={[
{ id: "tight", label: "타이트", value: 18 },
{ id: "normal", label: "보통", value: 24 },
{ id: "loose", label: "루즈", value: 32 },
{ id: "tight", label: "Tight", value: 18 },
{ id: "normal", label: "Normal", value: 24 },
{ id: "loose", label: "Loose", value: 32 },
]}
onChangeValue={(v) => {
updateBlock(selectedBlockId, {
@@ -466,9 +466,9 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
max={20}
step={0.5}
presets={[
{ id: "tight", label: "좁게", value: -1 },
{ id: "normal", label: "보통", value: 0 },
{ id: "wide", label: "넓게", value: 2 },
{ id: "tight", label: "Tight", value: -1 },
{ id: "normal", label: "Normal", value: 0 },
{ id: "wide", label: "Wide", value: 2 },
]}
onChangeValue={(v) => {
updateBlock(selectedBlockId, {