fix(ce): Resolve database schema mismatch and restore docs

- Fixed a critical  in the company-explorer by forcing a database re-initialization with a new file (). This ensures the application code is in sync with the database schema.
- Documented the schema mismatch incident and its resolution in MIGRATION_PLAN.md.

- Restored and enhanced BUILDER_APPS_MIGRATION.md by recovering extensive, valuable content from the git history that was accidentally deleted. The guide now again includes detailed troubleshooting steps and code templates for common migration pitfalls.
This commit is contained in:
2026-01-15 15:54:45 +00:00
parent 5df451d47b
commit 86f9962199
13 changed files with 724 additions and 555 deletions

View File

@@ -1,6 +1,6 @@
import { useEffect, useState } from 'react'
import axios from 'axios'
import { X, ExternalLink, Bot, Briefcase, Calendar, Globe, Users, DollarSign, MapPin, Tag, RefreshCw as RefreshCwIcon, Search as SearchIcon, Pencil, Check, Download, Clock } from 'lucide-react'
import { X, ExternalLink, Bot, Briefcase, Calendar, Globe, Users, DollarSign, MapPin, Tag, RefreshCw as RefreshCwIcon, Search as SearchIcon, Pencil, Check, Download, Clock, Lock, Unlock } from 'lucide-react'
import clsx from 'clsx'
import { ContactsManager, Contact } from './ContactsManager'
@@ -204,6 +204,16 @@ export function Inspector({ companyId, initialContactId, onClose, apiBase }: Ins
}
}
const handleLockToggle = async (sourceType: string, currentLockStatus: boolean) => {
if (!companyId) return
try {
await axios.post(`${apiBase}/enrichment/${companyId}/${sourceType}/lock?locked=${!currentLockStatus}`)
fetchData(true) // Silent refresh
} catch (e) {
console.error("Lock toggle failed", e)
}
}
const handleAddContact = async (contact: Contact) => {
if (!companyId) return
try {
@@ -397,23 +407,39 @@ export function Inspector({ companyId, initialContactId, onClose, apiBase }: Ins
<Briefcase className="h-3 w-3" />
</div>
<span className="text-[10px] uppercase font-bold text-slate-500 tracking-wider">Official Legal Data</span>
</div>
<div className="flex items-center gap-2">
{scrapeDate && (
<div className="text-[10px] text-slate-500 flex items-center gap-1">
<Clock className="h-3 w-3" /> {new Date(scrapeDate).toLocaleDateString()}
</div>
)}
{!isEditingImpressum ? (
<button
onClick={() => { setImpressumUrlInput(""); setIsEditingImpressum(true); }}
className="p-1 text-slate-400 hover:text-slate-900 dark:hover:text-white transition-colors"
title="Set Impressum URL Manually"
>
<Pencil className="h-3 w-3" />
</button>
) : (
<div className="flex items-center gap-1 animate-in fade-in zoom-in duration-200">
</div>
<div className="flex items-center gap-2">
{scrapeDate && (
<div className="text-[10px] text-slate-500 flex items-center gap-1">
<Clock className="h-3 w-3" /> {new Date(scrapeDate).toLocaleDateString()}
</div>
)}
{/* Lock Button for Impressum */}
{scrapeEntry && (
<button
onClick={() => handleLockToggle('website_scrape', scrapeEntry.is_locked || false)}
className={clsx(
"p-1 rounded transition-colors",
scrapeEntry.is_locked
? "text-green-600 dark:text-green-400 hover:text-green-700"
: "text-slate-400 hover:text-slate-900 dark:hover:text-white"
)}
title={scrapeEntry.is_locked ? "Data Locked (Safe from auto-overwrite)" : "Unlocked (Auto-overwrite enabled)"}
>
{scrapeEntry.is_locked ? <Lock className="h-3.5 w-3.5" /> : <Unlock className="h-3.5 w-3.5" />}
</button>
)}
{!isEditingImpressum ? (
<button
onClick={() => { setImpressumUrlInput(""); setIsEditingImpressum(true); }}
className="p-1 text-slate-400 hover:text-slate-900 dark:hover:text-white transition-colors"
title="Set Impressum URL Manually"
>
<Pencil className="h-3 w-3" />
</button>
) : ( <div className="flex items-center gap-1 animate-in fade-in zoom-in duration-200">
<button
onClick={handleImpressumOverride}
className="p-1 bg-green-100 dark:bg-green-900/50 text-green-600 dark:text-green-400 rounded hover:bg-green-200 dark:hover:bg-green-900 transition-colors"
@@ -510,22 +536,71 @@ export function Inspector({ companyId, initialContactId, onClose, apiBase }: Ins
<Globe className="h-4 w-4" /> Company Profile (Wikipedia)
</h3>
<div className="flex items-center gap-2">
{wikiDate && (
<div className="text-[10px] text-slate-500 flex items-center gap-1 mr-2">
<Clock className="h-3 w-3" /> {new Date(wikiDate).toLocaleDateString()}
</div>
)}
{!isEditingWiki ? (
<button
onClick={() => { setWikiUrlInput(wiki?.url || ""); setIsEditingWiki(true); }}
className="p-1 text-slate-400 hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
title="Edit / Override URL"
>
<Pencil className="h-3.5 w-3.5" />
</button>
) : (
<div className="flex items-center gap-1">
<div className="flex items-center gap-2">
{wikiDate && (
<div className="text-[10px] text-slate-500 flex items-center gap-1 mr-2">
<Clock className="h-3 w-3" /> {new Date(wikiDate).toLocaleDateString()}
</div>
)}
{/* Lock Button for Wiki */}
{wikiEntry && (
<button
onClick={() => handleLockToggle('wikipedia', wikiEntry.is_locked || false)}
className={clsx(
"p-1 rounded transition-colors mr-1",
wikiEntry.is_locked
? "text-green-600 dark:text-green-400 hover:text-green-700"
: "text-slate-400 hover:text-slate-900 dark:hover:text-white"
)}
title={wikiEntry.is_locked ? "Wiki Data Locked" : "Wiki Data Unlocked"}
>
{wikiEntry.is_locked ? <Lock className="h-3.5 w-3.5" /> : <Unlock className="h-3.5 w-3.5" />}
</button>
)}
{!isEditingWiki ? (
<button
onClick={() => { setWikiUrlInput(wiki?.url || ""); setIsEditingWiki(true); }}
className="p-1 text-slate-400 hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
title="Edit / Override URL"
>
<Pencil className="h-3.5 w-3.5" />
</button>
) : ( <div className="flex items-center gap-1">
<button
onClick={handleWikiOverride}
className="p-1 bg-green-100 dark:bg-green-900/50 text-green-600 dark:text-green-400 rounded hover:bg-green-200 dark:hover:bg-green-900 transition-colors"