WIP: Améliorations UX et corrections de bugs avant création des épiques
This commit is contained in:
382
keep-notes/app/actions/ai-actions.ts
Normal file
382
keep-notes/app/actions/ai-actions.ts
Normal file
@@ -0,0 +1,382 @@
|
||||
'use server'
|
||||
|
||||
/**
|
||||
* AI Server Actions Stub File
|
||||
*
|
||||
* This file provides a centralized location for all AI-related server action interfaces
|
||||
* and serves as documentation for the AI server action architecture.
|
||||
*
|
||||
* IMPLEMENTATION STATUS:
|
||||
* - Title Suggestions: ✅ Implemented (see app/actions/title-suggestions.ts)
|
||||
* - Semantic Search: ✅ Implemented (see app/actions/semantic-search.ts)
|
||||
* - Paragraph Reformulation: ✅ Implemented (see app/actions/paragraph-refactor.ts)
|
||||
* - Memory Echo: ⏳ STUB - To be implemented in Epic 5 (Story 5-1)
|
||||
* - Language Detection: ✅ Implemented (see app/actions/detect-language.ts)
|
||||
* - AI Settings: ✅ Implemented (see app/actions/ai-settings.ts)
|
||||
*
|
||||
* NOTE: This file defines TypeScript interfaces and placeholder functions.
|
||||
* Actual implementations are in separate action files (see references above).
|
||||
*/
|
||||
|
||||
import { auth } from '@/auth'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { revalidatePath } from 'next/cache'
|
||||
|
||||
// ============================================================================
|
||||
// TYPESCRIPT INTERFACES
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Title Suggestions Interfaces
|
||||
* @see app/actions/title-suggestions.ts for implementation
|
||||
*/
|
||||
export interface GenerateTitlesRequest {
|
||||
noteId: string
|
||||
}
|
||||
|
||||
export interface GenerateTitlesResponse {
|
||||
suggestions: Array<{
|
||||
title: string
|
||||
confidence: number
|
||||
reasoning?: string
|
||||
}>
|
||||
noteId: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Semantic Search Interfaces
|
||||
* @see app/actions/semantic-search.ts for implementation
|
||||
*/
|
||||
export interface SearchResult {
|
||||
noteId: string
|
||||
title: string | null
|
||||
content: string
|
||||
similarity: number
|
||||
matchType: 'exact' | 'related'
|
||||
}
|
||||
|
||||
export interface SemanticSearchRequest {
|
||||
query: string
|
||||
options?: {
|
||||
limit?: number
|
||||
threshold?: number
|
||||
notebookId?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface SemanticSearchResponse {
|
||||
results: SearchResult[]
|
||||
query: string
|
||||
totalResults: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Paragraph Reformulation Interfaces
|
||||
* @see app/actions/paragraph-refactor.ts for implementation
|
||||
*/
|
||||
export type RefactorMode = 'clarify' | 'shorten' | 'improve'
|
||||
|
||||
export interface RefactorParagraphRequest {
|
||||
noteId: string
|
||||
selectedText: string
|
||||
option: RefactorMode
|
||||
}
|
||||
|
||||
export interface RefactorParagraphResponse {
|
||||
originalText: string
|
||||
refactoredText: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Memory Echo Interfaces
|
||||
* STUB - To be implemented in Epic 5 (Story 5-1)
|
||||
*
|
||||
* This feature will analyze all user notes with embeddings to find
|
||||
* connections with cosine similarity > 0.75 and provide proactive insights.
|
||||
*/
|
||||
export interface GenerateMemoryEchoRequest {
|
||||
// No params - uses current user session
|
||||
}
|
||||
|
||||
export interface MemoryEchoInsight {
|
||||
note1Id: string
|
||||
note2Id: string
|
||||
similarityScore: number
|
||||
}
|
||||
|
||||
export interface GenerateMemoryEchoResponse {
|
||||
success: boolean
|
||||
insight: MemoryEchoInsight | null
|
||||
}
|
||||
|
||||
/**
|
||||
* Language Detection Interfaces
|
||||
* @see app/actions/detect-language.ts for implementation
|
||||
*/
|
||||
export interface DetectLanguageRequest {
|
||||
content: string
|
||||
}
|
||||
|
||||
export interface DetectLanguageResponse {
|
||||
language: string
|
||||
confidence: number
|
||||
method: 'tinyld' | 'ai'
|
||||
}
|
||||
|
||||
/**
|
||||
* AI Settings Interfaces
|
||||
* @see app/actions/ai-settings.ts for implementation
|
||||
*/
|
||||
export interface AISettingsConfig {
|
||||
titleSuggestions?: boolean
|
||||
semanticSearch?: boolean
|
||||
paragraphRefactor?: boolean
|
||||
memoryEcho?: boolean
|
||||
memoryEchoFrequency?: 'daily' | 'weekly' | 'custom'
|
||||
aiProvider?: 'auto' | 'openai' | 'ollama'
|
||||
preferredLanguage?: 'auto' | 'en' | 'fr' | 'es' | 'de' | 'fa' | 'it' | 'pt' | 'ru' | 'zh' | 'ja' | 'ko' | 'ar' | 'hi' | 'nl' | 'pl'
|
||||
demoMode?: boolean
|
||||
}
|
||||
|
||||
export interface UpdateAISettingsRequest {
|
||||
settings: Partial<AISettingsConfig>
|
||||
}
|
||||
|
||||
export interface UpdateAISettingsResponse {
|
||||
success: boolean
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// PLACEHOLDER FUNCTIONS
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Generate Title Suggestions
|
||||
*
|
||||
* ALREADY IMPLEMENTED: See app/actions/title-suggestions.ts
|
||||
*
|
||||
* This function generates 3 AI-powered title suggestions for a note when it
|
||||
* reaches 50+ words without a title.
|
||||
*
|
||||
* @see generateTitleSuggestions in app/actions/title-suggestions.ts
|
||||
*/
|
||||
export async function generateTitles(
|
||||
request: GenerateTitlesRequest
|
||||
): Promise<GenerateTitlesResponse> {
|
||||
// TODO: Import and use implementation from title-suggestions.ts
|
||||
// import { generateTitleSuggestions } from './title-suggestions'
|
||||
// return generateTitleSuggestions(request.noteId)
|
||||
|
||||
throw new Error('Not implemented in stub: Use app/actions/title-suggestions.ts')
|
||||
}
|
||||
|
||||
/**
|
||||
* Semantic Search
|
||||
*
|
||||
* ALREADY IMPLEMENTED: See app/actions/semantic-search.ts
|
||||
*
|
||||
* This function performs hybrid semantic + keyword search across user notes.
|
||||
*
|
||||
* @see semanticSearch in app/actions/semantic-search.ts
|
||||
*/
|
||||
export async function semanticSearch(
|
||||
request: SemanticSearchRequest
|
||||
): Promise<SemanticSearchResponse> {
|
||||
// TODO: Import and use implementation from semantic-search.ts
|
||||
// import { semanticSearch } from './semantic-search'
|
||||
// return semanticSearch(request.query, request.options)
|
||||
|
||||
throw new Error('Not implemented in stub: Use app/actions/semantic-search.ts')
|
||||
}
|
||||
|
||||
/**
|
||||
* Refactor Paragraph
|
||||
*
|
||||
* ALREADY IMPLEMENTED: See app/actions/paragraph-refactor.ts
|
||||
*
|
||||
* This function refactors a paragraph using AI with specific mode (clarify/shorten/improve).
|
||||
*
|
||||
* @see refactorParagraph in app/actions/paragraph-refactor.ts
|
||||
*/
|
||||
export async function refactorParagraph(
|
||||
request: RefactorParagraphRequest
|
||||
): Promise<RefactorParagraphResponse> {
|
||||
// TODO: Import and use implementation from paragraph-refactor.ts
|
||||
// import { refactorParagraph } from './paragraph-refactor'
|
||||
// return refactorParagraph(request.selectedText, request.option)
|
||||
|
||||
throw new Error('Not implemented in stub: Use app/actions/paragraph-refactor.ts')
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Memory Echo Insights
|
||||
*
|
||||
* STUB: To be implemented in Epic 5 (Story 5-1)
|
||||
*
|
||||
* This will analyze all user notes with embeddings to find
|
||||
* connections with cosine similarity > 0.75.
|
||||
*
|
||||
* Implementation Plan:
|
||||
* - Fetch all user notes with embeddings
|
||||
* - Calculate pairwise cosine similarities
|
||||
* - Find top connection with similarity > 0.75
|
||||
* - Store in MemoryEchoInsight table
|
||||
* - Return insight or null if none found
|
||||
*
|
||||
* @see Epic 5 Story 5-1 in planning/epics.md
|
||||
*/
|
||||
export async function generateMemoryEcho(): Promise<GenerateMemoryEchoResponse> {
|
||||
const session = await auth()
|
||||
if (!session?.user?.id) {
|
||||
throw new Error('Unauthorized')
|
||||
}
|
||||
|
||||
// TODO: Implement Memory Echo background processing
|
||||
// - Fetch all user notes with embeddings from prisma.note
|
||||
// - Calculate pairwise cosine similarities using embedding vectors
|
||||
// - Filter for similarity > 0.75
|
||||
// - Select top insight
|
||||
// - Store in prisma.memoryEchoInsight table (if it exists)
|
||||
// - Return { success: true, insight: {...} }
|
||||
|
||||
throw new Error('Not implemented: See Epic 5 Story 5-1')
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect Language
|
||||
*
|
||||
* ALREADY IMPLEMENTED: See app/actions/detect-language.ts
|
||||
*
|
||||
* This function detects the language of user content.
|
||||
*
|
||||
* @see getInitialLanguage in app/actions/detect-language.ts
|
||||
*/
|
||||
export async function detectLanguage(
|
||||
request: DetectLanguageRequest
|
||||
): Promise<DetectLanguageResponse> {
|
||||
// TODO: Import and use implementation from detect-language.ts
|
||||
// import { detectUserLanguage } from '@/lib/i18n/detect-user-language'
|
||||
// const language = await detectUserLanguage()
|
||||
// return { language, confidence: 0.95, method: 'tinyld' }
|
||||
|
||||
throw new Error('Not implemented in stub: Use app/actions/detect-language.ts')
|
||||
}
|
||||
|
||||
/**
|
||||
* Update AI Settings
|
||||
*
|
||||
* ALREADY IMPLEMENTED: See app/actions/ai-settings.ts
|
||||
*
|
||||
* This function updates user AI preferences.
|
||||
*
|
||||
* @see updateAISettings in app/actions/ai-settings.ts
|
||||
*/
|
||||
export async function updateAISettings(
|
||||
request: UpdateAISettingsRequest
|
||||
): Promise<UpdateAISettingsResponse> {
|
||||
const session = await auth()
|
||||
if (!session?.user?.id) {
|
||||
throw new Error('Unauthorized')
|
||||
}
|
||||
|
||||
// TODO: Import and use implementation from ai-settings.ts
|
||||
// import { updateAISettings } from './ai-settings'
|
||||
// return updateAISettings(request.settings)
|
||||
|
||||
throw new Error('Not implemented in stub: Use app/actions/ai-settings.ts')
|
||||
}
|
||||
|
||||
/**
|
||||
* Get AI Settings
|
||||
*
|
||||
* ALREADY IMPLEMENTED: See app/actions/ai-settings.ts
|
||||
*
|
||||
* This function retrieves user AI preferences.
|
||||
*
|
||||
* @see getAISettings in app/actions/ai-settings.ts
|
||||
*/
|
||||
export async function getAISettings(): Promise<AISettingsConfig> {
|
||||
const session = await auth()
|
||||
if (!session?.user?.id) {
|
||||
throw new Error('Unauthorized')
|
||||
}
|
||||
|
||||
// TODO: Import and use implementation from ai-settings.ts
|
||||
// import { getAISettings } from './ai-settings'
|
||||
// return getAISettings()
|
||||
|
||||
throw new Error('Not implemented in stub: Use app/actions/ai-settings.ts')
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// UTILITY FUNCTIONS
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Check if a specific AI feature is enabled for the user
|
||||
*
|
||||
* UTILITY: Helper function to check feature flags
|
||||
*
|
||||
* @param feature - The AI feature to check
|
||||
* @returns Promise<boolean> - Whether the feature is enabled
|
||||
*/
|
||||
export async function isAIFeatureEnabled(
|
||||
feature: keyof AISettingsConfig
|
||||
): Promise<boolean> {
|
||||
const session = await auth()
|
||||
if (!session?.user?.id) {
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
const settings = await prisma.userAISettings.findUnique({
|
||||
where: { userId: session.user.id }
|
||||
})
|
||||
|
||||
if (!settings) {
|
||||
// Default to enabled for new users
|
||||
return true
|
||||
}
|
||||
|
||||
switch (feature) {
|
||||
case 'titleSuggestions':
|
||||
return settings.titleSuggestions ?? true
|
||||
case 'semanticSearch':
|
||||
return settings.semanticSearch ?? true
|
||||
case 'paragraphRefactor':
|
||||
return settings.paragraphRefactor ?? true
|
||||
case 'memoryEcho':
|
||||
return settings.memoryEcho ?? true
|
||||
default:
|
||||
return true
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking AI feature enabled:', error)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user's preferred AI provider
|
||||
*
|
||||
* UTILITY: Helper function to get provider preference
|
||||
*
|
||||
* @returns Promise<'auto' | 'openai' | 'ollama'> - The AI provider
|
||||
*/
|
||||
export async function getUserAIPreference(): Promise<'auto' | 'openai' | 'ollama'> {
|
||||
const session = await auth()
|
||||
if (!session?.user?.id) {
|
||||
return 'auto'
|
||||
}
|
||||
|
||||
try {
|
||||
const settings = await prisma.userAISettings.findUnique({
|
||||
where: { userId: session.user.id }
|
||||
})
|
||||
|
||||
return (settings?.aiProvider || 'auto') as 'auto' | 'openai' | 'ollama'
|
||||
} catch (error) {
|
||||
console.error('Error getting user AI preference:', error)
|
||||
return 'auto'
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ export type UserAISettingsData = {
|
||||
aiProvider?: 'auto' | 'openai' | 'ollama'
|
||||
preferredLanguage?: 'auto' | 'en' | 'fr' | 'es' | 'de' | 'fa' | 'it' | 'pt' | 'ru' | 'zh' | 'ja' | 'ko' | 'ar' | 'hi' | 'nl' | 'pl'
|
||||
demoMode?: boolean
|
||||
showRecentNotes?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,17 +62,34 @@ export async function getAISettings() {
|
||||
memoryEchoFrequency: 'daily' as const,
|
||||
aiProvider: 'auto' as const,
|
||||
preferredLanguage: 'auto' as const,
|
||||
demoMode: false
|
||||
demoMode: false,
|
||||
showRecentNotes: false
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const settings = await prisma.userAISettings.findUnique({
|
||||
where: { userId: session.user.id }
|
||||
})
|
||||
// Use raw SQL query to get showRecentNotes until Prisma client is regenerated
|
||||
const settingsRaw = await prisma.$queryRaw<Array<{
|
||||
titleSuggestions: number
|
||||
semanticSearch: number
|
||||
paragraphRefactor: number
|
||||
memoryEcho: number
|
||||
memoryEchoFrequency: string
|
||||
aiProvider: string
|
||||
preferredLanguage: string
|
||||
fontSize: string
|
||||
demoMode: number
|
||||
showRecentNotes: number
|
||||
}>>`
|
||||
SELECT titleSuggestions, semanticSearch, paragraphRefactor, memoryEcho,
|
||||
memoryEchoFrequency, aiProvider, preferredLanguage, fontSize,
|
||||
demoMode, showRecentNotes
|
||||
FROM UserAISettings
|
||||
WHERE userId = ${session.user.id}
|
||||
`
|
||||
|
||||
// Return settings or defaults if not found
|
||||
if (!settings) {
|
||||
if (!settingsRaw || settingsRaw.length === 0) {
|
||||
return {
|
||||
titleSuggestions: true,
|
||||
semanticSearch: true,
|
||||
@@ -80,20 +98,29 @@ export async function getAISettings() {
|
||||
memoryEchoFrequency: 'daily' as const,
|
||||
aiProvider: 'auto' as const,
|
||||
preferredLanguage: 'auto' as const,
|
||||
demoMode: false
|
||||
demoMode: false,
|
||||
showRecentNotes: false
|
||||
}
|
||||
}
|
||||
|
||||
const settings = settingsRaw[0]
|
||||
|
||||
// Type-cast database values to proper union types
|
||||
// Handle NULL values - SQLite can return NULL for showRecentNotes if column was added later
|
||||
const showRecentNotesValue = settings.showRecentNotes !== null && settings.showRecentNotes !== undefined
|
||||
? settings.showRecentNotes === 1
|
||||
: false
|
||||
|
||||
return {
|
||||
titleSuggestions: settings.titleSuggestions,
|
||||
semanticSearch: settings.semanticSearch,
|
||||
paragraphRefactor: settings.paragraphRefactor,
|
||||
memoryEcho: settings.memoryEcho,
|
||||
titleSuggestions: settings.titleSuggestions === 1,
|
||||
semanticSearch: settings.semanticSearch === 1,
|
||||
paragraphRefactor: settings.paragraphRefactor === 1,
|
||||
memoryEcho: settings.memoryEcho === 1,
|
||||
memoryEchoFrequency: (settings.memoryEchoFrequency || 'daily') as 'daily' | 'weekly' | 'custom',
|
||||
aiProvider: (settings.aiProvider || 'auto') as 'auto' | 'openai' | 'ollama',
|
||||
preferredLanguage: (settings.preferredLanguage || 'auto') as 'auto' | 'en' | 'fr' | 'es' | 'de' | 'fa' | 'it' | 'pt' | 'ru' | 'zh' | 'ja' | 'ko' | 'ar' | 'hi' | 'nl' | 'pl',
|
||||
demoMode: settings.demoMode || false
|
||||
demoMode: settings.demoMode === 1,
|
||||
showRecentNotes: showRecentNotesValue
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error getting AI settings:', error)
|
||||
@@ -106,7 +133,8 @@ export async function getAISettings() {
|
||||
memoryEchoFrequency: 'daily' as const,
|
||||
aiProvider: 'auto' as const,
|
||||
preferredLanguage: 'auto' as const,
|
||||
demoMode: false
|
||||
demoMode: false,
|
||||
showRecentNotes: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,6 +364,7 @@ export async function createNote(data: {
|
||||
await syncLabels(session.user.id, data.labels)
|
||||
}
|
||||
|
||||
// Revalidate main page (handles both inbox and notebook views via query params)
|
||||
revalidatePath('/')
|
||||
return parseNote(note)
|
||||
} catch (error) {
|
||||
@@ -388,6 +389,7 @@ export async function updateNote(id: string, data: {
|
||||
isMarkdown?: boolean
|
||||
size?: 'small' | 'medium' | 'large'
|
||||
autoGenerated?: boolean | null
|
||||
notebookId?: string | null
|
||||
}) {
|
||||
const session = await auth();
|
||||
if (!session?.user?.id) throw new Error('Unauthorized');
|
||||
@@ -395,12 +397,13 @@ export async function updateNote(id: string, data: {
|
||||
try {
|
||||
const oldNote = await prisma.note.findUnique({
|
||||
where: { id, userId: session.user.id },
|
||||
select: { labels: true }
|
||||
select: { labels: true, notebookId: true }
|
||||
})
|
||||
const oldLabels: string[] = oldNote?.labels ? JSON.parse(oldNote.labels) : []
|
||||
const oldNotebookId = oldNote?.notebookId
|
||||
|
||||
const updateData: any = { ...data }
|
||||
|
||||
|
||||
if (data.content !== undefined) {
|
||||
try {
|
||||
const provider = getAIProvider(await getSystemConfig());
|
||||
@@ -415,6 +418,7 @@ export async function updateNote(id: string, data: {
|
||||
if ('labels' in data) updateData.labels = data.labels ? JSON.stringify(data.labels) : null
|
||||
if ('images' in data) updateData.images = data.images ? JSON.stringify(data.images) : null
|
||||
if ('links' in data) updateData.links = data.links ? JSON.stringify(data.links) : null
|
||||
if ('notebookId' in data) updateData.notebookId = data.notebookId
|
||||
updateData.updatedAt = new Date()
|
||||
|
||||
const note = await prisma.note.update({
|
||||
@@ -428,9 +432,21 @@ export async function updateNote(id: string, data: {
|
||||
await syncLabels(session.user.id, data.labels || [])
|
||||
}
|
||||
|
||||
// Don't revalidatePath here - it would close the note editor dialog!
|
||||
// The dialog will close via the onClose callback after save completes
|
||||
// The UI will update via the normal React state management
|
||||
// IMPORTANT: Call revalidatePath to ensure UI updates
|
||||
// Revalidate main page, the note itself, and both old and new notebook paths
|
||||
revalidatePath('/')
|
||||
revalidatePath(`/note/${id}`)
|
||||
|
||||
// If notebook changed, revalidate both notebook paths
|
||||
if (data.notebookId !== undefined && data.notebookId !== oldNotebookId) {
|
||||
if (oldNotebookId) {
|
||||
revalidatePath(`/notebook/${oldNotebookId}`)
|
||||
}
|
||||
if (data.notebookId) {
|
||||
revalidatePath(`/notebook/${data.notebookId}`)
|
||||
}
|
||||
}
|
||||
|
||||
return parseNote(note)
|
||||
} catch (error) {
|
||||
console.error('Error updating note:', error)
|
||||
@@ -713,6 +729,62 @@ export async function getAllNotes(includeArchived = false) {
|
||||
}
|
||||
}
|
||||
|
||||
// Get pinned notes only
|
||||
export async function getPinnedNotes() {
|
||||
const session = await auth();
|
||||
if (!session?.user?.id) return [];
|
||||
|
||||
const userId = session.user.id;
|
||||
|
||||
try {
|
||||
const notes = await prisma.note.findMany({
|
||||
where: {
|
||||
userId: userId,
|
||||
isPinned: true,
|
||||
isArchived: false
|
||||
},
|
||||
orderBy: [
|
||||
{ order: 'asc' },
|
||||
{ updatedAt: 'desc' }
|
||||
]
|
||||
})
|
||||
|
||||
return notes.map(parseNote)
|
||||
} catch (error) {
|
||||
console.error('Error fetching pinned notes:', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
// Get recent notes (notes modified in the last 7 days)
|
||||
export async function getRecentNotes(limit: number = 3) {
|
||||
const session = await auth();
|
||||
if (!session?.user?.id) return [];
|
||||
|
||||
const userId = session.user.id;
|
||||
|
||||
try {
|
||||
const sevenDaysAgo = new Date()
|
||||
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7)
|
||||
sevenDaysAgo.setHours(0, 0, 0, 0) // Set to start of day
|
||||
|
||||
const notes = await prisma.note.findMany({
|
||||
where: {
|
||||
userId: userId,
|
||||
updatedAt: { gte: sevenDaysAgo },
|
||||
isArchived: false
|
||||
},
|
||||
orderBy: { updatedAt: 'desc' },
|
||||
take: limit
|
||||
})
|
||||
|
||||
return notes.map(parseNote)
|
||||
} catch (error) {
|
||||
console.error('Error fetching recent notes:', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export async function getNoteById(noteId: string) {
|
||||
const session = await auth();
|
||||
if (!session?.user?.id) return null;
|
||||
|
||||
@@ -93,6 +93,8 @@ export async function updateTheme(theme: string) {
|
||||
where: { id: session.user.id },
|
||||
data: { theme },
|
||||
})
|
||||
revalidatePath('/')
|
||||
revalidatePath('/settings/profile')
|
||||
return { success: true }
|
||||
} catch (error) {
|
||||
return { error: 'Failed to update theme' }
|
||||
@@ -118,6 +120,7 @@ export async function updateLanguage(language: string) {
|
||||
|
||||
// Note: The language will be applied on next page load
|
||||
// The client component should handle updating localStorage and reloading
|
||||
revalidatePath('/')
|
||||
revalidatePath('/settings/profile')
|
||||
return { success: true, language }
|
||||
} catch (error) {
|
||||
@@ -156,11 +159,13 @@ export async function updateFontSize(fontSize: string) {
|
||||
memoryEcho: true,
|
||||
memoryEchoFrequency: 'daily',
|
||||
aiProvider: 'auto',
|
||||
preferredLanguage: 'auto'
|
||||
preferredLanguage: 'auto',
|
||||
showRecentNotes: false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
revalidatePath('/')
|
||||
revalidatePath('/settings/profile')
|
||||
return { success: true, fontSize }
|
||||
} catch (error) {
|
||||
@@ -168,3 +173,60 @@ export async function updateFontSize(fontSize: string) {
|
||||
return { error: 'Failed to update font size' }
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateShowRecentNotes(showRecentNotes: boolean) {
|
||||
const session = await auth()
|
||||
if (!session?.user?.id) return { error: 'Unauthorized' }
|
||||
|
||||
try {
|
||||
// Use EXACT same pattern as updateFontSize which works
|
||||
const existing = await prisma.userAISettings.findUnique({
|
||||
where: { userId: session.user.id }
|
||||
})
|
||||
|
||||
if (existing) {
|
||||
// Try Prisma client first, fallback to raw SQL if field doesn't exist in client
|
||||
try {
|
||||
await prisma.userAISettings.update({
|
||||
where: { userId: session.user.id },
|
||||
data: { showRecentNotes: showRecentNotes } as any
|
||||
})
|
||||
} catch (prismaError: any) {
|
||||
// If Prisma client doesn't know about showRecentNotes, use raw SQL
|
||||
if (prismaError?.message?.includes('Unknown argument') || prismaError?.code === 'P2009') {
|
||||
const value = showRecentNotes ? 1 : 0
|
||||
await prisma.$executeRaw`
|
||||
UPDATE UserAISettings
|
||||
SET showRecentNotes = ${value}
|
||||
WHERE userId = ${session.user.id}
|
||||
`
|
||||
} else {
|
||||
throw prismaError
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Create new - same as updateFontSize
|
||||
await prisma.userAISettings.create({
|
||||
data: {
|
||||
userId: session.user.id,
|
||||
titleSuggestions: true,
|
||||
semanticSearch: true,
|
||||
paragraphRefactor: true,
|
||||
memoryEcho: true,
|
||||
memoryEchoFrequency: 'daily',
|
||||
aiProvider: 'auto',
|
||||
preferredLanguage: 'auto',
|
||||
fontSize: 'medium',
|
||||
showRecentNotes: showRecentNotes
|
||||
} as any
|
||||
})
|
||||
}
|
||||
|
||||
revalidatePath('/')
|
||||
revalidatePath('/settings/profile')
|
||||
return { success: true, showRecentNotes }
|
||||
} catch (error) {
|
||||
console.error('[updateShowRecentNotes] Failed:', error)
|
||||
return { error: 'Failed to update show recent notes setting' }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user