diff --git a/src/components/config/ConfigTabs.tsx b/src/components/config/ConfigTabs.tsx index cacc2d7..1205c69 100644 --- a/src/components/config/ConfigTabs.tsx +++ b/src/components/config/ConfigTabs.tsx @@ -3,8 +3,6 @@ import { GitHubConfigForm } from './GitHubConfigForm'; import { GiteaConfigForm } from './GiteaConfigForm'; import { ScheduleConfigForm } from './ScheduleConfigForm'; import { DatabaseCleanupConfigForm } from './DatabaseCleanupConfigForm'; -import { MirrorOptionsForm } from './MirrorOptionsForm'; -import { AdvancedOptionsForm } from './AdvancedOptionsForm'; import type { ConfigApiResponse, GiteaConfig, @@ -540,62 +538,52 @@ export function ConfigTabs() { {/* Content section - Grid layout */} -
- {/* GitHub & Gitea connections */} -
-
- - -
-
- - - -
-
-
-
- - -
-
- - - - -
-
- - {/* Schedule & Database Cleanup */} -
-
- - - -
-
-
-
- - - -
-
-
- - {/* Mirror Options & Advanced - Full width sections */}
-
- -
- - + {/* GitHub & Gitea connections - Side by side */} +
+
+
+ + +
+
+ + + + + + +
+
+
+
+ + +
+
+ + + + +
-
- -
- + + {/* Schedule & Database Cleanup - Side by side */} +
+
+
+ + + +
+
+
+
+ + + +
@@ -662,7 +650,29 @@ export function ConfigTabs() { : update, })) } + mirrorOptions={config.mirrorOptions} + setMirrorOptions={update => + setConfig(prev => ({ + ...prev, + mirrorOptions: + typeof update === 'function' + ? update(prev.mirrorOptions) + : update, + })) + } + advancedOptions={config.advancedOptions} + setAdvancedOptions={update => + setConfig(prev => ({ + ...prev, + advancedOptions: + typeof update === 'function' + ? update(prev.advancedOptions) + : update, + })) + } onAutoSave={autoSaveGitHubConfig} + onMirrorOptionsAutoSave={autoSaveMirrorOptions} + onAdvancedOptionsAutoSave={autoSaveAdvancedOptions} isAutoSaving={isAutoSavingGitHub} />
- {/* Mirror Options - Full width */} -
- - setConfig(prev => ({ - ...prev, - mirrorOptions: - typeof update === 'function' - ? update(prev.mirrorOptions) - : update, - })) - } - onAutoSave={autoSaveMirrorOptions} - isAutoSaving={isAutoSavingMirrorOptions} - /> -
- {/* Schedule & Database Cleanup - Side by side */}
- - {/* Advanced options - Full width */} -
- - setConfig(prev => ({ - ...prev, - advancedOptions: - typeof update === 'function' - ? update(prev.advancedOptions) - : update, - })) - } - onAutoSave={autoSaveAdvancedOptions} - isAutoSaving={isAutoSavingAdvancedOptions} - /> -
); diff --git a/src/components/config/GitHubConfigForm.tsx b/src/components/config/GitHubConfigForm.tsx index 3671d74..f1704bf 100644 --- a/src/components/config/GitHubConfigForm.tsx +++ b/src/components/config/GitHubConfigForm.tsx @@ -8,7 +8,7 @@ import { CardTitle, } from "@/components/ui/card"; import { githubApi } from "@/lib/api"; -import type { GitHubConfig } from "@/types/config"; +import type { GitHubConfig, MirrorOptions, AdvancedOptions } from "@/types/config"; import { Input } from "../ui/input"; import { Checkbox } from "../ui/checkbox"; import { toast } from "sonner"; @@ -16,15 +16,34 @@ import { AlertTriangle } from "lucide-react"; import { Alert, AlertDescription } from "../ui/alert"; import { Info } from "lucide-react"; import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip"; +import { GitHubMirrorSettings } from "./GitHubMirrorSettings"; +import { Separator } from "../ui/separator"; interface GitHubConfigFormProps { config: GitHubConfig; setConfig: React.Dispatch>; + mirrorOptions: MirrorOptions; + setMirrorOptions: React.Dispatch>; + advancedOptions: AdvancedOptions; + setAdvancedOptions: React.Dispatch>; onAutoSave?: (githubConfig: GitHubConfig) => Promise; + onMirrorOptionsAutoSave?: (mirrorOptions: MirrorOptions) => Promise; + onAdvancedOptionsAutoSave?: (advancedOptions: AdvancedOptions) => Promise; isAutoSaving?: boolean; } -export function GitHubConfigForm({ config, setConfig, onAutoSave, isAutoSaving }: GitHubConfigFormProps) { +export function GitHubConfigForm({ + config, + setConfig, + mirrorOptions, + setMirrorOptions, + advancedOptions, + setAdvancedOptions, + onAutoSave, + onMirrorOptionsAutoSave, + onAdvancedOptionsAutoSave, + isAutoSaving +}: GitHubConfigFormProps) { const [isLoading, setIsLoading] = useState(false); const handleChange = (e: React.ChangeEvent) => { @@ -125,59 +144,25 @@ export function GitHubConfigForm({ config, setConfig, onAutoSave, isAutoSaving }

-
-

Repository Access

