fix: phase 1 refactor
This commit is contained in:
@@ -7,7 +7,7 @@ Scope: Reduce high-impact code duplication in production and test-adjacent areas
|
|||||||
|
|
||||||
| Phase | Area | Status | Notes |
|
| Phase | Area | Status | Notes |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| 1 | Electron API contract | ⚠️ Partial | Shared contract exists, but overlapping renderer store data interfaces remain. |
|
| 1 | Electron API contract | ✅ Completed | Shared contract now covers renderer store data interfaces too. |
|
||||||
| 2 | Tag mutation workflow | ⚠️ Partial | Helpers exist; duplicate blocks still present in task flows. |
|
| 2 | Tag mutation workflow | ⚠️ Partial | Helpers exist; duplicate blocks still present in task flows. |
|
||||||
| 3 | Post-media single/batch ops | ⚠️ Partial | Core primitives extracted; some duplicate link/unlink logic remains. |
|
| 3 | Post-media single/batch ops | ⚠️ Partial | Core primitives extracted; some duplicate link/unlink logic remains. |
|
||||||
| 4 | Project mapping + delete guards | ✅ Completed | Guard + mapper extracted and used in target methods. |
|
| 4 | Project mapping + delete guards | ✅ Completed | Guard + mapper extracted and used in target methods. |
|
||||||
@@ -39,7 +39,7 @@ Scope: Reduce high-impact code duplication in production and test-adjacent areas
|
|||||||
|
|
||||||
## Phase 1 — Consolidate Electron API Contract (Highest ROI)
|
## Phase 1 — Consolidate Electron API Contract (Highest ROI)
|
||||||
|
|
||||||
Status: ⚠️ Partially complete
|
Status: ✅ Completed
|
||||||
|
|
||||||
### Problem
|
### Problem
|
||||||
`electronAPI` shape is duplicated across multiple files and can drift:
|
`electronAPI` shape is duplicated across multiple files and can drift:
|
||||||
@@ -59,7 +59,7 @@ Create one canonical API contract and consume it from both preload and renderer
|
|||||||
|
|
||||||
### Progress Check
|
### Progress Check
|
||||||
- Completed: shared contract module exists and is imported by preload and renderer ambient types.
|
- Completed: shared contract module exists and is imported by preload and renderer ambient types.
|
||||||
- Remaining: reduce overlapping data-shape duplication between shared API data interfaces and `src/renderer/store/appStore.ts` interfaces.
|
- Completed: overlapping data-shape duplication was removed by sourcing store data interfaces from shared contract types.
|
||||||
|
|
||||||
### Acceptance Criteria
|
### Acceptance Criteria
|
||||||
- Single source of truth for `electronAPI` contract shape.
|
- Single source of truth for `electronAPI` contract shape.
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { persist } from 'zustand/middleware';
|
import { persist } from 'zustand/middleware';
|
||||||
import type { DeleteReference, ConfirmDeleteDetails } from '../components/ConfirmDeleteModal';
|
import type { DeleteReference, ConfirmDeleteDetails } from '../components/ConfirmDeleteModal';
|
||||||
|
import type {
|
||||||
|
ProjectData,
|
||||||
|
PostData,
|
||||||
|
MediaData,
|
||||||
|
TaskProgress,
|
||||||
|
} from '../../main/shared/electronApi';
|
||||||
|
|
||||||
// Storage key for persisted state
|
// Storage key for persisted state
|
||||||
const STORAGE_KEY = 'bds-app-state';
|
const STORAGE_KEY = 'bds-app-state';
|
||||||
@@ -20,58 +26,7 @@ export interface TabState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
export interface ProjectData {
|
export type { ProjectData, PostData, MediaData, TaskProgress };
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
slug: string;
|
|
||||||
description?: string;
|
|
||||||
dataPath?: string;
|
|
||||||
isActive: boolean;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PostData {
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
slug: string;
|
|
||||||
excerpt?: string;
|
|
||||||
content: string;
|
|
||||||
status: 'draft' | 'published' | 'archived';
|
|
||||||
author?: string;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
publishedAt?: string;
|
|
||||||
tags: string[];
|
|
||||||
categories: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MediaData {
|
|
||||||
id: string;
|
|
||||||
filename: string;
|
|
||||||
originalName: string;
|
|
||||||
mimeType: string;
|
|
||||||
size: number;
|
|
||||||
width?: number;
|
|
||||||
height?: number;
|
|
||||||
title?: string;
|
|
||||||
alt?: string;
|
|
||||||
caption?: string;
|
|
||||||
author?: string;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
tags: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TaskProgress {
|
|
||||||
taskId: string;
|
|
||||||
status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
|
|
||||||
progress: number;
|
|
||||||
message: string;
|
|
||||||
startTime: string;
|
|
||||||
endTime?: string;
|
|
||||||
error?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ErrorDetails {
|
export interface ErrorDetails {
|
||||||
message: string;
|
message: string;
|
||||||
|
|||||||
@@ -3,12 +3,19 @@
|
|||||||
* Validates state management behavior for posts, dirty tracking, and UI state
|
* Validates state management behavior for posts, dirty tracking, and UI state
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { describe, it, expect, beforeEach } from 'vitest';
|
import { describe, it, expect, expectTypeOf, beforeEach } from 'vitest';
|
||||||
import { useAppStore, PostData } from '../../../src/renderer/store/appStore';
|
import { useAppStore, ProjectData, PostData, MediaData, TaskProgress } from '../../../src/renderer/store/appStore';
|
||||||
|
import type {
|
||||||
|
ProjectData as SharedProjectData,
|
||||||
|
PostData as SharedPostData,
|
||||||
|
MediaData as SharedMediaData,
|
||||||
|
TaskProgress as SharedTaskProgress,
|
||||||
|
} from '../../../src/main/shared/electronApi';
|
||||||
|
|
||||||
// Helper to create a mock post
|
// Helper to create a mock post
|
||||||
const createMockPost = (overrides: Partial<PostData> = {}): PostData => ({
|
const createMockPost = (overrides: Partial<PostData> = {}): PostData => ({
|
||||||
id: `post-${Date.now()}-${Math.random().toString(36).substring(7)}`,
|
id: `post-${Date.now()}-${Math.random().toString(36).substring(7)}`,
|
||||||
|
projectId: 'project-1',
|
||||||
title: 'Test Post',
|
title: 'Test Post',
|
||||||
slug: 'test-post',
|
slug: 'test-post',
|
||||||
content: '# Test Content',
|
content: '# Test Content',
|
||||||
@@ -160,4 +167,13 @@ describe('AppStore', () => {
|
|||||||
expect(getStore().preferredEditorMode).toBe('markdown');
|
expect(getStore().preferredEditorMode).toBe('markdown');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Type Contract', () => {
|
||||||
|
it('should keep store data types aligned with the shared Electron API contract', () => {
|
||||||
|
expectTypeOf<ProjectData>().toEqualTypeOf<SharedProjectData>();
|
||||||
|
expectTypeOf<PostData>().toEqualTypeOf<SharedPostData>();
|
||||||
|
expectTypeOf<MediaData>().toEqualTypeOf<SharedMediaData>();
|
||||||
|
expectTypeOf<TaskProgress>().toEqualTypeOf<SharedTaskProgress>();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { useAppStore, PostData, MediaData, Tab } from '../../../src/renderer/sto
|
|||||||
// Helper to create a mock post
|
// Helper to create a mock post
|
||||||
const createMockPost = (overrides: Partial<PostData> = {}): PostData => ({
|
const createMockPost = (overrides: Partial<PostData> = {}): PostData => ({
|
||||||
id: `post-${Date.now()}-${Math.random().toString(36).substring(7)}`,
|
id: `post-${Date.now()}-${Math.random().toString(36).substring(7)}`,
|
||||||
|
projectId: 'project-1',
|
||||||
title: 'Test Post',
|
title: 'Test Post',
|
||||||
slug: 'test-post',
|
slug: 'test-post',
|
||||||
content: '# Test Content',
|
content: '# Test Content',
|
||||||
@@ -23,6 +24,7 @@ const createMockPost = (overrides: Partial<PostData> = {}): PostData => ({
|
|||||||
// Helper to create a mock media
|
// Helper to create a mock media
|
||||||
const createMockMedia = (overrides: Partial<MediaData> = {}): MediaData => ({
|
const createMockMedia = (overrides: Partial<MediaData> = {}): MediaData => ({
|
||||||
id: `media-${Date.now()}-${Math.random().toString(36).substring(7)}`,
|
id: `media-${Date.now()}-${Math.random().toString(36).substring(7)}`,
|
||||||
|
projectId: 'project-1',
|
||||||
filename: 'test.jpg',
|
filename: 'test.jpg',
|
||||||
originalName: 'test.jpg',
|
originalName: 'test.jpg',
|
||||||
mimeType: 'image/jpeg',
|
mimeType: 'image/jpeg',
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import type { PostData } from '../../../src/renderer/store/appStore';
|
|||||||
function createTestPost(overrides: Partial<PostData>): PostData {
|
function createTestPost(overrides: Partial<PostData>): PostData {
|
||||||
return {
|
return {
|
||||||
id: 'test-id',
|
id: 'test-id',
|
||||||
|
projectId: 'project-1',
|
||||||
title: 'Test Post',
|
title: 'Test Post',
|
||||||
slug: 'test-post',
|
slug: 'test-post',
|
||||||
excerpt: 'Test excerpt',
|
excerpt: 'Test excerpt',
|
||||||
|
|||||||
Reference in New Issue
Block a user