Files
Aislo/B03_FileInput/B03_FileInput_UI_Style.css
T
eomsangdonandClaude Opus 5 e2f6a101b6 feat(B03): 재접속 업로드 현황(10) + B05·B06 자동 설계 체인(9)
10. 재접속 현황·완료 표시·재업로드 경고:
- GET /projects/{id}/upload-overview 신설 — 완료 파일 목록(input_files 정본),
  중단 청크 세션(진행률), 필수 파일·WF1 분석 완료 여부
- 진입 시 서버 정본으로 슬롯 카드 표시(serverUploaded), localStorage는 보조로 강등
- 파일 재선택 전에도 중단 세션 이어올리기 안내 배너
- 전체 완료 배지 + 완료 슬롯 재업로드 시 교체 확인 모달(승인 시에만 진행)
- 필수 슬롯 검증: 서버 업로드분 있으면 충족 — 단일 파일 교체 업로드 허용

9. 자동 설계 체인 연장 (B03_FileInput_Service_Chain.py):
- WF1 자동 확정 후 같은 백그라운드 태스크에서 ① 계획노선 CSV 기반 B05 기본 경로
  계산(solve) ② 경로 확정(stage 2) ③ B06 기본 횡단 설계 확정(stage 3)까지 진행
- 수동 이력 보호: 프로젝트에 경로가 이미 있으면 건너뜀
- 단계별 실패 격리: 실패 단계에서 멈추고 로그·workflow 상태로만 기록
- AUTO_DESIGN_CHAIN_ENABLED config 플래그(기본 True)

typecheck·ruff·B03 unittest(7건) 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 18:09:15 +09:00

460 lines
11 KiB
CSS

