kiwi/frontend/src/stores/community.ts
pyr0ball 878a9a268c fix: community module integration fixes -- slots payload + ForkResult type
- PublishPayload gains optional slots field; PublishPlanModal maps
  plan.slots into the payload so the backend can compute element
  snapshot scores (seasoning, richness, etc.) from actual recipes
- plan-forked emit type updated to ForkResult across CommunityFeedPanel
  and RecipesView so forked_from is preserved for future navigation
2026-04-13 14:21:33 -07:00

119 lines
2.9 KiB
TypeScript

/**
* Community Store
*
* Manages community post feed state and fork actions using Pinia.
* Follows the composition store pattern established in recipes.ts.
*/
import { defineStore } from 'pinia'
import { ref } from 'vue'
import api from '../services/api'
// ========== Types ==========
export interface CommunityPostSlot {
day: string
meal_type: string
recipe_id: number
}
export interface ElementProfiles {
seasoning_score: number | null
richness_score: number | null
brightness_score: number | null
depth_score: number | null
aroma_score: number | null
structure_score: number | null
texture_profile: string | null
}
export interface CommunityPost {
slug: string
pseudonym: string
post_type: 'plan' | 'recipe_success' | 'recipe_blooper'
published: string
title: string
description: string | null
photo_url: string | null
slots: CommunityPostSlot[]
recipe_id: number | null
recipe_name: string | null
level: number | null
outcome_notes: string | null
element_profiles: ElementProfiles
dietary_tags: string[]
allergen_flags: string[]
flavor_molecules: string[]
fat_pct: number | null
protein_pct: number | null
moisture_pct: number | null
}
export interface ForkResult {
plan_id: number
week_start: string
forked_from: string
}
export interface PublishPayload {
post_type: 'plan' | 'recipe_success' | 'recipe_blooper'
title: string
description?: string
pseudonym_name?: string
plan_id?: number
recipe_id?: number
outcome_notes?: string
slots?: CommunityPostSlot[]
}
export interface PublishResult {
slug: string
}
// ========== Store ==========
export const useCommunityStore = defineStore('community', () => {
const posts = ref<CommunityPost[]>([])
const loading = ref(false)
const error = ref<string | null>(null)
const currentFilter = ref<string | null>(null)
async function fetchPosts(postType?: string) {
loading.value = true
error.value = null
currentFilter.value = postType ?? null
try {
const params: Record<string, string | number> = { page: 1, page_size: 40 }
if (postType) {
params.post_type = postType
}
const response = await api.get<{ posts: CommunityPost[] }>('/community/posts', { params })
posts.value = response.data.posts
} catch (err: unknown) {
error.value = err instanceof Error ? err.message : 'Could not load community posts.'
} finally {
loading.value = false
}
}
async function forkPost(slug: string): Promise<ForkResult> {
const response = await api.post<ForkResult>(`/community/posts/${slug}/fork`)
return response.data
}
async function publishPost(payload: PublishPayload): Promise<PublishResult> {
const response = await api.post<PublishResult>('/community/posts', payload)
return response.data
}
return {
posts,
loading,
error,
currentFilter,
fetchPosts,
forkPost,
publishPost,
}
})