From d52bfe2e27b3fd4adf2fc265d49c243749e8b341 Mon Sep 17 00:00:00 2001 From: Michael Chihlas Date: Tue, 3 Feb 2026 19:05:05 -0500 Subject: [PATCH] feat: Add Step Library API foundation (Phase 1: B.1-B.3, B.7) Implements foundational types and API clients for Step Library: Task B.3 - TypeScript Types: - Created types/step.ts with comprehensive interfaces - Step, StepListItem, StepCategory types - StepContent with instructions, help_text, commands - StepListParams for filtering/sorting - Rating and Review types - StepCreate/StepUpdate DTOs Task B.1 - Steps API Client: - Created api/steps.ts following existing patterns - CRUD operations (list, get, create, update, delete) - Search endpoint with query - Popular tags endpoint - Rating/review operations (rate, updateRating, deleteRating, getReviews) Task B.2 - Step Categories API Client: - Created api/stepCategories.ts - List and get operations for categories Task B.7 - Update API Index: - Exported stepsApi and stepCategoriesApi - Available for import from '@/api' Phase 1 foundation complete. Ready for Phase 2 (UI components). Build tested successfully. Related: Issue #10 Co-Authored-By: Claude Sonnet 4.5 --- frontend/src/api/index.ts | 2 + frontend/src/api/stepCategories.ts | 16 ++++ frontend/src/api/steps.ts | 72 +++++++++++++++++ frontend/src/types/step.ts | 124 +++++++++++++++++++++++++++++ 4 files changed, 214 insertions(+) create mode 100644 frontend/src/api/stepCategories.ts create mode 100644 frontend/src/api/steps.ts create mode 100644 frontend/src/types/step.ts diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts index 7f4a72ca..267fafa1 100644 --- a/frontend/src/api/index.ts +++ b/frontend/src/api/index.ts @@ -6,3 +6,5 @@ export { default as inviteApi } from './invite' export { default as tagsApi } from './tags' export { default as categoriesApi } from './categories' export { default as foldersApi } from './folders' +export { default as stepsApi } from './steps' +export { default as stepCategoriesApi } from './stepCategories' diff --git a/frontend/src/api/stepCategories.ts b/frontend/src/api/stepCategories.ts new file mode 100644 index 00000000..3d6cc471 --- /dev/null +++ b/frontend/src/api/stepCategories.ts @@ -0,0 +1,16 @@ +import apiClient from './client' +import type { StepCategory } from '@/types/step' + +export const stepCategoriesApi = { + async list(): Promise { + const response = await apiClient.get('/step-categories') + return response.data + }, + + async get(id: string): Promise { + const response = await apiClient.get(`/step-categories/${id}`) + return response.data + } +} + +export default stepCategoriesApi diff --git a/frontend/src/api/steps.ts b/frontend/src/api/steps.ts new file mode 100644 index 00000000..4b63762e --- /dev/null +++ b/frontend/src/api/steps.ts @@ -0,0 +1,72 @@ +import apiClient from './client' +import type { + Step, + StepListItem, + StepCreate, + StepUpdate, + StepListParams, + PopularTag, + RatingCreate, + RatingUpdate, + Rating, + Review +} from '@/types/step' + +export const stepsApi = { + async list(params?: StepListParams): Promise { + const response = await apiClient.get('/steps', { params }) + return response.data + }, + + async get(id: string): Promise { + const response = await apiClient.get(`/steps/${id}`) + return response.data + }, + + async create(data: StepCreate): Promise { + const response = await apiClient.post('/steps', data) + return response.data + }, + + async update(id: string, data: StepUpdate): Promise { + const response = await apiClient.put(`/steps/${id}`, data) + return response.data + }, + + async delete(id: string): Promise { + await apiClient.delete(`/steps/${id}`) + }, + + async search(query: string): Promise { + const response = await apiClient.get('/steps/search', { + params: { q: query } + }) + return response.data + }, + + async getPopularTags(): Promise { + const response = await apiClient.get('/steps/popular-tags') + return response.data + }, + + async rate(id: string, data: RatingCreate): Promise { + const response = await apiClient.post(`/steps/${id}/ratings`, data) + return response.data + }, + + async updateRating(id: string, data: RatingUpdate): Promise { + const response = await apiClient.put(`/steps/${id}/ratings`, data) + return response.data + }, + + async deleteRating(id: string): Promise { + await apiClient.delete(`/steps/${id}/ratings`) + }, + + async getReviews(id: string): Promise { + const response = await apiClient.get(`/steps/${id}/reviews`) + return response.data + } +} + +export default stepsApi diff --git a/frontend/src/types/step.ts b/frontend/src/types/step.ts new file mode 100644 index 00000000..01dc45e7 --- /dev/null +++ b/frontend/src/types/step.ts @@ -0,0 +1,124 @@ +// Step Library Types + +export interface StepCommand { + label: string + command: string + command_type?: string +} + +export interface StepContent { + instructions: string + help_text?: string + commands?: StepCommand[] +} + +export interface Step { + id: string + title: string + step_type: 'decision' | 'action' | 'solution' + content: StepContent + visibility: 'private' | 'team' | 'public' + category_id?: string + category_name?: string + tags: string[] + usage_count: number + rating_average: number + rating_count: number + helpful_yes: number + helpful_no: number + is_featured: boolean + is_verified: boolean + created_by: string + author_name?: string + created_at: string + updated_at: string +} + +export interface StepListItem { + id: string + title: string + step_type: string + visibility: string + category_id?: string + category_name?: string + tags: string[] + usage_count: number + rating_average: number + rating_count: number + is_featured: boolean + created_by: string + author_name?: string + created_at: string +} + +export interface StepCategory { + id: string + name: string + description?: string + display_order: number + team_id?: string + is_active: boolean +} + +export interface StepListParams { + visibility?: 'private' | 'team' | 'public' + category_id?: string + tags?: string[] + min_rating?: number + step_type?: 'decision' | 'action' | 'solution' + sort_by?: 'recent' | 'popular' | 'highest_rated' | 'most_used' + limit?: number + offset?: number +} + +export interface PopularTag { + tag: string + count: number +} + +export interface StepCreate { + title: string + step_type: 'decision' | 'action' | 'solution' + content: StepContent + visibility: 'private' | 'team' | 'public' + category_id?: string + tags?: string[] +} + +export interface StepUpdate { + title?: string + step_type?: 'decision' | 'action' | 'solution' + content?: StepContent + visibility?: 'private' | 'team' | 'public' + category_id?: string + tags?: string[] +} + +export interface RatingCreate { + rating: number + review_text?: string + verified_use: boolean +} + +export interface RatingUpdate { + rating?: number + review_text?: string +} + +export interface Rating { + id: string + step_id: string + user_id: string + rating: number + review_text?: string + verified_use: boolean + helpful_yes: number + helpful_no: number + created_at: string + updated_at: string + user_name?: string +} + +export interface Review extends Rating { + // Same as Rating, just an alias for clarity +}