refactor: path selector design

This commit is contained in:
Nicolas Meienberger 2025-11-29 17:09:50 +01:00
parent 7f9c020127
commit a6a28f7352
4 changed files with 12 additions and 28 deletions

View file

@ -8,12 +8,7 @@ type Props = {
label?: string;
};
/**
* A reusable path selector component that shows the selected path
* with a "Change" button, and expands to a DirectoryBrowser when clicked.
* Matches the pattern used in the volume creation form.
*/
export const PathSelector = ({ value, onChange, label = "Selected path:" }: Props) => {
export const PathSelector = ({ value, onChange }: Props) => {
const [showBrowser, setShowBrowser] = useState(false);
if (showBrowser) {
@ -26,25 +21,18 @@ export const PathSelector = ({ value, onChange, label = "Selected path:" }: Prop
}}
selectedPath={value}
/>
<Button
type="button"
variant="ghost"
size="sm"
onClick={() => setShowBrowser(false)}
>
<Button type="button" variant="ghost" size="sm" onClick={() => setShowBrowser(false)}>
Cancel
</Button>
</div>
);
}
console.log("Rendering PathSelector with value:", value);
return (
<div className="flex items-center gap-2">
<div className="flex-1 border rounded-md p-3 bg-muted/50">
<div className="text-xs font-medium text-muted-foreground mb-1">{label}</div>
<div className="text-sm font-mono break-all">{value}</div>
</div>
<Button type="button" variant="outline" size="sm" onClick={() => setShowBrowser(true)}>
<div className="flex-1 text-sm font-mono bg-muted px-3 py-2 rounded-md border">{value}</div>
<Button type="button" variant="outline" onClick={() => setShowBrowser(true)} size="sm">
Change
</Button>
</div>

View file

@ -142,7 +142,6 @@ export const SnapshotFileBrowser = (props: Props) => {
path: { name: repositoryName },
body: {
snapshotId: snapshot.short_id,
target: targetPath || undefined,
include: includePaths,
delete: deleteExtraFiles,
excludeXattr: excludeXattrArray && excludeXattrArray.length > 0 ? excludeXattrArray : undefined,
@ -282,11 +281,7 @@ export const SnapshotFileBrowser = (props: Props) => {
</div>
{restoreLocation === "custom" && (
<div className="space-y-2">
<Input
placeholder="/path/to/restore"
value={customTargetPath}
onChange={(e) => setCustomTargetPath(e.target.value)}
/>
<PathSelector value={customTargetPath || "/"} onChange={setCustomTargetPath} />
<p className="text-xs text-muted-foreground">Files will be restored directly to this path</p>
</div>
)}

View file

@ -61,7 +61,7 @@ export const RestoreSnapshotDialog = ({ name, snapshotId }: Props) => {
path: { name },
body: {
snapshotId,
target: values.target || undefined,
targetPath: values.targetPath || undefined,
include: include && include.length > 0 ? include : undefined,
exclude: exclude && exclude.length > 0 ? exclude : undefined,
excludeXattr: excludeXattr && excludeXattr.length > 0 ? excludeXattr : undefined,

View file

@ -14,10 +14,11 @@ import {
} from "~/client/components/ui/form";
import { Input } from "~/client/components/ui/input";
import { Button } from "~/client/components/ui/button";
import { PathSelector } from "~/client/components/path-selector";
const restoreSnapshotFormSchema = type({
path: "string?",
target: "string?",
targetPath: "string?",
include: "string?",
exclude: "string?",
excludeXattr: "string?",
@ -38,7 +39,7 @@ export const RestoreSnapshotForm = ({ formId, onSubmit, className }: Props) => {
resolver: arktypeResolver(restoreSnapshotFormSchema),
defaultValues: {
path: "",
target: "",
targetPath: "",
include: "",
exclude: "",
excludeXattr: "",
@ -72,12 +73,12 @@ export const RestoreSnapshotForm = ({ formId, onSubmit, className }: Props) => {
<FormField
control={form.control}
name="target"
name="targetPath"
render={({ field }) => (
<FormItem>
<FormLabel>Target Directory (Optional)</FormLabel>
<FormControl>
<Input placeholder="/" {...field} />
<PathSelector {...field} value={field.value ?? "/"} />
</FormControl>
<FormDescription>
Restore to a custom location instead of the original path (defaults to /)