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; label?: string;
}; };
/** export const PathSelector = ({ value, onChange }: Props) => {
* 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) => {
const [showBrowser, setShowBrowser] = useState(false); const [showBrowser, setShowBrowser] = useState(false);
if (showBrowser) { if (showBrowser) {
@ -26,25 +21,18 @@ export const PathSelector = ({ value, onChange, label = "Selected path:" }: Prop
}} }}
selectedPath={value} selectedPath={value}
/> />
<Button <Button type="button" variant="ghost" size="sm" onClick={() => setShowBrowser(false)}>
type="button"
variant="ghost"
size="sm"
onClick={() => setShowBrowser(false)}
>
Cancel Cancel
</Button> </Button>
</div> </div>
); );
} }
console.log("Rendering PathSelector with value:", value);
return ( return (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="flex-1 border rounded-md p-3 bg-muted/50"> <div className="flex-1 text-sm font-mono bg-muted px-3 py-2 rounded-md border">{value}</div>
<div className="text-xs font-medium text-muted-foreground mb-1">{label}</div> <Button type="button" variant="outline" onClick={() => setShowBrowser(true)} size="sm">
<div className="text-sm font-mono break-all">{value}</div>
</div>
<Button type="button" variant="outline" size="sm" onClick={() => setShowBrowser(true)}>
Change Change
</Button> </Button>
</div> </div>

View file

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

View file

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

View file

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