Keep/scripts/migrate-to-notebooks.ts
sepehr 7fb486c9a4 feat: Complete internationalization and code cleanup
## Translation Files
- Add 11 new language files (es, de, pt, ru, zh, ja, ko, ar, hi, nl, pl)
- Add 100+ missing translation keys across all 15 languages
- New sections: notebook, pagination, ai.batchOrganization, ai.autoLabels
- Update nav section with workspace, quickAccess, myLibrary keys

## Component Updates
- Update 15+ components to use translation keys instead of hardcoded text
- Components: notebook dialogs, sidebar, header, note-input, ghost-tags, etc.
- Replace 80+ hardcoded English/French strings with t() calls
- Ensure consistent UI across all supported languages

## Code Quality
- Remove 77+ console.log statements from codebase
- Clean up API routes, components, hooks, and services
- Keep only essential error handling (no debugging logs)

## UI/UX Improvements
- Update Keep logo to yellow post-it style (from-yellow-400 to-amber-500)
- Change selection colors to #FEF3C6 (notebooks) and #EFB162 (nav items)
- Make "+" button permanently visible in notebooks section
- Fix grammar and syntax errors in multiple components

## Bug Fixes
- Fix JSON syntax errors in it.json, nl.json, pl.json, zh.json
- Fix syntax errors in notebook-suggestion-toast.tsx
- Fix syntax errors in use-auto-tagging.ts
- Fix syntax errors in paragraph-refactor.service.ts
- Fix duplicate "fusion" section in nl.json

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

Ou une version plus courte si vous préférez :

feat(i18n): Add 15 languages, remove logs, update UI components

