Feature/#41 - CreateSecretView Form 조립 (9 subtype · 반응형 · Project 다중선택 · Save 검증 UX) - #61
Conversation
- DVChipsField 내부 private struct였던 flow layout을 별도 파일로 이관 - 이름을 chip 비종속으로 변경 (DVChipFlow → DVFlowLayout) - 접근제어를 internal로 열어 다른 컴포넌트도 재사용 가능 - 로직·동작 변화 없음
- Generic Item 기반 다중 선택 드롭다운 (Identifiable & Hashable) - 트리거는 값 없을 때 placeholder + chevron, 있을 때 chip flow (read-only) - searchText binding으로 헤더 검색 opt-in, onCreate 콜백으로 푸터 opt-in - 팝오버 open 시점 selection 스냅샷 기반 섹션 배치 (세션 중 재정렬 방지) - 검색어 하이라이트 (AttributedString semibold + vaultGreenDark) - 9종 Figma variants 커버하는 프리뷰
- ProjectFieldView: DVMultiSelectDropdown 소비, [Project.ID]↔Set 어댑터, Add-new 콜백 노출 - CreateSecretFeature: @presents createProject + didTapCreateProject Action + ifLet - .projectCreated 델리게이트 수신 시 availableProjects에 append - CreateSecretView: CreateProjectView sheet 라우팅 - DVMultiSelectDropdown Item constraint 완화 (Identifiable & Hashable → Identifiable)
- DVMultiSelectDropdown: placeholder 텍스트와 chevron을 gray600으로 통일 - ExpireDateFieldView: 체크박스 dummy 제거, Mac Calendar 앱 패턴으로 재설계 - nil 상태: gray300 textContainer 안에 No expired 트리거 - set 상태: 인라인 stepperField DatePicker - 옆에 trash 버튼 (nil일 땐 비활성 회색)
- $binding.scope가 TCA Scope 심볼과 dynamic member lookup 충돌해 컴파일 실패 - authorityScope로 개명해 회피, 도메인 매핑은 APIKeyMetadata.scope 그대로
- APIKeysTokenSectionView: apiKeyToken 타입의 6-row 폼 섹션 조립 (Name/Value/Project|Services/Expire|Env/Auth/Memo) - Common + type-specific 필드 바인딩, validation/detected hint 소비 - Preview 4종 (Empty·Dual / Filled+hint·Dual / Filled·Single / Validation errors) - Binding+CasePathable.typed(_:default:): CasePathable enum에서 특정 case의 non-optional Binding<T> 추출 헬퍼 - Value: CasePathable 제네릭이라 다른 도메인 enum에도 재사용 가능
- scrollContent를 secretType별 SectionView로 분기 (apiKeyToken → APIKeysTokenSectionView) - 나머지 SecretType은 placeholder + TODO(#41-followup) 유지 - 프리뷰 5종 재정비 (Wide·Empty / Wide·Filled+hint / Narrow·Filled·scroll / Medium·Empty / Database·placeholder) - 프리셋 데이터 주입 헬퍼(previewState)로 조합된 화면 시각 확인 가능 - CreateSecretHeaderView: maxWidth infinity leading 정렬로 컨테이너 크기 반영
- OAuthClientSectionView: 8-row 폼 (Name/Client ID/Client Secret/Project|Services/Expire|Env/Redirect URL/Scope/Memo) - ServiceAccountSectionView: 6-row 폼 (Name/Certification JSON/Project|Services/Expire|_/Authority/Memo) - Figma상 Environment 미노출 — 오른쪽 슬롯 Color.clear로 유지해 wide/narrow 전환 자연스럽게 - CreateSecretView: .oauth 라우팅 + selectedSubType별 subview 분기 - 프리뷰: 각 SectionView 4종 + CreateSecretView에 oauth Wide/Narrow · Client/ServiceAccount 4종 추가
- DatabaseSectionView: 6-row 폼 (Name/Link String/Project|Services/Expire|Env/SSL Required/Memo) - 기존 SSLRequiredFieldView + Project/ExpireDate 공통 컴포넌트 재사용 - CreateSecretView: .database 라우팅 추가, 기존 placeholder 제거 - 프리뷰: SectionView 4종 + CreateSecretView Database Wide/Narrow 2종
- SSHKeySectionView: 7-row 폼 (Name/Private Key/Project|Env/Public Key/PassPhrase/Host|Username/Memo) - SSLTLSCertSectionView: 7-row 폼 (Name/Certificate/Private Key/Project|Env/Cert Chain/Renew Cmd/Memo) - 두 케이스 모두 Services / Expire Date 미노출 — common 바인딩에서도 제외 (Figma 기준) - 새 페어링 [Project | Environment] — 지금까지의 [Project | Services] 패턴과 다름 - CreateSecretView: .sshAndCredentials 라우팅 + selectedSubType별 subview 분기
- EnvSetSectionView: 4-row 폼 (Name/envSet List/Project|Env/Memo) - Services / Expire Date 미노출 — common 바인딩에서도 제외 (Figma 기준) - CreateSecretView: .environmentVariableSet 라우팅 + EnvSet Wide/Narrow 프리뷰 2종
- LicenseKeySectionView: 8-row 폼 (Name/License Key/Project|Services/Expire|Type/Support Email/Website/Order Number/Memo) - Environment 미노출 — LicenseTier "Type"이 오른쪽 페어 슬롯 차지 - CustomSectionView: 5-row 폼 (Name/Value/Project|Services/Expire|Env/Memo) — 최소 구성 - CreateSecretView: .etc 라우팅 + selectedSubType별 subview 분기 - default 케이스 제거 — 모든 SecretType (apiKey/oauth/database/ssh/env/etc) 커버 완료 - 프리뷰 4종 추가 (License Key W/N, Custom W/N)
- GeometryReader로 실 컨테이너 폭 측정 → FormLayoutMode.mode(for:) → .environment 주입 - 하위 SectionView / AdaptiveFieldRow / Field 뷰들이 자동으로 dual↔single 전환 - .frame(minWidth: 520)로 헤더 3-radio 자연 폭 이하 축소 차단 (필드 clipping 방지) - 프리뷰의 explicit .environment(\.formLayoutMode, ...) 20개 제거 (폭 기반 자동 선택) - FormLayoutMode: wiring 완료로 TODO 코멘트 제거
- 신규 String.module(_:) 헬퍼 — LocalizedStringResource.module 결과를 String으로 반환 - SectionView 9개 + Field 뷰 8개 + FooterActions 하드코딩 label/placeholder/hint 이관 - Auto-detected hint 프리픽스, "No expired", "SSL Required", Cancel/Save 등 포함 - CreateSecretFeature validation "Required" 이관 - Preview seed 데이터는 유지 (필요시 개별 rollback 가능)
- FormError.missingRequired 단일 FieldID → [FieldID] 목록으로 변경 - SecretMetaFields: missingRequiredFieldID → missingRequiredFieldIDs (모든 누락 수집) - toCreateSecretPayload에서 name + type-specific missing을 한 번에 반환 - 리듀서: fieldIDs iterate로 validationErrors 다중 세팅, missing 케이스에 Log.warn - handleSave 성공 경로에 payload/draft/projectIds Log.info 콘솔 출력 (client 연결 전 임시 검증용) - 테스트: 초기 상태(name+value 둘 다 빈값) → 다중 warning 반영, OAuth Client 3개 동시 missing 케이스 신규
- 신규 CreateSecretSectionHintProviding 프로토콜 + extension default 구현 - 9개 SectionView가 conformance만 추가하면 로컬 hintFor 12줄 블록 삭제 - callsite 무변경 (hintFor(.value) 그대로)
- LabeledTextFieldView.SizeMode를 FormLayoutMode 파일의 FormSlotSize enum으로 이관 - ProjectFieldView가 더 이상 LabeledTextFieldView 내부 타입을 참조하지 않음 - 새 폼 필드가 추가되어도 FormSlotSize만 알면 됨 (텍스트필드 개념 non-leaky)
- DVButton.Style에 .secondaryProminent 신규 (secondary 지오메트리 + vaultGreen 강조 배경) - width/height 하드코딩 → minWidth/minHeight로 변경 (text 길이에 자연 확장, floor로만 유지) - FooterActionsView: SaveButtonView + SaveButtonStyle 로컬 struct 삭제 - Cancel 옆에 DVButton(.secondaryProminent) 사용, 라벨 "Save" → "Create" (Figma 준수)
- CreateSecretFeature.handleSave 평문 payload/draft 로그를 #if DEBUG로 감쌈 - AdaptiveFieldRow: left/right 슬롯 모두 optional화, 1-arg convenience init 2종 - ServiceAccountSectionView의 Color.clear 제거 → 1-arg 형태로 대체 - SecretMetaFields.isValid 함수 + metaIsValid_* 테스트 8개 삭제 — 프로덕션 dead code - saveResponse(.failure) → isSaving 해제 테스트 신규 - ExpireDateFieldView dateBinding fallback → Date() 단순화, defaultInitialDate 중복 호출 제거
- FormSectionScaffold 신규: NameFieldView + content + MemoFieldView + 16pt spacing + maxWidth infinity 컨테이너를 타입 시스템으로 강제 - 9개 SectionView가 스캐폴드 소비 — 각 body에서 outer VStack/frame/Name/Memo 반복 제거 (~4 lines × 9 = ~36 LOC 절감) - AdaptiveFieldRow spacing 20 → 16pt 통일 (Figma 실측)
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (8)
🚧 Files skipped from review as they are similar to previous changes (6)
WalkthroughCreateSecret 화면에 secret 유형별 입력 섹션, 반응형 폼 레이아웃, 프로젝트 다중 선택 및 생성 시트가 추가되었습니다. 필수 필드 검증은 여러 누락 필드를 동시에 표시하도록 변경되었고, 공통 버튼·칩 레이아웃 컴포넌트가 확장되었습니다. Changes디자인 컴포넌트와 선택 UI
폼 공통 기반과 필드 동작
Secret 유형별 섹션 뷰
CreateSecret 연결과 다중 검증
Estimated code review effort: 4 (Complex) | ~75 minutes Sequence Diagram(s)sequenceDiagram
participant CreateSecretView
participant CreateSecretFeature
participant ProjectFieldView
participant CreateProjectView
CreateSecretView->>CreateSecretFeature: 유형별 섹션과 바인딩 렌더링
ProjectFieldView->>CreateSecretFeature: 프로젝트 생성 액션 전달
CreateSecretFeature->>CreateProjectView: 생성 시트 표시
CreateProjectView-->>CreateSecretFeature: 생성된 프로젝트 delegate 전달
CreateSecretFeature->>CreateSecretView: availableProjects 갱신
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (10)
Projects/DVDesign/Sources/Components/DVFlowLayout.swift (1)
16-17: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
hSpacing/vSpacing을private로 좁히는 게 좋겠습니다.외부에서 읽을 필요가 없는 내부 상태인데
internal로 열려 있네요. 캡슐화 관점에서private로 제한하는 게 안전합니다.♻️ 제안
- let hSpacing: CGFloat - let vSpacing: CGFloat + private let hSpacing: CGFloat + private let vSpacing: CGFloat🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Projects/DVDesign/Sources/Components/DVFlowLayout.swift` around lines 16 - 17, Update the hSpacing and vSpacing properties in DVFlowLayout to use private access control instead of the default internal visibility, preserving their existing types and behavior.Source: Path instructions
Projects/DVDesign/Sources/Components/DVMultiSelectDropdown.swift (3)
363-369: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
toggle(_:)가FlatListView와SectionedListView에 그대로 중복돼 있습니다.동일한 selection insert/remove 로직이 두 곳에 복사돼 있어 나중에 한쪽만 고치는 실수가 생기기 쉽습니다. 공용 헬퍼로 빼시죠.
♻️ 제안
private func toggleSelection<ID: Hashable>(_ id: ID, in selection: inout Set<ID>) { if selection.contains(id) { selection.remove(id) } else { selection.insert(id) } }- private func toggle(_ id: Item.ID) { - if selection.contains(id) { - selection.remove(id) - } else { - selection.insert(id) - } - } + private func toggle(_ id: Item.ID) { + toggleSelection(id, in: &selection) + }(양쪽
toggle모두 동일하게 적용)Also applies to: 420-426
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Projects/DVDesign/Sources/Components/DVMultiSelectDropdown.swift` around lines 363 - 369, Extract the duplicated selection toggle logic from FlatListView.toggle(_:) and SectionedListView.toggle(_:) into one shared generic helper accepting an ID and inout Set<ID>. Update both toggle methods to delegate to that helper while preserving the existing insert/remove behavior.
251-314: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
PopoverContentView(64줄)/SectionedListView(56줄)는 50줄을 넘고,RowView는 2곳 이상에서 재사용됩니다.경로 가이드라인상 이 조건들은 별도 View 파일 분리 대상입니다. 지금 당장 급한 건 아니지만, 파일이 계속 커지면 가독성이 떨어질 수 있어요.
As per path instructions, "동일한 뷰가 2곳 이상 사용되거나, 독립적인 State/로직을 갖거나, 50줄 이상이면 별도 View 파일 분리를 제안하세요."
Also applies to: 372-427, 431-468
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Projects/DVDesign/Sources/Components/DVMultiSelectDropdown.swift` around lines 251 - 314, 분리 대상인 PopoverContentView, SectionedListView, 그리고 여러 위치에서 재사용되는 RowView를 각각 별도 View 파일로 이동하세요. 각 뷰의 현재 입력값, 바인딩, 상태 및 렌더링 동작은 유지하고, 호출부에서는 새 파일의 타입을 참조하도록 정리하세요.Source: Path instructions
10-10: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
fileprivate→private로 교체하세요.top-level 선언이라
private와fileprivate가 동일하게 동작합니다. SwiftLint도 동일하게 지적하고 있습니다.♻️ 제안
-fileprivate enum DropdownMetrics { +private enum DropdownMetrics {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Projects/DVDesign/Sources/Components/DVMultiSelectDropdown.swift` at line 10, Change the top-level DropdownMetrics declaration from fileprivate to private, preserving its current behavior and scope.Source: Linters/SAST tools
Projects/DVPresentation/Sources/Features/CreateSecret/Components/Fields/ExpireDateFieldView.swift (1)
18-33: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value본문 레이아웃을 private 서브뷰로 분리하세요.
body의 조건부HStack을private var로 추출하면 화면 조립과 상태 전환 로직이 분리됩니다.개선 예시
var body: some View { DVLabeledField(.module("Expire Date"), size: size) { + fieldContent + } +} + +private var fieldContent: some View { HStack(spacing: 6) { Group { if isSet { stepperPicker } else { activateButton } } .frame(maxWidth: .infinity, alignment: .leading) trashButton } .frame(width: size.width) - } }As per path instructions, “
var body안에 중첩 레이아웃이 직접 구현되어 있으면extension의private var/func로 분리를 제안하세요.”🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Projects/DVPresentation/Sources/Features/CreateSecret/Components/Fields/ExpireDateFieldView.swift` around lines 18 - 33, Extract the conditional HStack layout from body into a private computed subview in an extension, such as a private var that contains the isSet switch between stepperPicker and activateButton plus trashButton. Keep the existing sizing, spacing, alignment, and DVLabeledField composition unchanged, and have body use the extracted subview.Source: Path instructions
Projects/DVPresentation/Tests/Features/CreateSecret/CreateSecretFeatureTests.swift (1)
104-105: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value테스트 함수명이 검증 내용과 어긋납니다.
이제 name과 value 둘 다 비어 있는 케이스를 검증하므로
didTapSave_missingName→didTapSave_missingNameAndValue정도가 정확합니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Projects/DVPresentation/Tests/Features/CreateSecret/CreateSecretFeatureTests.swift` around lines 104 - 105, Rename the test method didTapSave_missingName to didTapSave_missingNameAndValue so its name accurately reflects that it verifies both name and value are empty, without changing the test behavior.Projects/DVPresentation/Sources/Features/CreateSecret/Components/Sections/APIKeysTokenSectionView.swift (1)
150-157: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win프리뷰
@State프로퍼티에private를 붙여주세요.SwiftLint
private_swiftui_state경고 8건이 그대로 남습니다. 프리뷰 전용 상태이므로 외부 노출이 필요 없습니다.expireDate의= nil도 불필요합니다.♻️ 제안 수정
- `@State` var name: String - `@State` var value: String - `@State` var authorityScope: String = "" - `@State` var projectIds: [Project.ID] - `@State` var service: String - `@State` var expireDate: Date? = nil - `@State` var environment: SecretEnvironment = .staging - `@State` var memo: String = "" + `@State` private var name: String + `@State` private var value: String + `@State` private var authorityScope: String = "" + `@State` private var projectIds: [Project.ID] + `@State` private var service: String + `@State` private var expireDate: Date? + `@State` private var environment: SecretEnvironment = .staging + `@State` private var memo: String = ""접근 제어가 가능한 가장 엄격한 수준인지 확인하라는 path instructions 기준입니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Projects/DVPresentation/Sources/Features/CreateSecret/Components/Sections/APIKeysTokenSectionView.swift` around lines 150 - 157, Update the preview state properties in the relevant view by marking each `@State` property private, including name, value, authorityScope, projectIds, service, expireDate, environment, and memo. Remove the redundant = nil initializer from expireDate while preserving the existing defaults for the other properties.Sources: Path instructions, Linters/SAST tools
Projects/DVPresentation/Sources/Features/CreateSecret/CreateSecretView.swift (2)
527-544: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value프리뷰의 PEM 헤더 문자열이 시크릿 스캐너에 high로 잡힙니다.
실제 키가 아닌 플레이스홀더지만 Betterleaks가 private-key로 오탐합니다.
"<preview-private-key>"같은 중립 문자열로 바꾸거나 스캐너 예외를 등록해 CI 소음을 줄이는 편이 좋습니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Projects/DVPresentation/Sources/Features/CreateSecret/CreateSecretView.swift` around lines 527 - 544, Update the preview SSH and SSL/TLS certificate fixture values in CreateSecretView so PEM header strings are replaced with neutral placeholder text such as "<preview-private-key>" and "<preview-certificate>". Preserve the fixture structure and non-sensitive example fields while removing scanner-triggering private-key and certificate markers.Source: Linters/SAST tools
122-159: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsubtype 불일치 시
EmptyView로 무음 처리되는 경로에 최소한의 흔적을 남기세요.세 분기 모두
default: EmptyView()라서 예상 밖 subtype이 들어오면 사용자는 헤더/푸터만 있는 빈 폼을 보고 원인을 알 수 없습니다.Log.warn한 줄 또는 placeholder 텍스트를 두면 디버깅이 훨씬 수월합니다. (init에서availableSubTypes.first가 보장되므로 현재는 도달 불가에 가깝지만, subtype 추가 시 회귀 지점입니다.)Also applies to: 163-195, 199-236
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Projects/DVPresentation/Sources/Features/CreateSecret/CreateSecretView.swift` around lines 122 - 159, Add a minimal diagnostic for unexpected values in the default branches of etcSection and the corresponding subtype switch sections, replacing silent EmptyView handling with a Log.warn call or visible placeholder text while preserving the existing behavior. Ensure all three default paths leave a trace when an unsupported selectedSubType is encountered.Projects/DVPresentation/Sources/Features/CreateSecret/Model/SecretMetaFields+Mapping.swift (1)
10-13: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
isValid주석을 현재 구현과 맞춰 갱신하세요.
isValid는missingRequiredFieldIDs에 의존하지 않는 사라진 검증 진입점입니다. 주석을 이 프로퍼티와toCreateSecretPayload가 공유하는 “단일 진실” required 규칙 정의부라고만 수정하면 됩니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Projects/DVPresentation/Sources/Features/CreateSecret/Model/SecretMetaFields`+Mapping.swift around lines 10 - 13, Update the documentation above missingRequiredFieldIDs to state only that this property is the single source of truth for the required-field rules shared by the property and toCreateSecretPayload; remove the claim that isValid shares or depends on it.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@Projects/DVPresentation/Sources/Features/CreateSecret/Components/AdaptiveFieldRow.swift`:
- Around line 45-90: The dual layout gap and threshold calculation use
inconsistent values. Keep the 16-point spacing in AdaptiveFieldRow’s dual
HStack, and update FormLayoutMode.dualThreshold’s comment and constant
calculation to use the actual 16-point column gap; apply the corresponding
change at AdaptiveFieldRow.swift lines 45-90 and FormLayoutMode.swift lines
6-12.
In
`@Projects/DVPresentation/Sources/Features/CreateSecret/Components/Fields/ExpireDateFieldView.swift`:
- Around line 44-60: Update the activateButton label in ExpireDateFieldView to
use a clear, grammatically correct localized string such as “Set expiration
date,” reflecting that tapping it assigns the default expiration date via
Self.defaultInitialDate().
In
`@Projects/DVPresentation/Sources/Features/CreateSecret/Components/Sections/OAuthClientSectionView.swift`:
- Around line 43-59: Update the Client ID LabeledTextFieldView in
OAuthClientSectionView to use hintFor(.clientId) for trailingHint, matching the
existing Client Secret field and other SectionView implementations so
detected-service fallback and validation messaging are handled consistently.
In
`@Projects/DVPresentation/Sources/Features/CreateSecret/CreateSecretFeature.swift`:
- Around line 243-248: Update the .failure(.missingRequired(let fieldIDs))
branch in CreateSecretFeature to clear state.validationErrors before assigning
errors for the currently missing fieldIDs, matching the reset behavior used by
the success path while preserving the existing Required assignments.
- Around line 215-228: Update the DEBUG-only Log.info block in
CreateSecretFeature to stop interpolating the full draft and payload, which may
contain plaintext secrets. Log only non-sensitive debugging metadata such as
secret type, subtype, field count, and value lengths, while preserving the
existing request-preparation context and project IDs.
In
`@Projects/DVPresentation/Sources/Features/CreateSecret/Model/SecretMetaFields`+Mapping.swift:
- Around line 13-40: Update missingRequiredFieldIDs to treat whitespace-only
strings as missing by reusing a shared String blank-check helper such as
isBlank. Apply the helper consistently to every required field across all
content cases, including names and secret values, while preserving the existing
FieldID results.
---
Nitpick comments:
In `@Projects/DVDesign/Sources/Components/DVFlowLayout.swift`:
- Around line 16-17: Update the hSpacing and vSpacing properties in DVFlowLayout
to use private access control instead of the default internal visibility,
preserving their existing types and behavior.
In `@Projects/DVDesign/Sources/Components/DVMultiSelectDropdown.swift`:
- Around line 363-369: Extract the duplicated selection toggle logic from
FlatListView.toggle(_:) and SectionedListView.toggle(_:) into one shared generic
helper accepting an ID and inout Set<ID>. Update both toggle methods to delegate
to that helper while preserving the existing insert/remove behavior.
- Around line 251-314: 분리 대상인 PopoverContentView, SectionedListView, 그리고 여러 위치에서
재사용되는 RowView를 각각 별도 View 파일로 이동하세요. 각 뷰의 현재 입력값, 바인딩, 상태 및 렌더링 동작은 유지하고, 호출부에서는
새 파일의 타입을 참조하도록 정리하세요.
- Line 10: Change the top-level DropdownMetrics declaration from fileprivate to
private, preserving its current behavior and scope.
In
`@Projects/DVPresentation/Sources/Features/CreateSecret/Components/Fields/ExpireDateFieldView.swift`:
- Around line 18-33: Extract the conditional HStack layout from body into a
private computed subview in an extension, such as a private var that contains
the isSet switch between stepperPicker and activateButton plus trashButton. Keep
the existing sizing, spacing, alignment, and DVLabeledField composition
unchanged, and have body use the extracted subview.
In
`@Projects/DVPresentation/Sources/Features/CreateSecret/Components/Sections/APIKeysTokenSectionView.swift`:
- Around line 150-157: Update the preview state properties in the relevant view
by marking each `@State` property private, including name, value, authorityScope,
projectIds, service, expireDate, environment, and memo. Remove the redundant =
nil initializer from expireDate while preserving the existing defaults for the
other properties.
In
`@Projects/DVPresentation/Sources/Features/CreateSecret/CreateSecretView.swift`:
- Around line 527-544: Update the preview SSH and SSL/TLS certificate fixture
values in CreateSecretView so PEM header strings are replaced with neutral
placeholder text such as "<preview-private-key>" and "<preview-certificate>".
Preserve the fixture structure and non-sensitive example fields while removing
scanner-triggering private-key and certificate markers.
- Around line 122-159: Add a minimal diagnostic for unexpected values in the
default branches of etcSection and the corresponding subtype switch sections,
replacing silent EmptyView handling with a Log.warn call or visible placeholder
text while preserving the existing behavior. Ensure all three default paths
leave a trace when an unsupported selectedSubType is encountered.
In
`@Projects/DVPresentation/Sources/Features/CreateSecret/Model/SecretMetaFields`+Mapping.swift:
- Around line 10-13: Update the documentation above missingRequiredFieldIDs to
state only that this property is the single source of truth for the
required-field rules shared by the property and toCreateSecretPayload; remove
the claim that isValid shares or depends on it.
In
`@Projects/DVPresentation/Tests/Features/CreateSecret/CreateSecretFeatureTests.swift`:
- Around line 104-105: Rename the test method didTapSave_missingName to
didTapSave_missingNameAndValue so its name accurately reflects that it verifies
both name and value are empty, without changing the test behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: e4fb9e3e-2975-4ccc-a6ed-86cd983e96a5
📒 Files selected for processing (37)
Projects/DVDesign/Sources/Components/DVButton.swiftProjects/DVDesign/Sources/Components/DVChipsField.swiftProjects/DVDesign/Sources/Components/DVFlowLayout.swiftProjects/DVDesign/Sources/Components/DVMultiSelectDropdown.swiftProjects/DVPresentation/Resources/Localizable.xcstringsProjects/DVPresentation/Sources/Features/CreateSecret/Components/AdaptiveFieldRow.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Components/CreateSecretHeaderView.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Components/Fields/EnvironmentFieldView.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Components/Fields/ExpireDateFieldView.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Components/Fields/LabeledTextFieldView.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Components/Fields/LicenseTierFieldView.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Components/Fields/MemoFieldView.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Components/Fields/NameFieldView.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Components/Fields/ProjectFieldView.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Components/Fields/SSLRequiredFieldView.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Components/Fields/ServicesFieldView.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Components/FooterActionsView.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Components/FormLayoutMode.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Components/Sections/APIKeysTokenSectionView.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Components/Sections/CreateSecretSectionHintProviding.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Components/Sections/CustomSectionView.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Components/Sections/DatabaseSectionView.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Components/Sections/EnvSetSectionView.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Components/Sections/FormSectionScaffold.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Components/Sections/LicenseKeySectionView.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Components/Sections/OAuthClientSectionView.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Components/Sections/SSHKeySectionView.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Components/Sections/SSLTLSCertSectionView.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Components/Sections/ServiceAccountSectionView.swiftProjects/DVPresentation/Sources/Features/CreateSecret/CreateSecretFeature.swiftProjects/DVPresentation/Sources/Features/CreateSecret/CreateSecretView.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Model/FormError.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Model/SecretContentFields.swiftProjects/DVPresentation/Sources/Features/CreateSecret/Model/SecretMetaFields+Mapping.swiftProjects/DVPresentation/Sources/Localization/String+Module.swiftProjects/DVPresentation/Sources/Support/Binding+CasePathable.swiftProjects/DVPresentation/Tests/Features/CreateSecret/CreateSecretFeatureTests.swift
💤 Files with no reviewable changes (1)
- Projects/DVPresentation/Resources/Localizable.xcstrings
|
|
|
새 프로젝트 만들어도 자동 선택 안 됨 @yeseonglee |
- validationErrors 재-Save 시 리셋 (이전 warning 잔존 방지) + 회귀 테스트 신규 - String.isBlank 확장 — 공백만 입력한 필수 필드 검증 통과 방지 - OAuthClient Client ID trailingHint를 hintFor 프로토콜로 통합 — detected 값 누출 방지 - FormLayoutMode.dualThreshold 828 → 816 (spacing 16 반영 재계산) - ExpireDate "No expired" → "No expiration"
저는 API Key / Access Token / Webhook Secret은 폼 UI가 같아 보여도 각 서브타입에서 value가 갖는 의미 자체가 다르기 때문에, 탭 전환 시 초기화되는 게 맞는 동작이라고 생각합니다. |
dlguszoo
left a comment
There was a problem hiding this comment.
와.. 다중 선택 드롭다운.... 수고하셨습니다!
✨ What’s this PR?
📌 관련 이슈 (Related Issue)
🧶 주요 변경 내용 (Summary)
DVDesign 컴포넌트 확장
DVMultiSelectDropdown— Figma 9 variants(chips trigger · sectioned/flat body · search · create footer · empty states) 커버DVFlowLayout—DVChipFlow를 공용으로 승격, DVMultiSelectDropdown이 재사용DVButton.secondaryProminent— Save/Create 확정 액션 스타일 (vaultGreen 강조). width→minWidth로 로컬라이제이션 대응CreateSecret 폼 조립 (9 subtype SectionView)
APIKeysTokenSectionView(apiKey/accessToken/webhookSecret 3 subtype 공유)OAuthClientSectionView,ServiceAccountSectionViewDatabaseSectionViewSSHKeySectionView,SSLTLSCertSectionViewEnvSetSectionViewLicenseKeySectionView,CustomSectionViewFormSectionScaffold(Name/Memo bookend + 16pt spacing + maxWidth infinity 강제) 위에서 type-specific rows만 구성핵심 필드 컴포넌트 · Reducer 라우팅
ProjectFieldView—DVMultiSelectDropdown소비,[Project.ID]↔Set<Project.ID>어댑터CreateProject시트 라우팅 —didTapCreateProject→.createProjectPresentationAction, delegate 수신 시availableProjects갱신ExpireDateFieldView재설계 — No expired 트리거(gray300 컨테이너) + 인라인.stepperFieldDatePicker + trash 버튼반응형 레이아웃 wiring
CreateSecretView.body에GeometryReader+.environment(\.formLayoutMode, ...)주입 — dual↔single 자동 전환AdaptiveFieldRow가 left/right 슬롯 optional 지원 (Service Account의 Environment 미노출 케이스)CreateSecretView최소 폭 520pt — 헤더 3-radio 자연 폭 floor유틸리티 · 리팩터
Binding+CasePathable.typed(_:default:)— CasePathable enum case 바인딩 헬퍼 (전 SectionView가 소비)FormSlotSize—LabeledTextFieldView.SizeModeleak 제거CreateSecretSectionHintProviding— hintFor 중복 프로토콜 default로 통합APIKeyTokenFields.scope→.authorityScope(TCAScope와 dynamic member lookup 충돌 회피).module(_:)일괄 로컬라이즈 (신규String.module헬퍼)검증 UX
FormError.missingRequired([FieldID]))#if DEBUG가드🧪 테스트 / 검증 내역
+→ CreateSecret 진입 (Composition Root wiring은 후속 PR-확인 후 관련 코드 삭제 처리)💬 기타 공유 사항
saveResponse실패 사용자 노출 (alert / inline)detectedServices감지 엔진 연결DVMultiSelectDropdownchip trigger "+N" overflow indicator🙇🏻♀️ 리뷰 가이드
CreateSecretView.body— GeometryReader 기반 반응형 wiring + 최소 너비 제약DVButton.swift—.secondaryProminent신규 + width/height → minWidth/minHeight 리팩터 (기존 소비처 회귀 확인 필요)Summary by CodeRabbit
새 기능
개선