| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- "use client";
- import { useEffect, useState } from "react";
- import PathConfigEditor from "./PathConfigEditor";
- import SlideInForm from "./SlideInForm";
- interface DatasetConfig {
- [path: string]: any;
- }
- interface DatasetCrudProps {
- datasetName?: string;
- datasetConfig?: DatasetConfig;
- onSave: (name: string, config: DatasetConfig) => void;
- onClose: () => void;
- onEnabledChange?: (enabled: boolean) => void;
- isOpen: boolean;
- }
- export default function DatasetCrud({
- datasetName = "",
- datasetConfig = {},
- onSave,
- onClose,
- onEnabledChange,
- isOpen
- }: DatasetCrudProps) {
- const [name, setName] = useState(datasetName);
- const [config, setConfig] = useState<DatasetConfig>(datasetConfig);
- const [newPath, setNewPath] = useState("");
- const [enabled, setEnabled] = useState(true);
- const isEditing = !!datasetName;
- useEffect(() => {
- setName(datasetName);
- setConfig(datasetConfig);
- // Set enabled state from existing config, defaulting to true
- setEnabled(
- datasetConfig.enabled !== undefined ? datasetConfig.enabled : true
- );
- }, [datasetName, datasetConfig]);
- const handleEnabledChange = (newEnabled: boolean) => {
- setEnabled(newEnabled);
- if (onEnabledChange) {
- onEnabledChange(newEnabled);
- }
- };
- const handleSubmit = (e: React.FormEvent) => {
- e.preventDefault();
- if (!name.trim()) return;
- // Note: enabled is handled separately via onEnabledChange
- // so we don't include it in the config here
- onSave(name, config);
- };
- const addPath = () => {
- if (!newPath.trim()) return;
- setConfig({
- ...config,
- [newPath]: {}
- });
- setNewPath("");
- };
- const removePath = (path: string) => {
- const newConfig = { ...config };
- delete newConfig[path];
- setConfig(newConfig);
- };
- const updatePathConfig = (path: string, pathConfig: any) => {
- try {
- const parsedConfig =
- typeof pathConfig === "string" ? JSON.parse(pathConfig) : pathConfig;
- setConfig({
- ...config,
- [path]: parsedConfig
- });
- } catch {
- // Invalid JSON, ignore
- }
- };
- return (
- <SlideInForm
- isOpen={isOpen}
- onClose={onClose}
- title={isEditing ? `Edit Dataset: ${datasetName}` : "Add New Dataset"}
- actions={
- <div className="flex justify-end space-x-3">
- <button
- type="button"
- onClick={onClose}
- className="inline-flex items-center rounded-md border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-200 shadow-sm hover:bg-gray-50 dark:hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
- >
- Cancel
- </button>
- <button
- type="submit"
- onClick={handleSubmit}
- className="inline-flex items-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
- >
- {isEditing ? "Update Dataset" : "Add Dataset"}
- </button>
- </div>
- }
- >
- <form onSubmit={handleSubmit} className="space-y-6">
- {/* Dataset Name */}
- <div>
- <label className="block text-sm font-medium text-gray-700 dark:text-gray-200 mb-1">
- Dataset Name
- </label>
- <input
- type="text"
- value={name}
- onChange={(e) => setName(e.target.value)}
- placeholder="e.g., pr0n, kids, movies"
- className="block w-full rounded-md border-gray-300 dark:border-gray-700 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100"
- required
- />
- </div>
- {/* Enabled Toggle */}
- <div>
- <label className="block text-sm font-medium text-gray-700 dark:text-gray-200 mb-2">
- Status
- </label>
- <div className="flex items-center">
- <button
- type="button"
- onClick={() => handleEnabledChange(!enabled)}
- className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 ${
- enabled ? "bg-indigo-600" : "bg-gray-200 dark:bg-gray-700"
- }`}
- >
- <span
- className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
- enabled ? "translate-x-6" : "translate-x-1"
- }`}
- />
- </button>
- <span className="ml-3 text-sm text-gray-700 dark:text-gray-200">
- {enabled ? "Enabled" : "Disabled"}
- </span>
- </div>
- <p className="text-xs text-gray-500 mt-1">
- When disabled, this dataset will not be monitored for new files.
- </p>
- </div>
- {/* Paths Configuration */}
- <div>
- <label className="block text-sm font-medium text-gray-700 dark:text-gray-200 mb-2">
- Watch Paths
- </label>
- <p className="text-xs text-gray-500 mb-4">
- Add paths to watch for this dataset. Each path can have its own
- configuration.
- </p>
- {/* Add new path */}
- <div className="flex gap-2 mb-4">
- <input
- type="text"
- value={newPath}
- onChange={(e) => setNewPath(e.target.value)}
- placeholder="/path/to/watch"
- className="flex-1 rounded-md border-gray-300 dark:border-gray-700 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100"
- />
- <button
- type="button"
- onClick={addPath}
- className="inline-flex items-center rounded-md bg-green-600 px-3 py-2 text-sm font-medium text-white shadow-sm hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2"
- >
- Add Path
- </button>
- </div>
- {/* Existing paths */}
- {Object.entries(config).filter(([key]) => key !== "enabled").length >
- 0 ? (
- <div className="space-y-3">
- {Object.entries(config)
- .filter(([key]) => key !== "enabled")
- .map(([path, pathConfig]) => (
- <div
- key={path}
- className="border rounded-lg p-3 bg-gray-50 dark:bg-gray-800"
- >
- <div className="flex items-center justify-between mb-2">
- <span className="text-sm font-mono text-gray-900 dark:text-gray-100">
- {path}
- </span>
- <button
- type="button"
- onClick={() => removePath(path)}
- className="inline-flex items-center rounded-md bg-red-600 px-2 py-1 text-xs font-medium text-white shadow-sm hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2"
- >
- Remove
- </button>
- </div>
- <PathConfigEditor
- value={pathConfig}
- onChange={(newConfig) =>
- updatePathConfig(path, newConfig)
- }
- />
- </div>
- ))}
- </div>
- ) : (
- <p className="text-sm text-gray-500 italic text-center py-4">
- No paths configured. Add a path above.
- </p>
- )}
- </div>
- </form>
- </SlideInForm>
- );
- }
|