generated from gc/template-java-maven
- 디자인 시스템 가이드 문서 11개 생성 (docs/design/) - CSS 변수 토큰 시스템 (@theme + :root/.dark 전환) - cn() 유틸리티 (clsx + tailwind-merge) - Button/Badge 공통 컴포넌트 (variant/size, 다크모드 대응) - 하드코딩 Tailwind 색상 → CSS 변수 토큰 리팩토링 (30개 파일) - 차트 팔레트 다크모드 색상 업데이트 (CHART_COLORS_HEX) - 버튼 다크모드 채도/대비 강화 (primary-600 기반) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
109 lines
3.7 KiB
TypeScript
109 lines
3.7 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { getSystemConfig, updateSystemConfig } from '../../services/configService';
|
|
import Button from '../../components/ui/Button';
|
|
|
|
const COMMON_SAMPLE_CODE_KEY = 'COMMON_SAMPLE_CODE';
|
|
|
|
const INPUT_CLS =
|
|
'w-full border border-[var(--color-border-strong)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] rounded-lg px-3 py-2 focus:ring-2 focus:ring-[var(--color-primary)] focus:outline-none text-sm font-mono';
|
|
|
|
const SampleCodePage = () => {
|
|
const [sampleCode, setSampleCode] = useState('');
|
|
const [loading, setLoading] = useState(true);
|
|
const [saving, setSaving] = useState(false);
|
|
const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null);
|
|
|
|
useEffect(() => {
|
|
const fetchConfig = async () => {
|
|
try {
|
|
const res = await getSystemConfig(COMMON_SAMPLE_CODE_KEY);
|
|
if (res.success && res.data?.configValue != null) {
|
|
setSampleCode(res.data.configValue);
|
|
}
|
|
} catch {
|
|
setMessage({ type: 'error', text: '샘플 코드를 불러오는데 실패했습니다.' });
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
fetchConfig();
|
|
}, []);
|
|
|
|
const handleSave = async () => {
|
|
setSaving(true);
|
|
setMessage(null);
|
|
try {
|
|
const res = await updateSystemConfig(COMMON_SAMPLE_CODE_KEY, sampleCode);
|
|
if (res.success) {
|
|
setMessage({ type: 'success', text: '저장되었습니다.' });
|
|
} else {
|
|
setMessage({ type: 'error', text: res.message || '저장에 실패했습니다.' });
|
|
}
|
|
} catch {
|
|
setMessage({ type: 'error', text: '저장 중 오류가 발생했습니다.' });
|
|
} finally {
|
|
setSaving(false);
|
|
setTimeout(() => setMessage(null), 3000);
|
|
}
|
|
};
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="flex items-center justify-center py-20">
|
|
<div className="w-8 h-8 border-4 border-[var(--color-primary)] border-t-transparent rounded-full animate-spin" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="max-w-4xl mx-auto">
|
|
<div className="flex items-start justify-between mb-6">
|
|
<div>
|
|
<h1 className="text-2xl font-bold text-[var(--color-text-primary)]">공통 샘플 코드 관리</h1>
|
|
<p className="text-sm text-[var(--color-text-secondary)] mt-1">
|
|
API HUB 상세 페이지에 공통으로 표시되는 샘플 코드를 관리합니다.
|
|
</p>
|
|
</div>
|
|
<Button
|
|
onClick={handleSave}
|
|
disabled={saving}
|
|
className="shrink-0"
|
|
>
|
|
{saving ? '저장 중...' : '저장'}
|
|
</Button>
|
|
</div>
|
|
|
|
{message && (
|
|
<div
|
|
className={`mb-4 p-3 rounded-lg text-sm ${
|
|
message.type === 'success'
|
|
? 'bg-green-50 dark:bg-green-900/30 text-green-700 dark:text-green-400'
|
|
: 'bg-red-50 dark:bg-red-900/30 text-red-700 dark:text-red-400'
|
|
}`}
|
|
>
|
|
{message.text}
|
|
</div>
|
|
)}
|
|
|
|
<div className="bg-[var(--color-bg-surface)] rounded-lg shadow p-6">
|
|
<label className="block text-sm font-medium text-[var(--color-text-primary)] mb-2">
|
|
샘플 코드
|
|
</label>
|
|
<textarea
|
|
value={sampleCode}
|
|
onChange={(e) => setSampleCode(e.target.value)}
|
|
rows={20}
|
|
placeholder="API HUB 상세 페이지에 표시할 공통 샘플 코드를 입력하세요."
|
|
className={`${INPUT_CLS} resize-y`}
|
|
/>
|
|
<p className="mt-2 text-xs text-[var(--color-text-tertiary)]">
|
|
이 코드는 모든 API 상세 페이지의 '요청 URL 생성' 섹션 하단에 표시됩니다.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default SampleCodePage;
|