180 lines
7.3 KiB
TypeScript
180 lines
7.3 KiB
TypeScript
import { useEffect } from 'react';
|
|
import { useForm } from 'react-hook-form';
|
|
import { X } from 'lucide-react';
|
|
import { useModuleApi } from '../hooks/useModuleApi';
|
|
import type { Module, ModuleCreate, ModuleUpdate, ModuleStatus } from '../AdminModuleTypes';
|
|
import CustomInput from '../../../../components/custom/CustomInput';
|
|
import CustomButton from '../../../../components/custom/CustomButton';
|
|
|
|
interface ModuleFormProps {
|
|
module: Module | null;
|
|
onClose: (saved: boolean) => void;
|
|
}
|
|
|
|
interface ModuleFormData {
|
|
module_id: string;
|
|
module_name: string;
|
|
description: string;
|
|
icon_url: string;
|
|
status: ModuleStatus;
|
|
display_order: number;
|
|
}
|
|
|
|
const ModuleForm = ({ module, onClose }: ModuleFormProps) => {
|
|
const { createModule, updateModule, loading } = useModuleApi();
|
|
|
|
const { register, handleSubmit, formState: { errors }, reset } = useForm<ModuleFormData>({
|
|
defaultValues: {
|
|
module_id: '',
|
|
module_name: '',
|
|
description: '',
|
|
icon_url: '',
|
|
status: 'active',
|
|
display_order: 0,
|
|
}
|
|
});
|
|
|
|
useEffect(() => {
|
|
if (module) {
|
|
reset({
|
|
module_id: module.module_id,
|
|
module_name: module.module_name,
|
|
description: module.description || '',
|
|
icon_url: module.icon_url || '',
|
|
status: module.status,
|
|
display_order: module.display_order,
|
|
});
|
|
}
|
|
}, [module, reset]);
|
|
|
|
const onSubmit = async (data: ModuleFormData) => {
|
|
let success;
|
|
if (module) {
|
|
const updateData: ModuleUpdate = {
|
|
module_name: data.module_name,
|
|
description: data.description || undefined,
|
|
icon_url: data.icon_url || undefined,
|
|
status: data.status,
|
|
display_order: data.display_order,
|
|
};
|
|
success = await updateModule(module.id, updateData);
|
|
} else {
|
|
const createData: ModuleCreate = {
|
|
module_id: data.module_id,
|
|
module_name: data.module_name,
|
|
description: data.description || undefined,
|
|
icon_url: data.icon_url || undefined,
|
|
status: data.status,
|
|
display_order: data.display_order,
|
|
};
|
|
success = await createModule(createData);
|
|
}
|
|
|
|
if (success) {
|
|
onClose(true);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50 p-4">
|
|
<div className="bg-(--card-bg) rounded-lg shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto border border-(--card-border)">
|
|
<div className="sticky top-0 bg-(--card-bg) border-b border-(--card-border) px-6 py-4 flex items-center justify-between z-10">
|
|
<h2 className="text-xl font-semibold text-(--text-primary)">
|
|
{module ? 'Edit Module' : 'Create Module'}
|
|
</h2>
|
|
<button
|
|
onClick={() => onClose(false)}
|
|
className="p-2 hover:bg-(--background) rounded-lg transition-colors text-(--text-secondary) hover:text-(--text-primary)"
|
|
>
|
|
<X size={20} />
|
|
</button>
|
|
</div>
|
|
|
|
<form onSubmit={handleSubmit(onSubmit)} className="p-6 space-y-4">
|
|
<CustomInput
|
|
label="Module ID *"
|
|
placeholder="e.g., inventory"
|
|
{...register('module_id', {
|
|
required: 'Module ID is required',
|
|
pattern: {
|
|
value: /^[a-z0-9-]+$/,
|
|
message: 'Only lowercase alphanumeric and hyphens allowed'
|
|
}
|
|
})}
|
|
error={errors.module_id?.message}
|
|
disabled={!!module}
|
|
/>
|
|
<p className="text-xs text-(--text-secondary) -mt-3 ml-1">Unique identifier (lowercase, alphanumeric, hyphens)</p>
|
|
|
|
<CustomInput
|
|
label="Module Name *"
|
|
placeholder="e.g., Inventory Management"
|
|
{...register('module_name', { required: 'Module Name is required' })}
|
|
error={errors.module_name?.message}
|
|
/>
|
|
|
|
<div className="space-y-1.5">
|
|
<label className="block text-sm font-medium text-(--text-primary)">
|
|
Description
|
|
</label>
|
|
<textarea
|
|
{...register('description')}
|
|
className="w-full px-3 py-2 border border-(--card-border) rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 bg-(--background) text-(--text-primary) placeholder-(--text-secondary)/50"
|
|
placeholder="Brief description of the module"
|
|
rows={3}
|
|
/>
|
|
</div>
|
|
|
|
<CustomInput
|
|
label="Icon URL"
|
|
placeholder="https://example.com/icon.png"
|
|
type="url"
|
|
{...register('icon_url')}
|
|
/>
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div className="space-y-1.5">
|
|
<label className="block text-sm font-medium text-(--text-primary)">
|
|
Status
|
|
</label>
|
|
<select
|
|
{...register('status')}
|
|
className="w-full px-3 py-2 border border-(--card-border) rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 bg-(--background) text-(--text-primary)"
|
|
>
|
|
<option value="active">Active</option>
|
|
<option value="inactive">Inactive</option>
|
|
</select>
|
|
</div>
|
|
|
|
<CustomInput
|
|
label="Display Order"
|
|
type="number"
|
|
{...register('display_order', { valueAsNumber: true })}
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-3 pt-4">
|
|
<CustomButton
|
|
type="submit"
|
|
loading={loading}
|
|
className="flex-1"
|
|
>
|
|
{module ? 'Update Module' : 'Create Module'}
|
|
</CustomButton>
|
|
<CustomButton
|
|
type="button"
|
|
variant="secondary"
|
|
onClick={() => onClose(false)}
|
|
className="flex-1"
|
|
disabled={loading}
|
|
>
|
|
Cancel
|
|
</CustomButton>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ModuleForm; |