feat: update bucket types and management logic for website access and quotas

- Refactored `Bucket` and `UpdateBucket` types to include detailed website configuration and quota properties.
- Updated hooks to utilize the new `UpdateBucket` type for mutation functions.
- Adjusted form schemas and management logic to reflect changes in website access and quota handling.
- Enhanced default values and reset logic in forms for better user experience.
This commit is contained in:
Adekabang 2025-07-31 15:42:59 -04:00
parent 6528cb759c
commit 621761aeb6
5 changed files with 89 additions and 43 deletions

View File

@ -5,7 +5,7 @@ import {
UseMutationOptions, UseMutationOptions,
useQuery, useQuery,
} from "@tanstack/react-query"; } from "@tanstack/react-query";
import { Bucket, Permissions } from "../types"; import { Bucket, Permissions, UpdateBucket } from "../types";
export const useBucket = (id?: string | null) => { export const useBucket = (id?: string | null) => {
return useQuery({ return useQuery({
@ -17,8 +17,11 @@ export const useBucket = (id?: string | null) => {
export const useUpdateBucket = (id?: string | null) => { export const useUpdateBucket = (id?: string | null) => {
return useMutation({ return useMutation({
mutationFn: (values: Partial<Bucket>) => { mutationFn: (values: Partial<UpdateBucket>) => {
return api.put<Bucket>("/v2/UpdateBucket", { params: { id }, body: values }); return api.post<Bucket>("/v2/UpdateBucket", {
params: { id },
body: values,
});
}, },
}); });
}; };

View File

@ -13,35 +13,48 @@ const QuotaSection = () => {
const form = useForm<QuotaSchema>({ const form = useForm<QuotaSchema>({
resolver: zodResolver(quotaSchema), resolver: zodResolver(quotaSchema),
defaultValues: {
enabled: false,
maxObjects: null,
maxSize: null,
},
}); });
const isEnabled = useWatch({ control: form.control, name: "enabled" }); const isEnabled = useWatch({ control: form.control, name: "enabled" });
const updateMutation = useUpdateBucket(data?.id); const updateMutation = useUpdateBucket(data?.id);
const onChange = useDebounce((values: DeepPartial<QuotaSchema>) => { const handleChange = useDebounce((values: DeepPartial<QuotaSchema>) => {
const { enabled } = values; const { enabled } = values;
const maxObjects = Number(values.maxObjects); const maxObjects = Number(values.maxObjects);
const maxSize = Math.round(Number(values.maxSize) * 1024 ** 3); const maxSize = Math.round(Number(values.maxSize) * 1024 ** 3);
const data = { const quotaData = {
maxObjects: enabled && maxObjects > 0 ? maxObjects : null, maxObjects: enabled && maxObjects > 0 ? maxObjects : null,
maxSize: enabled && maxSize > 0 ? maxSize : null, maxSize: enabled && maxSize > 0 ? maxSize : null,
}; };
updateMutation.mutate({ quotas: data }); updateMutation.mutate({ quotas: quotaData });
}); });
// Reset form when data changes without triggering watch
useEffect(() => { useEffect(() => {
form.reset({ if (!data) return;
enabled:
data?.quotas?.maxSize != null || data?.quotas?.maxObjects != null,
maxSize: data?.quotas?.maxSize ? data?.quotas?.maxSize / 1024 ** 3 : null,
maxObjects: data?.quotas?.maxObjects || null,
});
const { unsubscribe } = form.watch((values) => onChange(values)); const formValues = {
return unsubscribe; enabled:
}, [data]); data.quotas?.maxSize != null || data.quotas?.maxObjects != null,
maxSize: data.quotas?.maxSize ? data.quotas?.maxSize / 1024 ** 3 : null,
maxObjects: data.quotas?.maxObjects || null,
};
form.reset(formValues, { keepDirty: false });
}, [data, form]);
// Set up form watcher
useEffect(() => {
const subscription = form.watch(handleChange);
return () => subscription.unsubscribe();
}, [form, handleChange]);
return ( return (
<div className="mt-8"> <div className="mt-8">

View File

@ -16,6 +16,11 @@ const WebsiteAccessSection = () => {
const { data: config } = useConfig(); const { data: config } = useConfig();
const form = useForm<WebsiteConfigSchema>({ const form = useForm<WebsiteConfigSchema>({
resolver: zodResolver(websiteConfigSchema), resolver: zodResolver(websiteConfigSchema),
defaultValues: {
websiteAccess: false,
indexDocument: "index.html",
errorDocument: "error/400.html",
},
}); });
const isEnabled = useWatch({ control: form.control, name: "websiteAccess" }); const isEnabled = useWatch({ control: form.control, name: "websiteAccess" });
@ -24,38 +29,46 @@ const WebsiteAccessSection = () => {
const updateMutation = useUpdateBucket(data?.id); const updateMutation = useUpdateBucket(data?.id);
const onChange = useDebounce((values: DeepPartial<WebsiteConfigSchema>) => { const handleChange = useDebounce((values: DeepPartial<WebsiteConfigSchema>) => {
const websiteData = { const websiteData = {
enabled: values.websiteAccess, enabled: values.websiteAccess,
indexDocument: values.websiteAccess indexDocument: values.websiteAccess
? values.websiteConfig?.indexDocument ? values.indexDocument
: undefined, : undefined,
errorDocument: values.websiteAccess errorDocument: values.websiteAccess
? values.websiteConfig?.errorDocument ? values.errorDocument
: undefined, : undefined,
}; };
updateMutation.mutate({ updateMutation.mutate({
websiteAccess: values.websiteAccess, websiteAccess: {
websiteConfig: values.websiteAccess && websiteData.indexDocument && websiteData.errorDocument ? { enabled: values.websiteAccess ?? false,
indexDocument: websiteData.indexDocument, indexDocument: values.websiteAccess
errorDocument: websiteData.errorDocument, ? websiteData.indexDocument ?? "index.html"
} : null, : null,
errorDocument: values.websiteAccess
? websiteData.errorDocument ?? "error/400.html"
: null,
}
}); });
}); });
// Reset form when data changes without triggering watch
useEffect(() => { useEffect(() => {
form.reset({ if (!data) return;
websiteAccess: data?.websiteAccess,
websiteConfig: {
indexDocument: data?.websiteConfig?.indexDocument || "index.html",
errorDocument: data?.websiteConfig?.errorDocument || "error/400.html",
},
});
const { unsubscribe } = form.watch((values) => onChange(values)); form.reset({
return unsubscribe; websiteAccess: data?.websiteAccess ?? false,
}, [data, form, onChange]); indexDocument: data?.websiteConfig?.indexDocument || "index.html",
errorDocument: data?.websiteConfig?.errorDocument || "error/400.html",
}, { keepDirty: false });
}, [data, form]);
// Set up form watcher
useEffect(() => {
const subscription = form.watch(handleChange);
return () => subscription.unsubscribe();
}, [form, handleChange]);
return ( return (
<div className="mt-8"> <div className="mt-8">
@ -79,12 +92,12 @@ const WebsiteAccessSection = () => {
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<InputField <InputField
form={form} form={form}
name="websiteConfig.indexDocument" name="indexDocument"
title="Index Document" title="Index Document"
/> />
<InputField <InputField
form={form} form={form}
name="websiteConfig.errorDocument" name="errorDocument"
title="Error Document" title="Error Document"
/> />
</div> </div>

View File

@ -8,9 +8,8 @@ export type AddAliasSchema = z.infer<typeof addAliasSchema>;
export const websiteConfigSchema = z.object({ export const websiteConfigSchema = z.object({
websiteAccess: z.boolean(), websiteAccess: z.boolean(),
websiteConfig: z indexDocument: z.string().nullish(),
.object({ indexDocument: z.string(), errorDocument: z.string() }) errorDocument: z.string().nullish(),
.nullish(),
}); });
export type WebsiteConfigSchema = z.infer<typeof websiteConfigSchema>; export type WebsiteConfigSchema = z.infer<typeof websiteConfigSchema>;

View File

@ -7,7 +7,29 @@ export type Bucket = {
globalAliases: string[]; globalAliases: string[];
localAliases: LocalAlias[]; localAliases: LocalAlias[];
websiteAccess: boolean; websiteAccess: boolean;
websiteConfig?: WebsiteConfig | null; websiteConfig: {
indexDocument: string | null;
errorDocument: string | null;
};
keys: Key[];
objects: number;
bytes: number;
unfinishedUploads: number;
unfinishedMultipartUploads: number;
unfinishedMultipartUploadParts: number;
unfinishedMultipartUploadBytes: number;
quotas: Quotas;
};
export type UpdateBucket = {
id: string;
globalAliases: string[];
localAliases: LocalAlias[];
websiteAccess: {
enabled: boolean;
indexDocument: string | null;
errorDocument: string | null;
};
keys: Key[]; keys: Key[];
objects: number; objects: number;
bytes: number; bytes: number;
@ -36,10 +58,6 @@ export type Permissions = {
owner: boolean; owner: boolean;
}; };
export type WebsiteConfig = {
indexDocument: string;
errorDocument: string;
};
export type Quotas = { export type Quotas = {
maxSize: number | null; maxSize: number | null;