preview modules: SQL file list, install with SQL waits for confirmation, link panel closes after install

- The SQL review listed '( statements)': the page read file_path and statement_count, the
  backend sends file, target_schema, line_count and what each file does.
- After a build from a link, a module with SQL files was installed without them (its NPCs
  missing) and the panel kept offering a second install; now the SQL confirmation opens
  instead, and the link panel closes once the module is installed.
- The catalogue says when a filter hides modules, with Show all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ДмитрийandClaude Opus 5.5 committed 2026-11-07 13:23:13 +05:00
1 parent 899b7c6617
commit e7744dfc7c
2 files changed
+81 -22

No files matched your search

+10 -1
View File
@@ -342,7 +342,16 @@ export interface CompatibilityReport {
has_duplicate_registration: boolean;
};
databases: Array<{ database_type: string; operation: string; file: string; line: number }>;
sql_files: Array<{ file_path: string; statement_count: number; tables_referenced: string[]; has_schema_mutation: boolean }>;
/** As the backend sends it (analyzer::SqlFileAudit). */
sql_files: Array<{
file: string;
target_schema: string;
has_create_table: boolean;
has_alter_table: boolean;
has_insert_replace: boolean;
has_delete_update: boolean;
line_count: number;
}>;
core_patches: string[];
has_cmakelists: boolean;
has_entrypoint_adapter: boolean;
+71 -21
View File
@@ -650,7 +650,13 @@ export function ModulesPage({
}, [menuOpen, serverId]);
const [analyzing, setAnalyzing] = useState(false);
const [analysisError, setAnalysisError] = useState<string | null>(null);
const [report, setReport] = useState<CompatibilityReport | null>(null);
const [report, setReportState] = useState<CompatibilityReport | null>(null);
// The build callbacks run after an await, when `report` in their closure may be older: they read this.
const reportRef = useRef<CompatibilityReport | null>(null);
const setReport = (r: CompatibilityReport | null) => {
reportRef.current = r;
setReportState(r);
};
// Toolchain state
const [toolchain, setToolchain] = useState<ToolchainStatus | null>(null);
@@ -912,26 +918,28 @@ export function ModulesPage({
elapsedSeconds: (Date.now() - startTime) / 1000,
});
// Automatically install if module doesn't require schema changes
// A module without SQL is installed right away; one with SQL waits for the owner to confirm it (installing
// it without its SQL would leave out what the SQL adds, such as the module's NPCs).
let autoInstalled = false;
try {
await api.previewInstallModule(
serverId,
res.module_id,
res.version,
false
);
setNotification(
locale === "ru"
? `Модуль '${res.display_name || res.module_id}' успешно собран и установлен!`
: `Module '${res.display_name || res.module_id}' successfully built and installed!`
);
await load();
setActiveTab("installed");
onChanged?.();
autoInstalled = true;
} catch {
// If auto-install requires manual SQL confirmation, keep build results visible
if ((reportRef.current?.sql_files.length ?? 0) > 0) {
setShowSqlDialog(true);
autoInstalled = true; // the confirmation dialog takes over; no "build succeeded" banner
} else {
try {
await api.previewInstallModule(serverId, res.module_id, res.version, false);
setNotification(
locale === "ru"
? `Модуль '${res.display_name || res.module_id}' собран и установлен.`
: `Module '${res.display_name || res.module_id}' built and installed.`
);
closeLinkPanel();
await load();
setActiveTab("installed");
onChanged?.();
autoInstalled = true;
} catch {
// keep the build result visible: "Install" stays available
}
}
if (!autoInstalled) {
@@ -999,6 +1007,15 @@ export function ModulesPage({
}
}
/** After a module from a link is installed, the link panel and its analysis are done with. */
function closeLinkPanel() {
setReport(null);
setBuildResult(null);
setGithubUrl("");
setAnalysisError(null);
setShowUrlBar(false);
}
async function performInstall(applySql: boolean) {
if (!buildResult) return;
setShowSqlDialog(false);
@@ -1012,6 +1029,7 @@ export function ModulesPage({
applySql
);
setNotification(res.message);
closeLinkPanel();
await load();
setActiveTab("installed");
onChanged?.();
@@ -2094,6 +2112,25 @@ export function ModulesPage({
})}
</div>
{(catalogFilter !== "all" || catalogCategory !== "all") && (
<p className="flex flex-wrap items-center justify-center gap-2 text-xs text-muted">
{locale === "ru"
? `Показано ${filteredVerifiedCatalog.length + filteredCommunityCatalog.length} из ${(searchedVerifiedCatalog.length + searchedCommunityCatalog.length)}: включён фильтр.`
: `Showing ${filteredVerifiedCatalog.length + filteredCommunityCatalog.length} of ${searchedVerifiedCatalog.length + searchedCommunityCatalog.length}: a filter is on.`}
<Button
size="sm"
variant="ghost"
onClick={() => {
setCatalogFilter("all");
setCatalogCategory("all");
}}
className="h-6 border border-line px-2 text-xs"
>
{locale === "ru" ? "Показать все" : "Show all"}
</Button>
</p>
)}
{filteredCommunityCatalog.length > communityDisplayLimit && (
<div className="flex flex-col items-center gap-1.5 pt-1">
<Button size="sm" variant="ghost" onClick={() => setCommunityDisplayLimit((prev) => prev + 50)} className="border border-line px-4 text-xs">
@@ -2550,7 +2587,20 @@ export function ModulesPage({
<ul className="space-y-1 font-mono text-[11px] text-zinc-300">
{report.sql_files.map((s, idx) => (
<li key={idx}>
• {s.file_path} ({s.statement_count} statements)
• {s.file.split(/[\\/]/).pop()}{" "}
<span className="text-muted">
({s.target_schema === "unspecified" ? (locale === "ru" ? "база не указана" : "database not given") : s.target_schema}
{", "}
{[
s.has_create_table && (locale === "ru" ? "создаёт таблицы" : "creates tables"),
s.has_alter_table && (locale === "ru" ? "меняет таблицы" : "alters tables"),
s.has_insert_replace && (locale === "ru" ? "добавляет данные" : "adds data"),
s.has_delete_update && (locale === "ru" ? "меняет/удаляет данные" : "changes or deletes data"),
]
.filter(Boolean)
.join(", ") || (locale === "ru" ? `${s.line_count} строк` : `${s.line_count} lines`)}
)
</span>
</li>
))}
</ul>