+ -
-
- - handleChange({ - target: { - name: "privateRepositories", - type: "checkbox", - checked: Boolean(checked), - value: "", - }, - } as React.ChangeEvent) - } - /> - -
- -
- - handleChange({ - target: { - name: "mirrorStarred", - type: "checkbox", - checked: Boolean(checked), - value: "", - }, - } as React.ChangeEvent) - } - /> - -
-
-
+ { + setConfig(newConfig); + if (onAutoSave) onAutoSave(newConfig); + }} + onMirrorOptionsChange={(newOptions) => { + setMirrorOptions(newOptions); + if (onMirrorOptionsAutoSave) onMirrorOptionsAutoSave(newOptions); + }} + onAdvancedOptionsChange={(newOptions) => { + setAdvancedOptions(newOptions); + if (onAdvancedOptionsAutoSave) onAdvancedOptionsAutoSave(newOptions); + }} + /> diff --git a/src/components/config/GitHubMirrorSettings.tsx b/src/components/config/GitHubMirrorSettings.tsx new file mode 100644 index 0000000..2859de4 --- /dev/null +++ b/src/components/config/GitHubMirrorSettings.tsx @@ -0,0 +1,360 @@ +import React from "react"; +import { Checkbox } from "@/components/ui/checkbox"; +import { Label } from "@/components/ui/label"; +import { Separator } from "@/components/ui/separator"; +import { Badge } from "@/components/ui/badge"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { + Info, + GitBranch, + Star, + Building2, + Lock, + Archive, + GitPullRequest, + Tag, + FileText, + MessageSquare, + Target, + BookOpen, + GitFork +} from "lucide-react"; +import type { GitHubConfig, MirrorOptions, AdvancedOptions } from "@/types/config"; +import { cn } from "@/lib/utils"; + +interface GitHubMirrorSettingsProps { + githubConfig: GitHubConfig; + mirrorOptions: MirrorOptions; + advancedOptions: AdvancedOptions; + onGitHubConfigChange: (config: GitHubConfig) => void; + onMirrorOptionsChange: (options: MirrorOptions) => void; + onAdvancedOptionsChange: (options: AdvancedOptions) => void; +} + +export function GitHubMirrorSettings({ + githubConfig, + mirrorOptions, + advancedOptions, + onGitHubConfigChange, + onMirrorOptionsChange, + onAdvancedOptionsChange, +}: GitHubMirrorSettingsProps) { + + const handleGitHubChange = (field: keyof GitHubConfig, value: boolean) => { + onGitHubConfigChange({ ...githubConfig, [field]: value }); + }; + + const handleMirrorChange = (field: keyof MirrorOptions, value: boolean) => { + onMirrorOptionsChange({ ...mirrorOptions, [field]: value }); + }; + + const handleMetadataComponentChange = (component: keyof MirrorOptions['metadataComponents'], value: boolean) => { + onMirrorOptionsChange({ + ...mirrorOptions, + metadataComponents: { + ...mirrorOptions.metadataComponents, + [component]: value, + }, + }); + }; + + const handleAdvancedChange = (field: keyof AdvancedOptions, value: boolean) => { + onAdvancedOptionsChange({ ...advancedOptions, [field]: value }); + }; + + // When metadata is disabled, all components should be disabled + const isMetadataEnabled = mirrorOptions.mirrorMetadata; + + return ( +
+ {/* Repository Selection Section */} +
+
+

+ + Repository Selection +

+

+ Choose which repositories to include in mirroring +

+
+ +
+
+ handleGitHubChange('privateRepositories', !!checked)} + /> +
+ +

+ Mirror your private repositories (requires appropriate token permissions) +

+
+
+ +
+ handleGitHubChange('mirrorStarred', !!checked)} + /> +
+ +

+ Include repositories you've starred on GitHub +

+
+
+
+
+ + + + {/* Content & Data Section */} +
+
+

+ + Content & Data +

+

+ Select what content to mirror from each repository +

+
+ +
+ {/* Code is always mirrored - shown as info */} +
+ +
+

Source code & branches

+

Always included

+
+ Default +
+ +
+ handleMirrorChange('mirrorReleases', !!checked)} + /> +
+ +

+ Include GitHub releases, tags, and associated assets +

+
+
+ +
+
+ handleMirrorChange('mirrorMetadata', !!checked)} + /> +
+ +

+ Mirror issues, pull requests, and other repository data +

+
+
+ + {/* Metadata sub-options */} + {mirrorOptions.mirrorMetadata && ( +
+
+
+ handleMetadataComponentChange('issues', !!checked)} + disabled={!isMetadataEnabled} + /> + +
+ +
+ handleMetadataComponentChange('pullRequests', !!checked)} + disabled={!isMetadataEnabled} + /> + +
+ +
+ handleMetadataComponentChange('labels', !!checked)} + disabled={!isMetadataEnabled} + /> + +
+ +
+ handleMetadataComponentChange('milestones', !!checked)} + disabled={!isMetadataEnabled} + /> + +
+ +
+ handleMetadataComponentChange('wiki', !!checked)} + disabled={!isMetadataEnabled} + /> + +
+
+
+ )} +
+
+
+ + + + {/* Filtering & Behavior Section */} +
+
+

+ + Filtering & Behavior +

+

+ Fine-tune what gets excluded from mirroring +

+
+ +
+
+ handleAdvancedChange('skipForks', !!checked)} + /> +
+ +

+ Exclude repositories that are forks of other projects +

+
+
+ + {githubConfig.mirrorStarred && ( +
+ handleAdvancedChange('skipStarredIssues', !!checked)} + /> +
+ +

+ Only mirror code from starred repos, skip issues and metadata +

+
+
+ )} +
+
+
+ ); +} \ No newline at end of file