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:
@@ -67,7 +67,9 @@ export function DownloadedScriptsTab({
|
|||||||
if (saveFilterEnabled) {
|
if (saveFilterEnabled) {
|
||||||
const filtersResponse = await fetch("/api/settings/filters");
|
const filtersResponse = await fetch("/api/settings/filters");
|
||||||
if (filtersResponse.ok) {
|
if (filtersResponse.ok) {
|
||||||
const filtersData = await filtersResponse.json();
|
const filtersData = (await filtersResponse.json()) as {
|
||||||
|
filters?: Partial<FilterState>;
|
||||||
|
};
|
||||||
if (filtersData.filters) {
|
if (filtersData.filters) {
|
||||||
setFilters(mergeFiltersWithDefaults(filtersData.filters));
|
setFilters(mergeFiltersWithDefaults(filtersData.filters));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -322,9 +322,9 @@ export function FilterBar({
|
|||||||
{/* Repository Filter Buttons - Only show if more than one enabled repo */}
|
{/* Repository Filter Buttons - Only show if more than one enabled repo */}
|
||||||
{enabledRepos.length > 1 &&
|
{enabledRepos.length > 1 &&
|
||||||
enabledRepos.map((repo) => {
|
enabledRepos.map((repo) => {
|
||||||
const isSelected = filters.selectedRepositories.includes(
|
const repoUrl = String(repo.url);
|
||||||
repo.url,
|
const isSelected =
|
||||||
);
|
filters.selectedRepositories.includes(repoUrl);
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
key={repo.id}
|
key={repo.id}
|
||||||
@@ -334,13 +334,13 @@ export function FilterBar({
|
|||||||
// Remove repository from selection
|
// Remove repository from selection
|
||||||
updateFilters({
|
updateFilters({
|
||||||
selectedRepositories: currentSelected.filter(
|
selectedRepositories: currentSelected.filter(
|
||||||
(url) => url !== repo.url,
|
(url) => url !== repoUrl,
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Add repository to selection
|
// Add repository to selection
|
||||||
updateFilters({
|
updateFilters({
|
||||||
selectedRepositories: [...currentSelected, repo.url],
|
selectedRepositories: [...currentSelected, repoUrl],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@@ -353,7 +353,7 @@ export function FilterBar({
|
|||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<GitBranch className="h-4 w-4" />
|
<GitBranch className="h-4 w-4" />
|
||||||
<span>{getRepoName(repo.url)}</span>
|
<span>{getRepoName(repoUrl)}</span>
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -11,6 +11,20 @@ import { api } from "~/trpc/react";
|
|||||||
import { useAuth } from "./AuthProvider";
|
import { useAuth } from "./AuthProvider";
|
||||||
import { Trash2, ExternalLink } from "lucide-react";
|
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 {
|
interface GeneralSettingsModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@@ -482,7 +496,9 @@ export function GeneralSettingsModal({
|
|||||||
try {
|
try {
|
||||||
const response = await fetch("/api/settings/auto-sync");
|
const response = await fetch("/api/settings/auto-sync");
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const data = (await response.json()) as { settings: any };
|
const data = (await response.json()) as {
|
||||||
|
settings: AutoSyncSettings | null;
|
||||||
|
};
|
||||||
const settings = data.settings;
|
const settings = data.settings;
|
||||||
if (settings) {
|
if (settings) {
|
||||||
setAutoSyncEnabled(settings.autoSyncEnabled ?? false);
|
setAutoSyncEnabled(settings.autoSyncEnabled ?? false);
|
||||||
|
|||||||
@@ -45,10 +45,12 @@ export function PBSCredentialsModal({
|
|||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
if (credentialData?.success && credentialData.credential) {
|
if (credentialData?.success && credentialData.credential) {
|
||||||
// Load existing credentials
|
// Load existing credentials
|
||||||
setPbsIp(credentialData.credential.pbs_ip);
|
setPbsIp(String(credentialData.credential.pbs_ip));
|
||||||
setPbsDatastore(credentialData.credential.pbs_datastore);
|
setPbsDatastore(String(credentialData.credential.pbs_datastore));
|
||||||
setPbsPassword(""); // Don't show password
|
setPbsPassword(""); // Don't show password
|
||||||
setPbsFingerprint(credentialData.credential.pbs_fingerprint ?? "");
|
setPbsFingerprint(
|
||||||
|
String(credentialData.credential.pbs_fingerprint ?? ""),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// Initialize with storage config values
|
// Initialize with storage config values
|
||||||
setPbsIp(pbsIpFromStorage ?? "");
|
setPbsIp(pbsIpFromStorage ?? "");
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -48,7 +48,7 @@ export function ServerStoragesModal({
|
|||||||
const credentialsMap = new Map<string, boolean>();
|
const credentialsMap = new Map<string, boolean>();
|
||||||
if (allCredentials?.success) {
|
if (allCredentials?.success) {
|
||||||
allCredentials.credentials.forEach((c) => {
|
allCredentials.credentials.forEach((c) => {
|
||||||
credentialsMap.set(c.storage_name, true);
|
credentialsMap.set(String(c.storage_name), true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -161,8 +161,11 @@ export default function Home() {
|
|||||||
|
|
||||||
// Secondary: Check install basenames (for edge cases where install script names differ from slugs)
|
// Secondary: Check install basenames (for edge cases where install script names differ from slugs)
|
||||||
const normalizedLocal = normalizeId(local.name);
|
const normalizedLocal = normalizeId(local.name);
|
||||||
|
const scriptWithBasenames = script as {
|
||||||
|
install_basenames?: string[];
|
||||||
|
};
|
||||||
const matchesInstallBasename =
|
const matchesInstallBasename =
|
||||||
(script as any)?.install_basenames?.some(
|
scriptWithBasenames.install_basenames?.some(
|
||||||
(base: string) => normalizeId(base) === normalizedLocal,
|
(base: string) => normalizeId(base) === normalizedLocal,
|
||||||
) ?? false;
|
) ?? false;
|
||||||
if (matchesInstallBasename) return true;
|
if (matchesInstallBasename) return true;
|
||||||
|
|||||||
Reference in New Issue
Block a user