refactor: implement project package segment lazy select

This commit is contained in:
2026-06-18 16:30:06 +05:30
parent 6cf363dce1
commit 9e76b8e6d0
31 changed files with 513 additions and 1076 deletions

View File

@@ -1,10 +1,13 @@
'use client';
import type { ComponentProps } from 'react';
import { useRef } from 'react';
import type { FieldErrors, UseFormRegister } from 'react-hook-form';
import { Loader2 } from 'lucide-react';
import { FormField } from '@/components/form';
import { PackageSelect } from '@/components/lookups/PackageSelect';
import { ProjectSelect } from '@/components/lookups/ProjectSelect';
import { Button } from '@/components/ui/button';
import {
Dialog,
@@ -22,7 +25,6 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import type { Package, Project } from '@/types';
import type { SegmentFormValues } from '../hooks/useSegmentForm';
@@ -30,10 +32,6 @@ interface SegmentDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
segmentId?: string;
projects: Project[];
packages: Package[];
isProjectsLoading: boolean;
isPackagesLoading: boolean;
projectId: string;
onProjectChange: (projectId: string) => void;
packageId: string;
@@ -52,10 +50,6 @@ export function SegmentDialog({
open,
onOpenChange,
segmentId,
projects,
packages,
isProjectsLoading,
isPackagesLoading,
projectId,
onProjectChange,
packageId,
@@ -69,6 +63,8 @@ export function SegmentDialog({
canSubmit,
isSaving,
}: SegmentDialogProps) {
const projectSelectPortalRef = useRef<HTMLDivElement | null>(null);
const packageSelectPortalRef = useRef<HTMLDivElement | null>(null);
const getError = (field: keyof SegmentFormValues) =>
isSubmitted ? errors[field]?.message : undefined;
@@ -104,25 +100,13 @@ export function SegmentDialog({
required
error={getError('project_id')}
>
<Select value={projectId} onValueChange={onProjectChange}>
<SelectTrigger aria-invalid={!!getError('project_id')}>
{isProjectsLoading ? (
<span className="flex items-center gap-2 text-muted-foreground">
<Loader2 className="size-4 animate-spin" />
Loading...
</span>
) : (
<SelectValue placeholder="Choose a project" />
)}
</SelectTrigger>
<SelectContent>
{projects.map((project) => (
<SelectItem key={project.id} value={project.id}>
{project.name}
</SelectItem>
))}
</SelectContent>
</Select>
<ProjectSelect
value={projectId}
onValueChange={onProjectChange}
enabled={open}
disabled={isSaving}
portalContainer={projectSelectPortalRef}
/>
<input
type="hidden"
{...register('project_id')}
@@ -136,35 +120,14 @@ export function SegmentDialog({
required
error={getError('package_id')}
>
<Select
<PackageSelect
value={packageId}
onValueChange={onPackageChange}
disabled={!projectId || isPackagesLoading}
>
<SelectTrigger aria-invalid={!!getError('package_id')}>
{isPackagesLoading ? (
<span className="flex items-center gap-2 text-muted-foreground">
<Loader2 className="size-4 animate-spin" />
Loading...
</span>
) : (
<SelectValue
placeholder={
projectId
? 'Choose a package'
: 'Select project first'
}
/>
)}
</SelectTrigger>
<SelectContent>
{packages.map((pkg) => (
<SelectItem key={pkg.id} value={pkg.id}>
{pkg.name}
</SelectItem>
))}
</SelectContent>
</Select>
projectId={projectId}
enabled={open && Boolean(projectId)}
disabled={!projectId || isSaving}
portalContainer={packageSelectPortalRef}
/>
<input
type="hidden"
{...register('package_id')}
@@ -350,6 +313,9 @@ export function SegmentDialog({
</Button>
</DialogFooter>
</form>
<div ref={projectSelectPortalRef} />
<div ref={packageSelectPortalRef} />
</DialogContent>
</Dialog>
);

View File

@@ -44,15 +44,6 @@ export function useAllPackageOptionsQuery() {
});
}
export function usePackagesByProjectQuery(projectId: string, enabled: boolean) {
return useQuery({
queryKey: ['packages', 'by-project', projectId],
queryFn: () =>
packageService.getPackagesByProject(projectId, { skip: 0, limit: 1000 }),
enabled: enabled && Boolean(projectId),
});
}
export function useDeleteSegmentMutation() {
const queryClient = useQueryClient();

View File

@@ -15,7 +15,6 @@ import { useSegmentForm } from './hooks/useSegmentForm';
import {
useAllPackageOptionsQuery,
useDeleteSegmentMutation,
usePackagesByProjectQuery,
useProjectOptionsQuery,
useSegmentsQuery,
} from './hooks/useSegmentQueries';
@@ -48,11 +47,6 @@ export default function SegmentPage() {
canSubmit,
isSaving,
} = segmentForm;
const projectPackagesQuery = usePackagesByProjectQuery(
projectId,
isDialogOpen,
);
const openCreate = useCallback(() => {
prepareCreateSegment();
setIsDialogOpen(true);
@@ -95,7 +89,6 @@ export default function SegmentPage() {
const projects = projectsQuery.data?.items ?? [];
const allPackages = allPackagesQuery.data?.items ?? [];
const projectPackages = projectPackagesQuery.data?.items ?? [];
const segments = segmentsQuery.data?.items ?? [];
const total = segmentsQuery.data?.totalItems ?? 0;
const columns = useSegmentColumns(projects, allPackages);
@@ -133,10 +126,6 @@ export default function SegmentPage() {
open={isDialogOpen}
onOpenChange={handleDialogOpenChange}
segmentId={segmentId}
projects={projects}
packages={projectPackages}
isProjectsLoading={projectsQuery.isLoading}
isPackagesLoading={projectPackagesQuery.isLoading}
projectId={projectId}
onProjectChange={setProjectId}
packageId={packageId}

View File

@@ -4,4 +4,6 @@ export const segmentKeys = {
all: ['segments'] as const,
lists: () => [...segmentKeys.all, 'list'] as const,
list: (params: PaginationParams) => [...segmentKeys.lists(), params] as const,
details: () => [...segmentKeys.all, 'detail'] as const,
detail: (id: string) => [...segmentKeys.details(), id] as const,
};