259 lines
11 KiB
JavaScript
259 lines
11 KiB
JavaScript
/**
|
|
* SatelliteRequest.tsx 디자인 시스템 적용 스크립트
|
|
* 실행: node scripts/fix-satellite-request.mjs
|
|
*/
|
|
import { readFileSync, writeFileSync } from 'fs';
|
|
import { fileURLToPath } from 'url';
|
|
import { dirname, resolve } from 'path';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
|
|
const filePath = resolve(__dirname, '../frontend/src/tabs/aerial/components/SatelliteRequest.tsx');
|
|
let src = readFileSync(filePath, 'utf-8');
|
|
|
|
// ────────────────────────────────────────────────────
|
|
// 작업 1: UI 개선
|
|
// ────────────────────────────────────────────────────
|
|
|
|
// 1-1. stats 배열 color 필드 제거
|
|
src = src.replace(
|
|
`const stats = [
|
|
{ value: '3', label: '요청 대기', color: 'var(--color-info)' },
|
|
{ value: '1', label: '촬영 진행 중', color: 'var(--color-caution)' },
|
|
{ value: '7', label: '수신 완료', color: 'var(--color-success)' },
|
|
{ value: '0.5m', label: '최고 해상도', color: 'var(--color-accent)' },
|
|
];`,
|
|
`const stats = [
|
|
{ value: '3', label: '요청 대기' },
|
|
{ value: '1', label: '촬영 진행 중' },
|
|
{ value: '7', label: '수신 완료' },
|
|
{ value: '0.5m', label: '최고 해상도' },
|
|
];`
|
|
);
|
|
|
|
// 1-1. stats 값 div: style color 제거, text-fg 추가
|
|
src = src.replace(
|
|
`<div className="text-[22px] font-bold font-mono" style={{ color: s.color }}>`,
|
|
`<div className="text-[22px] font-bold font-mono text-fg">`
|
|
);
|
|
|
|
// 1-2. 예상수신 컬럼 style 제거
|
|
src = src.replace(
|
|
` <div
|
|
className="text-caption font-semibold font-mono"
|
|
style={{
|
|
color: r.status === '촬영중' ? 'var(--color-caution)' : 'var(--fg-sub)',
|
|
}}
|
|
>
|
|
{r.expectedReceive}
|
|
</div>`,
|
|
` <div className="text-caption font-mono text-fg">
|
|
{r.expectedReceive}
|
|
</div>`
|
|
);
|
|
|
|
// 1-2. 해상도 컬럼 style 제거
|
|
src = src.replace(
|
|
` <div
|
|
className="text-label-2 font-bold font-mono"
|
|
style={{
|
|
color: r.status === '완료' ? 'var(--fg-disabled)' : 'var(--color-accent)',
|
|
}}
|
|
>
|
|
{r.resolution}
|
|
</div>`,
|
|
` <div className="text-label-2 font-mono text-fg">
|
|
{r.resolution}
|
|
</div>`
|
|
);
|
|
|
|
// 1-3. statusBadge 아이콘 제거
|
|
src = src.replace(` ⏳ 대기`, ` 대기`);
|
|
src = src.replace(` ✕ 취소`, ` 취소`);
|
|
src = src.replace(` ✅ 완료`, ` 완료`);
|
|
|
|
// 1-4. 촬영 가능 시간 스타일 변경
|
|
src = src.replace(
|
|
` <div className="flex flex-col gap-1.5">
|
|
{passSchedules.map((ps, i) => (
|
|
<div
|
|
key={i}
|
|
className="flex items-center gap-2 px-2.5 py-[7px] rounded-[5px]"
|
|
style={{
|
|
background: ps.today ? 'rgba(34,197,94,.05)' : 'rgba(59,130,246,.05)',
|
|
border: ps.today
|
|
? '1px solid rgba(34,197,94,.15)'
|
|
: '1px solid rgba(59,130,246,.15)',
|
|
}}
|
|
>
|
|
<span
|
|
className="text-caption font-bold font-mono min-w-[90px]"
|
|
style={{ color: ps.today ? 'var(--color-accent)' : 'var(--color-info)' }}
|
|
>
|
|
{ps.time}
|
|
</span>
|
|
<span className="text-caption text-fg font-korean">{ps.desc}</span>
|
|
</div>
|
|
))}`,
|
|
` <div className="flex flex-col gap-1.5">
|
|
{passSchedules.map((ps, i) => (
|
|
<div
|
|
key={i}
|
|
className="flex items-center gap-2.5 px-3 py-2 bg-bg-card rounded-md"
|
|
style={{ border: '1px solid var(--stroke-default)' }}
|
|
>
|
|
<span
|
|
className={\`text-label-2 font-semibold font-mono min-w-[90px] \${ps.today ? 'text-color-accent' : 'text-fg'}\`}
|
|
>
|
|
{ps.time}
|
|
</span>
|
|
<span className="text-caption text-fg-disabled font-korean">{ps.desc}</span>
|
|
</div>
|
|
))}`
|
|
);
|
|
|
|
// ────────────────────────────────────────────────────
|
|
// 작업 2: rgba 비-accent → color-mix 전환
|
|
// ────────────────────────────────────────────────────
|
|
|
|
// 제외 구간 마킹: up42Satellites 배열 (L181-310 범위)은 보호
|
|
// 전략: 배열 부분을 임시 플레이스홀더로 치환 → bulk 작업 → 복원
|
|
|
|
const UP42_PLACEHOLDER = '___UP42_SATELLITES_PLACEHOLDER___';
|
|
|
|
const up42StartMarker = '// UP42 위성 카탈로그 데이터\nconst up42Satellites = [';
|
|
const up42EndMarker = '];\n\n// up42Passes';
|
|
|
|
const up42Start = src.indexOf(up42StartMarker);
|
|
const up42End = src.indexOf(up42EndMarker) + up42EndMarker.length;
|
|
|
|
if (up42Start === -1 || up42End === -1) {
|
|
console.error('up42Satellites 배열을 찾지 못했습니다. 스크립트를 중단합니다.');
|
|
process.exit(1);
|
|
}
|
|
|
|
const up42Block = src.slice(up42Start, up42End);
|
|
src = src.slice(0, up42Start) + UP42_PLACEHOLDER + src.slice(up42End);
|
|
|
|
// --- rgba → color-mix 전환 ---
|
|
// 매핑 함수
|
|
function pct(alpha) {
|
|
// alpha: 문자열 (예: ".15", "0.15", ".5", "0.5")
|
|
const n = parseFloat(alpha);
|
|
return Math.round(n * 100) + '%';
|
|
}
|
|
|
|
// 정규식 패턴: rgba(R,G,B,A) 형태를 매칭 (공백 허용)
|
|
// 각 색상별로 처리
|
|
|
|
// rgba(59,130,246,N) → info
|
|
src = src.replace(/rgba\(59,\s*130,\s*246,\s*([\d.]+)\)/g, (match, alpha) => {
|
|
return `color-mix(in srgb, var(--color-info) ${pct(alpha)}, transparent)`;
|
|
});
|
|
|
|
// rgba(99,102,241,N) → info (보라 계열도 info로)
|
|
src = src.replace(/rgba\(99,\s*102,\s*241,\s*([\d.]+)\)/g, (match, alpha) => {
|
|
return `color-mix(in srgb, var(--color-info) ${pct(alpha)}, transparent)`;
|
|
});
|
|
|
|
// rgba(34,197,94,N) → success
|
|
src = src.replace(/rgba\(34,\s*197,\s*94,\s*([\d.]+)\)/g, (match, alpha) => {
|
|
return `color-mix(in srgb, var(--color-success) ${pct(alpha)}, transparent)`;
|
|
});
|
|
|
|
// rgba(239,68,68,N) → danger
|
|
src = src.replace(/rgba\(239,\s*68,\s*68,\s*([\d.]+)\)/g, (match, alpha) => {
|
|
return `color-mix(in srgb, var(--color-danger) ${pct(alpha)}, transparent)`;
|
|
});
|
|
|
|
// rgba(234,179,8,N) → caution
|
|
src = src.replace(/rgba\(234,\s*179,\s*8,\s*([\d.]+)\)/g, (match, alpha) => {
|
|
return `color-mix(in srgb, var(--color-caution) ${pct(alpha)}, transparent)`;
|
|
});
|
|
|
|
// rgba(100,116,139,N) → fg-disabled
|
|
src = src.replace(/rgba\(100,\s*116,\s*139,\s*([\d.]+)\)/g, (match, alpha) => {
|
|
return `color-mix(in srgb, var(--fg-disabled) ${pct(alpha)}, transparent)`;
|
|
});
|
|
|
|
// Tailwind className 내 변환
|
|
// border-[rgba(59,130,246,.3)] → border-[rgba(6,182,212,0.3)]
|
|
src = src.replace(/border-\[rgba\(59,130,246,\.3\)\]/g, 'border-[rgba(6,182,212,0.3)]');
|
|
src = src.replace(/hover:border-\[rgba\(59,130,246,\.5\)\]/g, 'hover:border-[rgba(6,182,212,0.4)]');
|
|
src = src.replace(/hover:bg-\[rgba\(59,130,246,\.04\)\]/g, 'hover:bg-[rgba(6,182,212,0.04)]');
|
|
|
|
// className 내 border-[rgba(99,102,241,...)] 는 그대로 유지 (위성 식별용이 아닌 UI 요소)
|
|
// → 이미 위 정규식으로 style 속성 내는 변환됨. className 내의 것은 별도 처리
|
|
src = src.replace(/border-\[rgba\(99,102,241,\.3\)\]/g, 'border-[rgba(6,182,212,0.3)]');
|
|
src = src.replace(/hover:border-\[rgba\(99,102,241,\.5\)\]/g, 'hover:border-[rgba(6,182,212,0.4)]');
|
|
src = src.replace(/hover:bg-\[rgba\(99,102,241,\.04\)\]/g, 'hover:bg-[rgba(6,182,212,0.04)]');
|
|
|
|
// up42 블록 복원
|
|
src = src.replace(UP42_PLACEHOLDER, up42Block);
|
|
|
|
// ────────────────────────────────────────────────────
|
|
// 작업 3: 나머지 하드코딩 색상
|
|
// ────────────────────────────────────────────────────
|
|
|
|
// urgency '예정' color: '#06b6d4' → 'var(--color-accent)'
|
|
// L2122 근처: urgency === '예정' ? '#06b6d4' 패턴
|
|
src = src.replace(
|
|
` ? '#06b6d4'`,
|
|
` ? 'var(--color-accent)'`
|
|
);
|
|
|
|
// UP42 제출 버튼 전체 변경 (color '#fff' → 'var(--color-accent)', boxShadow 제거)
|
|
src = src.replace(
|
|
` <button
|
|
onClick={() => setModalPhase('none')}
|
|
className="px-6 py-2 rounded-lg text-label-2 font-bold cursor-pointer font-korean text-color-accent transition-opacity"
|
|
style={{
|
|
background: up42SelSat ? 'rgba(6,182,212,0.08)' : 'var(--stroke-light)',
|
|
opacity: up42SelSat ? 1 : 0.5,
|
|
color: up42SelSat ? '#fff' : 'var(--fg-disabled)',
|
|
boxShadow: up42SelSat ? '0 4px 16px rgba(59,130,246,.35)' : 'none',
|
|
}}
|
|
>`,
|
|
` <button
|
|
onClick={() => setModalPhase('none')}
|
|
className="px-6 py-2 rounded-lg text-label-2 font-bold cursor-pointer font-korean text-color-accent transition-opacity"
|
|
style={{
|
|
background: up42SelSat ? 'rgba(6,182,212,0.08)' : 'var(--stroke-light)',
|
|
opacity: up42SelSat ? 1 : 0.5,
|
|
color: up42SelSat ? 'var(--color-accent)' : 'var(--fg-disabled)',
|
|
}}
|
|
>`
|
|
);
|
|
|
|
// BlackSky 제출 버튼 boxShadow 제거
|
|
src = src.replace(
|
|
` style={{
|
|
background: 'rgba(6,182,212,0.08)',
|
|
boxShadow: '0 4px 16px color-mix(in srgb, var(--color-info) 35%, transparent)',
|
|
}}`,
|
|
` style={{
|
|
background: 'rgba(6,182,212,0.08)',
|
|
}}`
|
|
);
|
|
|
|
// text-blue-500 → text-color-info (2곳: L1908, L2129)
|
|
src = src.replace(/className="text-label-1 text-blue-500"/g, 'className="text-label-1 text-color-info"');
|
|
src = src.replace(/className="text-xs text-blue-500"/g, 'className="text-xs text-color-info"');
|
|
|
|
// text-green-500 → text-color-success (L1243, L1306, L1468)
|
|
src = src.replace(/className="([^"]*)\btext-green-500\b([^"]*)"/g, (match, before, after) => {
|
|
return `className="${before}text-color-success${after}"`;
|
|
});
|
|
|
|
// text-red-400 → text-color-danger
|
|
src = src.replace(/className="([^"]*)\btext-red-400\b([^"]*)"/g, (match, before, after) => {
|
|
return `className="${before}text-color-danger${after}"`;
|
|
});
|
|
|
|
// ────────────────────────────────────────────────────
|
|
// 결과 저장
|
|
// ────────────────────────────────────────────────────
|
|
writeFileSync(filePath, src, 'utf-8');
|
|
console.log('✅ SatelliteRequest.tsx 변환 완료');
|