/** * Core feedback types โ€” Single source of truth for the @mana/feedback hub. * * Mana-analytics' Postgres enums (`feedback.feedback_category`, * `feedback.feedback_status`) MUST mirror these literal unions exactly. * If you add or rename a value here, also write a SQL migration under * services/mana-analytics/drizzle/. */ export type FeedbackCategory = | 'bug' | 'feature' | 'improvement' | 'question' | 'praise' | 'onboarding-wish' | 'other'; export type FeedbackStatus = | 'submitted' | 'under_review' | 'planned' | 'in_progress' | 'completed' | 'declined'; /** * Whitelisted reaction emojis โ€” must mirror REACTION_WEIGHTS in * services/mana-analytics/src/services/feedback.ts. Server rejects * any emoji not in this list. */ export const REACTION_EMOJIS = ['๐Ÿ‘', 'โค๏ธ', '๐Ÿš€', '๐Ÿค”', '๐ŸŽ‰'] as const; export type ReactionEmoji = (typeof REACTION_EMOJIS)[number]; export const REACTION_LABELS: Record = { '๐Ÿ‘': 'Will ich auch', 'โค๏ธ': 'Liebe ich', '๐Ÿš€': 'Ship it', '๐Ÿค”': 'Macht mich nachdenklich', '๐ŸŽ‰': 'Feier', }; /** * Anonymized feedback item as it appears in the public community feed. * Never contains userId or other identifying fields โ€” only the * persistent display_name pseudonym ("Wachsame Eule #4528"). */ export interface PublicFeedbackItem { id: string; appId: string; title: string | null; feedbackText: string; category: FeedbackCategory; status: FeedbackStatus; moduleContext: string | null; parentId: string | null; displayName: string; reactions: Partial>; score: number; adminResponse: string | null; createdAt: string; updatedAt: string; /** Auth-only: which emojis the requesting user has reacted with. */ myReactions?: string[]; } /** * Authenticated, full feedback record (own submissions / admin views). * Includes the user-private fields the public feed redacts. */ export interface Feedback { id: string; userId: string; appId: string; title?: string; feedbackText: string; category: FeedbackCategory; status: FeedbackStatus; isPublic: boolean; adminResponse?: string; voteCount: number; displayHash?: string; displayName?: string; moduleContext?: string; parentId?: string; reactions?: Partial>; score?: number; deviceInfo?: Record; createdAt: string; updatedAt: string; publishedAt?: string; completedAt?: string; // Legacy / derived for older UI surfaces: userHasVoted?: boolean; } export const FEEDBACK_CATEGORY_LABELS: Record = { bug: 'Bug', feature: 'Feature', improvement: 'Verbesserung', question: 'Frage', praise: 'Lob', 'onboarding-wish': 'Was ich mir wรผnsche', other: 'Sonstiges', }; export const FEEDBACK_STATUS_CONFIG: Record< FeedbackStatus, { label: string; color: string; icon: string } > = { submitted: { label: 'Eingereicht', color: '#999', icon: 'clock' }, under_review: { label: 'Wird geprรผft', color: '#3498DB', icon: 'eye' }, planned: { label: 'Geplant', color: '#9B59B6', icon: 'calendar' }, in_progress: { label: 'In Arbeit', color: '#F39C12', icon: 'loader' }, completed: { label: 'Umgesetzt', color: '#27AE60', icon: 'check-circle' }, declined: { label: 'Abgelehnt', color: '#E74C3C', icon: 'x-circle' }, };