diff --git a/CHANGELOG.md b/CHANGELOG.md index b353850..7d96121 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,42 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- **Secret Management Frontend - Phase 3: File Attachments UI Integration (Display)** (#200, Part of #191) - **MERGED 22.11.2025** + - `AttachmentUpload.tsx` component with drag-and-drop and file validation + - `AttachmentPreview.tsx` modal for images and PDFs with zoom controls + - Integration of `AttachmentList` component in `SecretDetail.tsx` + - Download, delete, and preview handlers with master key management + - File size limits (10MB) and type validation (images, PDFs, documents) + - Security: Blocks executable files, validates MIME types + - 21 new unit tests (547 total tests passing) + - Coverage: 87.95% (exceeds 80% requirement) + - Part of Secret Management Epic #191 (Phase 3/5) + +- **Secret Management Frontend - Phase 2: Secret Create/Edit Forms** (#198, Part of #191) - **MERGED 22.11.2025** + - `SecretForm.tsx` reusable form component with validation + - `SecretCreate.tsx` page for creating new secrets + - `SecretEdit.tsx` page for editing existing secrets + - API service extensions: `createSecret()`, `updateSecret()` + - Client-side validation and error handling + - Password show/hide toggle for security + - 28 new unit tests (all passing) + - Routing: `/secrets/new` and `/secrets/:id/edit` + - Part of Secret Management Epic #191 (Phase 2/5) + +- **Secret Management Frontend - Phase 1: Secret List & Detail Views** (#197, Part of #191) - **MERGED 22.11.2025** + - `SecretList.tsx` component with search, filtering, and pagination (20 items/page) + - `SecretDetail.tsx` full detail view with encrypted fields + - `SecretCard.tsx` reusable card component with badges + - API service: `secretApi.ts` with `getSecrets()`, `getSecretById()` + - Grid/List view toggle (persisted in localStorage) + - Filter by tags and expiration status + - Password show/hide toggle + - Tags, expiration badges, attachment count, shared indicator + - 31 new unit tests (450 total tests passing) + - Coverage: 87.95% (exceeds 80% requirement) + - Routing: `/secrets` and `/secrets/:id` + - Part of Secret Management Epic #191 (Phase 1/5) + - **Client-Side File Encryption - Phase 5: Security Audit & Documentation** (#174, Part of #143) - Comprehensive security documentation in `CRYPTO_ARCHITECTURE.md` - Security audit completed for all encryption code diff --git a/README.md b/README.md index 1818203..97ca205 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,50 @@ For comprehensive security details, threat model, and cryptographic guarantees, - ✅ Phase 2: ShareTarget Integration (PR #178, merged 19.11.2025) - ✅ Phase 3: Upload Integration (PR #187, merged 21.11.2025) - ✅ Phase 4: Download & Decryption (PR #188, merged 21.11.2025) -- 🔄 Phase 5: Security Audit & Documentation (PR #190, this PR) +- ✅ Phase 5: Security Audit & Documentation (PR #190, merged 22.11.2025) + +## 🔐 Secret Management (Password Vault UI) + +SecPal provides a comprehensive **password vault UI** with encrypted storage, file attachments, and sharing capabilities. + +**Features (Phases 1-3 Complete):** + +- 📋 **Secret List & Detail Views**: Browse, search, and filter secrets with grid/list toggle + - Search by title, filter by tags/expiration status + - Pagination (20 items/page) + - Password show/hide toggle + - Expiration badges, attachment count, shared indicator +- ✏️ **Create/Edit Forms**: Full CRUD operations with validation + - Title, username, password, URL, notes fields + - Client-side validation + - Loading states and error handling +- 📎 **File Attachments**: Upload, download, preview encrypted files + - Drag-and-drop upload with file validation (10MB max) + - Image preview with zoom controls (50%-200%) + - PDF preview in modal + - File type validation (images, PDFs, documents) + - Download with automatic decryption + +**Usage Example:** + +```tsx +import { SecretList } from "@/pages/Secrets/SecretList"; +import { SecretDetail } from "@/pages/Secrets/SecretDetail"; + +// List all secrets + + +// View secret details + +``` + +**Implementation Status:** + +- ✅ Phase 1: Secret List & Detail Views (PR #197, merged 22.11.2025) +- ✅ Phase 2: Secret Create/Edit Forms (PR #198, merged 22.11.2025) +- ✅ Phase 3: File Attachments UI Integration (PR #200, merged 22.11.2025) +- 🔜 Phase 4: Secret Sharing UI (Issue #195) +- 🔜 Phase 5: Offline Support & PWA Integration (Issue #196) ## 🌍 Internationalization (i18n) diff --git a/src/components/SecretForm.enhanced.test.tsx b/src/components/SecretForm.enhanced.test.tsx index 4b4b9d6..3c89abd 100644 --- a/src/components/SecretForm.enhanced.test.tsx +++ b/src/components/SecretForm.enhanced.test.tsx @@ -24,7 +24,9 @@ describe("SecretForm", () => { it("should generate password when generate button clicked", async () => { render(); - const passwordInput = screen.getByLabelText("Password", { selector: "input" }); + const passwordInput = screen.getByLabelText("Password", { + selector: "input", + }); const generateButton = screen.getByLabelText(/generate password/i); expect(passwordInput).toHaveValue(""); @@ -39,7 +41,9 @@ describe("SecretForm", () => { it("should show password strength indicator when password is entered", async () => { render(); - const passwordInput = screen.getByLabelText("Password", { selector: "input" }); + const passwordInput = screen.getByLabelText("Password", { + selector: "input", + }); // Type weak password await userEvent.type(passwordInput, "abc"); @@ -52,7 +56,9 @@ describe("SecretForm", () => { it("should update strength indicator as password improves", async () => { render(); - const passwordInput = screen.getByLabelText("Password", { selector: "input" }); + const passwordInput = screen.getByLabelText("Password", { + selector: "input", + }); // Type weak password await userEvent.type(passwordInput, "abc"); @@ -71,9 +77,9 @@ describe("SecretForm", () => { it("should toggle password visibility", async () => { render(); - const passwordInput = screen.getByLabelText( - "Password", { selector: "input" } - ) as HTMLInputElement; + const passwordInput = screen.getByLabelText("Password", { + selector: "input", + }) as HTMLInputElement; const toggleButton = screen.getByLabelText(/show password/i); expect(passwordInput.type).toBe("password"); @@ -233,7 +239,9 @@ describe("SecretForm", () => { render(); const titleInput = screen.getByLabelText(/title/i); - const passwordInput = screen.getByLabelText("Password", { selector: "input" }); + const passwordInput = screen.getByLabelText("Password", { + selector: "input", + }); const tagInput = screen.getByPlaceholderText(/enter tag name/i); const dateInput = screen.getByLabelText(/expiration date/i); const submitButton = screen.getByRole("button", { name: /save/i });