.b03-file {
min-height: calc(100vh - var(--app-header-height, 0px));
background: linear-gradient(
180deg,
var(--color-canvas, #ffffff) 0%,
var(--color-mist-violet, #edecff) 100%
);
padding: 0 var(--spacing-24) var(--spacing-48) var(--spacing-24); /* 상단 여백은 main-layout 마진으로 조정하므로 padding-top은 0 */
}
.b03-file__main-layout {
max-width: var(--page-max-width, 1200px);
margin: var(--spacing-40) auto 0 auto; /* 상단 구분선(헤더)과의 간격을 확실하게 40px 벌림 */
display: flex;
flex-direction: column;
gap: var(--spacing-32);
}
.b03-file__control-panel {
display: flex;
flex-direction: column;
gap: var(--spacing-20);
background: var(--color-canvas, #ffffff);
padding: var(--spacing-32);
border-radius: var(--radius-large, 24px); /* Wiza 24px 대형 둥글기 */
border: 1px solid var(--color-mist, #e6e2e3);
box-shadow: var(--shadow-lg);
}
.b03-file__cards-container-panel {
margin-top: 0; /* 단일 layout gap에 의해 제어되도록 설정 */
}
.b03-file__subtitle {
font-size: var(--text-body-sm, 14px);
color: var(--color-slate, #615e6e);
margin: 0 0 var(--spacing-8) 0;
}
.b03-file__native-input,
.b03-file__slot-input {
display: none;
}
/* Wiza 스타일 가이드의 Lavender Glow 그라데이션 및 soft purple border hover */
.b03-file__dropzone {
min-height: 160px;
padding: var(--spacing-32) var(--spacing-24);
border: 2px dashed var(--color-mist, #e6e2e3);
border-radius: var(--radius-large, 24px);
background: var(--color-paper, #f6f7fa);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: var(--spacing-8);
cursor: pointer;
text-align: center;
transition:
background var(--transition-base, 0.2s),
border-color var(--transition-base, 0.2s),
box-shadow var(--transition-base, 0.2s);
}
.b03-file__dropzone:hover {
background: var(--color-mist-violet, #edecff);
border-color: var(--color-royal-amethyst, #3e0079);
}
.b03-file__dropzone:focus-visible,
.b03-file__dropzone.is-dragging {
outline: none;
background: var(--color-mist-violet, #edecff);
border-color: var(--color-royal-amethyst, #3e0079);
box-shadow: 0 0 0 4px rgba(62, 0, 121, 0.15);
}
.b03-file__dropzone strong {
color: var(--color-deep-iris, #26114a);
font-size: var(--text-body, 16px);
font-weight: var(--font-weight-medium, 500);
}
.b03-file__dropzone span {
font-size: var(--text-body-sm, 14px);
color: var(--color-slate, #615e6e);
}
.b03-file__error {
min-height: var(--spacing-16);
color: var(--color-danger, #dc2626);
font-size: var(--text-body-sm, 14px);
margin: var(--spacing-8) 0;
}
.b03-file__resume {
display: none;
align-items: center;
gap: var(--spacing-8);
flex-wrap: wrap;
padding: var(--spacing-12) var(--spacing-16);
border: 1px solid var(--color-mist, #e6e2e3);
border-radius: var(--radius-cards, 8px);
background: var(--color-paper, #f6f7fa);
color: var(--color-deep-iris, #26114a);
font-size: var(--text-body-sm, 14px);
margin-top: var(--spacing-8);
}
.b03-file__resume.is-visible {
display: flex;
}
/* 재접속 현황(서버 정본) 배너 — 완료 안내와 중단 세션 이어올리기 안내를 담는다. */
.b03-file__overview {
display: none;
flex-direction: column;
gap: var(--spacing-4);
margin-top: var(--spacing-8);
padding: var(--spacing-12) var(--spacing-16);
border: 1px solid var(--color-border);
border-radius: var(--radius-cards, 8px);
background: var(--color-surface);
font-size: var(--text-body-sm, 14px);
}
.b03-file__overview.is-visible {
display: flex;
}
.b03-file__overview-complete {
margin: 0;
color: var(--color-success, #3fbb6f);
font-weight: 600;
}
.b03-file__overview-pending {
margin: 0;
color: var(--color-text-secondary);
}
/* 완료 슬롯 재업로드 확인 모달 — 페이지 위를 덮는 단순 확인창. */
.b03-file__modal-backdrop {
position: fixed;
inset: 0;
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
background: rgb(0 0 0 / 45%);
}
.b03-file__modal {
display: flex;
flex-direction: column;
gap: var(--spacing-12);
max-width: 420px;
padding: var(--spacing-24);
border: 1px solid var(--color-border);
border-radius: var(--radius-cards, 8px);
background: var(--color-surface-raised);
box-shadow: 0 12px 32px rgb(0 0 0 / 30%);
}
.b03-file__modal p {
margin: 0;
color: var(--color-text-secondary);
white-space: pre-line;
}
.b03-file__modal-actions {
display: flex;
gap: var(--spacing-8);
justify-content: flex-end;
}
.b03-file__cards-grid {
display: grid;
grid-template-columns: 1fr;
gap: var(--spacing-32);
}
.b03-file__group {
display: flex;
flex-direction: column;
gap: var(--spacing-20);
}
.b03-file__group-title {
font-size: var(--text-subheading, 24px);
color: var(--color-deep-iris, #26114a);
font-family: var(--font-britti-sans, inherit);
margin: 0 0 var(--spacing-8) 0;
}
.b03-file__group-content {
display: grid;
grid-template-columns: repeat(2, 1fr); /* 2행 2열 구조 */
gap: var(--spacing-24);
}
.b03-file__group--route .b03-file__group-content {
grid-template-columns: 1fr;
}
.b03-file__group--route .b03-file__card {
border-color: var(--color-royal-amethyst, #3e0079);
background: var(--color-mist-violet, #edecff);
}
/* Wiza 8px radius 카드 */
.b03-file__card {
min-height: 220px;
padding: var(--spacing-24); /* 내부 전체 패딩 확대 */
border: 1px solid var(--color-mist, #e6e2e3);
border-radius: var(--radius-cards, 8px);
background: var(--color-canvas, #ffffff);
box-shadow: var(--shadow-sm);
display: flex;
flex-direction: column;
gap: var(--spacing-20);
transition:
transform var(--transition-base, 0.2s),
border-color var(--transition-base, 0.2s),
box-shadow var(--transition-base, 0.2s);
}
.b03-file__card:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-lg);
border-color: var(--color-royal-amethyst, #3e0079);
}
.b03-file__card--selected {
border-color: var(--color-royal-amethyst, #3e0079);
background: var(--color-paper, #f6f7fa);
}
.b03-file__card--uploading {
border-color: var(--color-royal-amethyst, #3e0079);
box-shadow: var(--shadow-lg-2);
}
.b03-file__card--completed {
border-color: #10b981; /* Success Green */
}
.b03-file__card--error {
border-color: var(--color-danger, #dc2626);
}
.b03-file__card-header {
display: grid;
grid-template-columns: auto 1fr auto auto; /* 아이콘 및 닫기 버튼 주변 확보 */
gap: var(--spacing-12);
align-items: center;
}
.b03-file__card-icon {
width: 28px;
height: 28px;
border-radius: var(--radius-icons, 8px);
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--color-royal-amethyst, #3e0079);
background: var(--color-mist-violet, #edecff);
font-size: var(--text-body-sm, 14px);
margin-right: var(--spacing-8); /* 아이콘 우측 마진 추가 (아이콘 좌측 여유 확대 효과) */
}
.b03-file__card-heading {
min-width: 0;
display: flex;
flex-direction: column;
gap: 2px;
margin-top: 2px; /* 제목 상단 여유 추가 */
}
.b03-file__card-label {
color: var(--color-deep-iris, #26114a);
font-size: var(--text-body-sm, 14px);
font-weight: var(--font-weight-medium, 500);
}
.b03-file__card-ext,
.b03-file__file-size,
.b03-file__progress-info {
color: var(--color-slate, #615e6e);
font-size: var(--text-caption, 12px);
}
.b03-file__card-badge-container {
display: flex;
align-items: center;
margin-right: var(--spacing-4);
}
.b03-file__card-remove {
width: 24px;
height: 24px;
border: 1px solid var(--color-mist, #e6e2e3);
border-radius: var(--radius-buttons, 8px);
color: var(--color-slate, #615e6e);
background: var(--color-canvas, #ffffff);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: var(--text-body-sm, 14px);
line-height: 1;
padding: 0;
margin-left: var(--spacing-8); /* 취소 버튼 좌측 여유 추가 (취소 버튼 우측 여유 확보) */
transition: all var(--transition-base, 0.2s);
}
.b03-file__card-remove:hover {
color: var(--color-danger, #dc2626);
border-color: var(--color-danger, #dc2626);
background: #fef2f2;
}
.b03-file__card-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: flex-end;
gap: var(--spacing-16); /* 버튼 하단 및 내부 간격 확대 */
padding-bottom: var(--spacing-8); /* 파일 선택 버튼 하단 패딩 확보 */
}
.b03-file__card-select {
width: 100%;
min-height: 40px; /* 버튼 높이 증가 */
border: 1px solid var(--color-mist, #e6e2e3);
border-radius: var(--radius-buttons, 8px);
color: var(--color-deep-iris, #26114a);
background: var(--color-canvas, #ffffff);
font-weight: var(--font-weight-medium, 500);
font-size: var(--text-body-sm, 14px);
cursor: pointer;
margin-bottom: var(--spacing-8); /* 파일 선택 버튼 하단 마진 추가 */
transition:
background var(--transition-base, 0.2s),
border-color var(--transition-base, 0.2s);
}
.b03-file__file-info {
display: flex;
flex-direction: column;
gap: 4px;
min-width: 0;
}
.b03-file__file-name {
color: var(--color-deep-iris, #26114a);
font-size: var(--text-body-sm, 14px);
font-weight: var(--font-weight-medium, 500);
overflow-wrap: anywhere;
}
.b03-file__progress-section {
display: flex;
flex-direction: column;
gap: var(--spacing-8);
}
.b03-file__progress-bar-container {
width: 100%;
height: 6px;
overflow: hidden;
border-radius: var(--radius-pills, 1440px);
background: var(--color-mist, #e6e2e3);
}
.b03-file__progress-bar {
width: 0;
height: 100%;
border-radius: var(--radius-pills, 1440px);
background: var(--color-royal-amethyst, #3e0079);
transition: width var(--transition-base, 0.2s);
}
.b03-file__progress-info {
display: flex;
flex-direction: column;
gap: 2px;
}
.b03-file__error-message {
display: none;
padding: var(--spacing-8) var(--spacing-12);
border-radius: var(--radius-cards, 8px);
color: var(--color-danger, #dc2626);
background: #fef2f2;
font-size: var(--text-caption, 12px);
border: 1px solid rgba(220, 38, 38, 0.15);
}
.b03-file__card--empty .b03-file__file-info,
.b03-file__card--empty .b03-file__progress-section,
.b03-file__card--empty .b03-file__error-message {
display: none;
}
.b03-file__card--selected .b03-file__progress-section {
display: none;
}
.b03-file__card--uploading .b03-file__card-select,
.b03-file__card--completed .b03-file__card-select {
display: none;
}
.b03-file__card--error .b03-file__error-message {
display: block;
}
.b03-file__card--error .b03-file__progress-section {
display: none;
}
.b03-file__results {
margin: var(--spacing-8) 0 0 0;
padding: 0;
list-style: none;
border: 1px solid var(--color-mist, #e6e2e3);
border-radius: var(--radius-cards, 8px);
overflow: hidden;
background: var(--color-canvas, #ffffff);
}
.b03-file__results:empty {
display: none;
}
.b03-file__results li {
padding: var(--spacing-12) var(--spacing-16);
display: flex;
flex-direction: column;
gap: 2px;
border-bottom: 1px solid var(--color-mist, #e6e2e3);
color: var(--color-deep-iris, #26114a);
font-size: var(--text-body-sm, 14px);
}
.b03-file__results li:last-child {
border-bottom: 0;
}
@media (max-width: 720px) {
.b03-file {
padding: var(--spacing-16) var(--spacing-12);
}
.b03-file__group-content {
grid-template-columns: 1fr; /* 모바일에서는 1행 1열 구조 */
}
}