Files
Nico a039bb478e fix: separate raw include paths and patterns (#683)
Separate include patters and included path cleanly to avoid path with special characters to be expanded. Closes https://github.com/nicotsx/zerobyte/discussions/680

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **New Features**
  * Added ability to select specific directories and paths for inclusion in backup schedules, separate from pattern-based rules.

* **Bug Fixes & Improvements**
  * Automatically migrates existing backup configurations to work with the new path selection system.
  * Enhanced backup restoration to properly handle both selected paths and pattern-based inclusions.

* **Chores**
  * Updated database schema to support path selections in backup schedules.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-03-20 19:27:54 +01:00

93 lines
2.9 KiB
TypeScript

import { X } from "lucide-react";
import { VolumeFileBrowser } from "~/client/components/file-browsers/volume-file-browser";
import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "~/client/components/ui/form";
import { Textarea } from "~/client/components/ui/textarea";
import type { Volume } from "~/client/lib/types";
import type { UseFormReturn } from "react-hook-form";
import type { InternalFormValues } from "./types";
type PathsSectionProps = {
form: UseFormReturn<InternalFormValues>;
volume: Volume;
selectedPaths: Set<string>;
onSelectionChange: (paths: Set<string>) => void;
onRemovePath: (path: string) => void;
showAllSelectedPaths: boolean;
onToggleShowAllPaths: () => void;
};
export const PathsSection = ({
form,
volume,
selectedPaths,
onSelectionChange,
onRemovePath,
showAllSelectedPaths,
onToggleShowAllPaths,
}: PathsSectionProps) => {
return (
<>
<VolumeFileBrowser
key={volume.id}
volumeId={volume.shortId}
selectedPaths={selectedPaths}
onSelectionChange={onSelectionChange}
withCheckboxes
className="relative border rounded-md bg-card p-2 h-100 overflow-y-auto"
emptyMessage="This volume appears to be empty."
/>
{selectedPaths.size > 0 && (
<div className="mt-4">
<p className="text-xs text-muted-foreground mb-2">Selected paths:</p>
<div className="flex flex-wrap gap-2">
{Array.from(selectedPaths)
.slice(0, showAllSelectedPaths ? undefined : 20)
.map((path) => (
<span
key={path}
className="text-xs bg-accent px-2 py-1 rounded-md font-mono inline-flex items-center gap-1"
>
{path}
<button
type="button"
onClick={() => onRemovePath(path)}
className="ml-1 hover:bg-destructive/20 rounded p-0.5 transition-colors"
aria-label={`Remove ${path}` as string}
>
<X className="h-3 w-3" />
</button>
</span>
))}
{selectedPaths.size > 20 && (
<button type="button" onClick={onToggleShowAllPaths} className="text-xs text-primary hover:underline">
{showAllSelectedPaths ? "Show less" : `+ ${selectedPaths.size - 20} more`}
</button>
)}
</div>
</div>
)}
<FormField
control={form.control}
name="includePatterns"
render={({ field }) => (
<FormItem className="mt-6">
<FormLabel>Additional include patterns</FormLabel>
<FormControl>
<Textarea
{...field}
placeholder="/data/**&#10;/config/*.json&#10;*.db&#10;**/*.log"
className="font-mono text-sm min-h-25"
/>
</FormControl>
<FormDescription>
Optionally add custom include patterns using glob syntax. Enter one pattern per line. These will be
combined with the paths selected above.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
</>
);
};