- Create 11 new translation files (es, de, pt, ru, zh, ja, ko, ar, hi, nl, pl)
- Add 100+ translation keys: notebook, pagination, AI features
- Update 15+ components to use translations (80+ strings)
- Remove 77+ console.log statements from codebase
- Fix JSON syntax errors in 4 translation files
- Fix component syntax errors (toast, hooks, services)
- Update logo to yellow post-it style
- Change selection colors (#FEF3C6, #EFB162)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-11 22:26:13 +01:00

246 lines
7.0 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Migration Script: Tags → Notebooks with Contextual Labels
*
* This script migrates from the flat tags system to the new notebooks system.
* It creates a default "Labels Migrés" notebook for each user and moves all
* existing labels to that notebook. Notes remain in "Notes générales" (Inbox).
*
* IMPORTANT: This is a NON-BREAKING migration. The existing system continues
* to work during and after migration.
*
* Usage:
* npx tsx scripts/migrate-to-notebooks.ts
*
* Rollback:
* npx tsx scripts/rollback-notebooks.ts
*/
import { prisma } from '../keep-notes/lib/prisma'
interface MigrationStats {
usersProcessed: number
notebooksCreated: number
labelsMigrated: number
notesAffected: number
errors: string[]
}
/**
* Main migration function
*/
async function migrateToNotebooks(): Promise<MigrationStats> {
console.log('🚀 Starting migration to notebooks...\n')
const stats: MigrationStats = {
usersProcessed: 0,
notebooksCreated: 0,
labelsMigrated: 0,
notesAffected: 0,
errors: [],
}
try {
// Step 1: Get all users
console.log('📊 Fetching users...')
const users = await prisma.user.findMany({
select: {
id: true,
name: true,
email: true,
},
})
if (users.length === 0) {
console.log('⚠️ No users found. Nothing to migrate.')
return stats
}
console.log(`✅ Found ${users.length} user(s)\n`)
// Step 2: Process each user
for (const user of users) {
console.log(`\n👤 Processing user: ${user.name || user.email} (${user.id})`)
try {
// Step 2.1: Check if migration notebook already exists
const existingNotebook = await prisma.notebook.findFirst({
where: {
name: 'Labels Migrés',
userId: user.id,
},
})
if (existingNotebook) {
console.log(' ⏭️ Migration notebook already exists, skipping...')
continue
}
// Step 2.2: Create "Labels Migrés" notebook
console.log(' 📁 Creating "Labels Migrés" notebook...')
const migrationNotebook = await prisma.notebook.create({
data: {
id: `migrate-${user.id}`, // Deterministic ID
name: 'Labels Migrés',
icon: '📦',
color: '#9CA3AF', // gray-400
order: 999, // At the bottom
userId: user.id,
},
})
stats.notebooksCreated++
console.log(` ✅ Created notebook: ${migrationNotebook.id}`)
// Step 2.3: Migrate all labels for this user
console.log(' 🏷️ Migrating labels...')
const labels = await prisma.label.findMany({
where: {
userId: user.id,
notebookId: {
equals: '', // Empty string = not migrated yet
},
},
})
// Also try with null (in case database has null values)
const labelsWithNull = await prisma.label.findMany({
where: {
userId: user.id,
notebookId: null,
},
})
// Combine both results (avoid duplicates)
const allLabels = [...labels]
for (const label of labelsWithNull) {
if (!allLabels.find(l => l.id === label.id)) {
allLabels.push(label)
}
}
if (allLabels.length === 0) {
console.log(' No labels to migrate')
continue
}
// Update all labels to belong to the migration notebook
const labelUpdates = allLabels.map((label) =>
prisma.label.update({
where: { id: label.id },
data: { notebookId: migrationNotebook.id },
})
)
await prisma.$transaction(labelUpdates)
stats.labelsMigrated += allLabels.length
console.log(` ✅ Migrated ${allLabels.length} label(s)`)
// Step 2.4: Count notes that will be affected (information only)
const noteCount = await prisma.note.count({
where: { userId: user.id },
})
stats.notesAffected += noteCount
console.log(` User has ${noteCount} note(s) (will remain in "Notes générales")`)
stats.usersProcessed++
} catch (userError) {
const errorMsg = `Failed to migrate user ${user.id}: ${userError}`
console.error(`${errorMsg}`)
stats.errors.push(errorMsg)
}
}
// Summary
console.log('\n' + '='.repeat(60))
console.log('✅ Migration complete!\n')
console.log('📊 Summary:')
console.log(` Users processed: ${stats.usersProcessed}`)
console.log(` Notebooks created: ${stats.notebooksCreated}`)
console.log(` Labels migrated: ${stats.labelsMigrated}`)
console.log(` Notes affected: ${stats.notesAffected} (all remain in Inbox)`)
if (stats.errors.length > 0) {
console.log(`\n⚠ Errors: ${stats.errors.length}`)
stats.errors.forEach((error) => console.log(`${error}`))
}
console.log('\n' + '='.repeat(60))
console.log('\n✨ Migration successful!')
console.log('\n📌 Next steps:')
console.log(' 1. Test the application to ensure everything works')
console.log(' 2. Users can now organize their notes into notebooks')
console.log(' 3. Users can move labels from "Labels Migrés" to new notebooks')
console.log(' 4. Consider deleting old labels field from Note model after verification\n')
return stats
} catch (error) {
console.error('\n❌ Migration failed:', error)
throw error
} finally {
await prisma.$disconnect()
}
}
/**
* Dry-run mode: Show what would happen without making changes
*/
async function dryRunMigration() {
console.log('🔍 DRY RUN MODE - No changes will be made\n')
const users = await prisma.user.findMany({
select: {
id: true,
name: true,
email: true,
},
})
console.log(`Found ${users.length} user(s)\n`)
for (const user of users) {
console.log(`\n👤 User: ${user.name || user.email}`)
const labels = await prisma.label.findMany({
where: { userId: user.id },
select: { id: true, name: true },
})
const notes = await prisma.note.count({
where: { userId: user.id },
})
console.log(` Labels: ${labels.length}`)
console.log(` Notes: ${notes}`)
console.log(` Would create: "Labels Migrés" notebook`)
console.log(` Would migrate: ${labels.length} labels`)
}
await prisma.$disconnect()
}
// Run migration
const args = process.argv.slice(2)
const isDryRun = args.includes('--dry-run')
if (isDryRun) {
dryRunMigration()
.then(() => {
console.log('\n✅ Dry run complete')
process.exit(0)
})
.catch((error) => {
console.error('\n❌ Dry run failed:', error)
process.exit(1)
})
} else {
migrateToNotebooks()
.then(() => {
console.log('\n✨ All done!')
process.exit(0)
})
.catch((error) => {
console.error('\n💥 Fatal error:', error)
process.exit(1)
})
}