Refactor scripts grid and filter handling for robustness

Improves type safety and normalization in filter, repository, and script status handling across multiple components. Refactors ScriptsGrid for better readability, deduplication, and error messaging, and updates UI markup for consistency. Also adds explicit types for auto-sync settings and ensures string conversion for credential fields.
This commit is contained in:
CanbiZ
2025-11-28 11:58:38 +01:00
parent 9c759ba99b
commit d40aeb6c82
7 changed files with 538 additions and 342 deletions

View File

@@ -67,7 +67,9 @@ export function DownloadedScriptsTab({
if (saveFilterEnabled) {
const filtersResponse = await fetch("/api/settings/filters");
if (filtersResponse.ok) {
const filtersData = await filtersResponse.json();
const filtersData = (await filtersResponse.json()) as {
filters?: Partial<FilterState>;
};
if (filtersData.filters) {
setFilters(mergeFiltersWithDefaults(filtersData.filters));
}

View File

@@ -322,9 +322,9 @@ export function FilterBar({
{/* Repository Filter Buttons - Only show if more than one enabled repo */}
{enabledRepos.length > 1 &&
enabledRepos.map((repo) => {
const isSelected = filters.selectedRepositories.includes(
repo.url,
);
const repoUrl = String(repo.url);
const isSelected =
filters.selectedRepositories.includes(repoUrl);
return (
<Button
key={repo.id}
@@ -334,13 +334,13 @@ export function FilterBar({
// Remove repository from selection
updateFilters({
selectedRepositories: currentSelected.filter(
(url) => url !== repo.url,
(url) => url !== repoUrl,
),
});
} else {
// Add repository to selection
updateFilters({
selectedRepositories: [...currentSelected, repo.url],
selectedRepositories: [...currentSelected, repoUrl],
});
}
}}
@@ -353,7 +353,7 @@ export function FilterBar({
}`}
>
<GitBranch className="h-4 w-4" />
<span>{getRepoName(repo.url)}</span>
<span>{getRepoName(repoUrl)}</span>
</Button>
);
})}

View File

@@ -11,6 +11,20 @@ import { api } from "~/trpc/react";
import { useAuth } from "./AuthProvider";
import { Trash2, ExternalLink } from "lucide-react";
interface AutoSyncSettings {
autoSyncEnabled: boolean;
syncIntervalType: "predefined" | "custom";
syncIntervalPredefined: string;
syncIntervalCron: string;
autoDownloadNew: boolean;
autoUpdateExisting: boolean;
notificationEnabled: boolean;
appriseUrls: string[];
lastAutoSync: string;
lastAutoSyncError: string | null;
lastAutoSyncErrorTime: string | null;
}
interface GeneralSettingsModalProps {
isOpen: boolean;
onClose: () => void;
@@ -482,7 +496,9 @@ export function GeneralSettingsModal({
try {
const response = await fetch("/api/settings/auto-sync");
if (response.ok) {
const data = (await response.json()) as { settings: any };
const data = (await response.json()) as {
settings: AutoSyncSettings | null;
};
const settings = data.settings;
if (settings) {
setAutoSyncEnabled(settings.autoSyncEnabled ?? false);

View File

@@ -45,10 +45,12 @@ export function PBSCredentialsModal({
if (isOpen) {
if (credentialData?.success && credentialData.credential) {
// Load existing credentials
setPbsIp(credentialData.credential.pbs_ip);
setPbsDatastore(credentialData.credential.pbs_datastore);
setPbsIp(String(credentialData.credential.pbs_ip));
setPbsDatastore(String(credentialData.credential.pbs_datastore));
setPbsPassword(""); // Don't show password
setPbsFingerprint(credentialData.credential.pbs_fingerprint ?? "");
setPbsFingerprint(
String(credentialData.credential.pbs_fingerprint ?? ""),
);
} else {
// Initialize with storage config values
setPbsIp(pbsIpFromStorage ?? "");

File diff suppressed because it is too large Load Diff

View File

@@ -48,7 +48,7 @@ export function ServerStoragesModal({
const credentialsMap = new Map<string, boolean>();
if (allCredentials?.success) {
allCredentials.credentials.forEach((c) => {
credentialsMap.set(c.storage_name, true);
credentialsMap.set(String(c.storage_name), true);
});
}

View File

@@ -161,8 +161,11 @@ export default function Home() {
// Secondary: Check install basenames (for edge cases where install script names differ from slugs)
const normalizedLocal = normalizeId(local.name);
const scriptWithBasenames = script as {
install_basenames?: string[];
};
const matchesInstallBasename =
(script as any)?.install_basenames?.some(
scriptWithBasenames.install_basenames?.some(
(base: string) => normalizeId(base) === normalizedLocal,
) ?? false;
if (matchesInstallBasename) return true;