Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,539 changes: 862 additions & 677 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@
"@types/react-color": "^3.0.13",
"@types/react-dom": "^19",
"eslint": "^9.39.4",
"eslint-config-next": "16.1.6",
"eslint-config-next": "16.2.4",
"eslint-config-prettier": "^10.1.8",
"typescript": "^5"
"typescript": "~5.8.3"
}
}
4 changes: 2 additions & 2 deletions src/app/api/hawkbit/[...path]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ export async function GET(request: NextRequest, context: { params: Promise<{ pat
return new NextResponse(axiosResponse.data, {
status: axiosResponse.status,
headers: {
'Content-Type': axiosResponse.headers['content-type'] ?? 'application/octet-stream',
'Content-Disposition': axiosResponse.headers['content-disposition'] ?? '',
'Content-Type': (axiosResponse.headers['content-type'] as string) ?? 'application/octet-stream',
'Content-Disposition': (axiosResponse.headers['content-disposition'] as string) ?? '',
},
});
}
Expand Down
25 changes: 12 additions & 13 deletions src/app/components/collapsible/index.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,20 @@
import React, { createContext, useContext, useState, useRef, useEffect, ReactNode, RefObject } from 'react';
import React, { createContext, useContext, useState, useRef, useEffect, ReactNode } from 'react';
import styles from './styles.module.scss';

type CollapsibleContextType = {
isOpen: boolean;
toggle: () => void;
contentRef: RefObject<HTMLDivElement | null>;
height: string;
};

const CollapsibleContext = createContext<CollapsibleContextType | null>(null);

export function Collapsible({ children, defaultOpen = false }: { children: ReactNode; defaultOpen?: boolean }) {
const [isOpen, setIsOpen] = useState(defaultOpen);
const contentRef = useRef<HTMLDivElement>(null);
const [height, setHeight] = useState('0px');

const toggle = () => setIsOpen((prev) => !prev);

useEffect(() => {
if (contentRef.current) {
setHeight(isOpen ? `${contentRef.current.scrollHeight}px` : '0px');
}
}, [isOpen, children]);

return (
<CollapsibleContext.Provider value={{ isOpen, toggle, contentRef, height }}>
<CollapsibleContext.Provider value={{ isOpen, toggle }}>
<div className={styles.collapsible}>{children}</div>
</CollapsibleContext.Provider>
);
Expand All @@ -46,8 +36,17 @@ function Content({ children }: { children: ReactNode }) {
const ctx = useContext(CollapsibleContext);
if (!ctx) throw new Error('Content must be used within Collapsible');

const contentRef = useRef<HTMLDivElement>(null);
const [height, setHeight] = useState('0px');

useEffect(() => {
if (contentRef.current) {
setHeight(ctx.isOpen ? `${contentRef.current.scrollHeight}px` : '0px');
}
}, [ctx.isOpen, children]);

return (
<div ref={ctx.contentRef} className={styles.collapsible__content} style={{ maxHeight: ctx.height }}>
<div ref={contentRef} className={styles.collapsible__content} style={{ maxHeight: height }}>
<div className={styles.collapsible__inner}>{children}</div>
</div>
);
Expand Down
4 changes: 1 addition & 3 deletions src/app/hooks/use-filter-multiple-select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ export function useFilterMultipleSelect<T>({
);

useEffect(() => {
console.log('Selected options changed:', selectedOptions);

const isFilterPropertyEmpty = filter.current.property === '' || filter.current.property === undefined;

filter.current.setValues(selectedOptions.map((opt) => [isFilterPropertyEmpty ? '' : '==', getOptionLabel(opt)]));
Expand All @@ -47,7 +45,7 @@ export function useFilterMultipleSelect<T>({
setFilters(newFilters);

onFilterChanged(newFilters);
}, [selectedOptions]);
}, [selectedOptions, getOptionLabel, filters, setFilters, onFilterChanged]);

return {
allOptions,
Expand Down
13 changes: 6 additions & 7 deletions src/app/x/configuration/components/configuration-form/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useState } from 'react';
import Checkbox from '@/app/components/checkbox';
import Select from '@/app/components/select';
import Input from '@/app/components/input';
Expand Down Expand Up @@ -111,6 +111,11 @@ export default function ConfigurationForm({ onSubmit, initialValues, loading = f
};

const [form, setForm] = useState<FormStateType>(initialValues ?? defaultFormState);
const [prevInitialValues, setPrevInitialValues] = useState(initialValues);
if (initialValues !== prevInitialValues) {
setPrevInitialValues(initialValues);
setForm(initialValues ?? defaultFormState);
}

const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
Expand Down Expand Up @@ -155,12 +160,6 @@ export default function ConfigurationForm({ onSubmit, initialValues, loading = f
const maybeSkeleton = (content: React.ReactNode, width: string | number = '100%', height: string | number = 24) =>
loading ? <Skeleton width={width} height={height} /> : content;

useEffect(() => {
if (initialValues) {
setForm(initialValues);
}
}, [initialValues]);

return (
<form className={styles.form} onSubmit={handleSubmit} onReset={handleReset}>
{/* Repository */}
Expand Down
19 changes: 8 additions & 11 deletions src/app/x/deployment/components/distribution-tag-form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Input from '@/app/components/input';
import TextArea from '@/app/components/text-area';
import { ActionButtons } from '@/app/components/action-buttons';
import { useForm } from 'react-hook-form';
import { useEffect, useState } from 'react';
import { useState } from 'react';
import { ChromePicker } from 'react-color';
import Text from '@/app/components/text';

Expand All @@ -27,23 +27,20 @@ export default function DistributionTagForm({ onSubmit, onCancel, defaultValues,
register,
handleSubmit,
formState: { errors },
reset,
setValue,
} = useForm<FormData>({
defaultValues,
values: defaultValues as FormData | undefined,
});

const [color, setColor] = useState<string>('#FFFFFF');
const [color, setColor] = useState<string>(defaultValues?.color ?? '#FFFFFF');
const [prevDefaultColor, setPrevDefaultColor] = useState(defaultValues?.color);
if (defaultValues?.color !== prevDefaultColor) {
setPrevDefaultColor(defaultValues?.color);
if (defaultValues?.color) setColor(defaultValues.color);
}

const isEditing = mode === 'edit';

useEffect(() => {
if (defaultValues) {
reset(defaultValues);
if (defaultValues.color) setColor(defaultValues.color);
}
}, [defaultValues, reset]);

return (
<>
<div className={styles.container}>
Expand Down
19 changes: 8 additions & 11 deletions src/app/x/deployment/components/target-tag-form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Input from '@/app/components/input';
import TextArea from '@/app/components/text-area';
import { ActionButtons } from '@/app/components/action-buttons';
import { useForm } from 'react-hook-form';
import { useEffect, useState } from 'react';
import { useState } from 'react';
import { ChromePicker } from 'react-color';
import Text from '@/app/components/text';

Expand All @@ -27,23 +27,20 @@ export default function TargetTagForm({ onSubmit, onCancel, defaultValues, mode
register,
handleSubmit,
formState: { errors },
reset,
setValue,
} = useForm<FormData>({
defaultValues,
values: defaultValues as FormData | undefined,
});

const [color, setColor] = useState<string>('#FFFFFF');
const [color, setColor] = useState<string>(defaultValues?.color ?? '#FFFFFF');
const [prevDefaultColor, setPrevDefaultColor] = useState(defaultValues?.color);
if (defaultValues?.color !== prevDefaultColor) {
setPrevDefaultColor(defaultValues?.color);
if (defaultValues?.color) setColor(defaultValues.color);
}

const isEditing = mode === 'edit';

useEffect(() => {
if (defaultValues) {
reset(defaultValues);
if (defaultValues.color) setColor(defaultValues.color);
}
}, [defaultValues, reset]);

return (
<>
<div className={styles.container}>
Expand Down
19 changes: 8 additions & 11 deletions src/app/x/deployment/components/target-type-form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Input from '@/app/components/input';
import TextArea from '@/app/components/text-area';
import { ActionButtons } from '@/app/components/action-buttons';
import { useForm } from 'react-hook-form';
import { useEffect, useState } from 'react';
import { useState } from 'react';
import { ChromePicker } from 'react-color';
import Text from '@/app/components/text';

Expand All @@ -27,23 +27,20 @@ export default function TargetTypeForm({ onSubmit, onCancel, defaultValues, mode
register,
handleSubmit,
formState: { errors },
reset,
setValue,
} = useForm<FormData>({
defaultValues,
values: defaultValues as FormData | undefined,
});

const [color, setColor] = useState<string>('#FFFFFF');
const [color, setColor] = useState<string>(defaultValues?.color ?? '#FFFFFF');
const [prevDefaultColor, setPrevDefaultColor] = useState(defaultValues?.color);
if (defaultValues?.color !== prevDefaultColor) {
setPrevDefaultColor(defaultValues?.color);
if (defaultValues?.color) setColor(defaultValues.color);
}

const isEditing = mode === 'edit';

useEffect(() => {
if (defaultValues) {
reset(defaultValues);
if (defaultValues.color) setColor(defaultValues.color);
}
}, [defaultValues, reset]);

return (
<>
<div className={styles.container}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ export default function DeploymentDndLayoutContainer() {

const [isDragging, setIsDragging] = useState(false);
const [isDraggingDistribution, setIsDraggingDistribution] = useState(false);
const draggedTargets = useRef<Target[]>([]);
const draggedDistributions = useRef<Distribution[]>([]);
const [draggedTargets, setDraggedTargets] = useState<Target[]>([]);
const [draggedDistributions, setDraggedDistributions] = useState<Distribution[]>([]);

const selectedTarget = useTargetsTableStore((state) => state.selectedTarget);

Expand All @@ -77,13 +77,13 @@ export default function DeploymentDndLayoutContainer() {
console.log('draggedData', draggedData);

if (isTargetRecord(draggedData)) {
draggedTargets.current = Object.values(draggedData);
draggedDistributions.current = [];
setDraggedTargets(Object.values(draggedData));
setDraggedDistributions([]);
}
if (isDistributionRecord(draggedData)) {
setIsDraggingDistribution(true);
draggedDistributions.current = Object.values(draggedData);
draggedTargets.current = [];
setDraggedDistributions(Object.values(draggedData));
setDraggedTargets([]);
}
}

Expand Down Expand Up @@ -169,8 +169,8 @@ export default function DeploymentDndLayoutContainer() {
<DragOverlay>
{isDragging ? (
<DraggedItemPreview
name={isDraggingDistribution ? draggedDistributions.current[0]?.name : draggedTargets.current[0]?.name}
count={isDraggingDistribution ? draggedDistributions.current.length : draggedTargets.current.length}
name={isDraggingDistribution ? draggedDistributions[0]?.name : draggedTargets[0]?.name}
count={isDraggingDistribution ? draggedDistributions.length : draggedTargets.length}
/>
) : null}
</DragOverlay>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import DistributionSetsCard from '../../components/distribution-sets-card';
import SoftwareModulesCard from '@/app/x/software-modules/components/software-module-card';
import { DndContext, DragEndEvent, DragOverlay, DragStartEvent, MouseSensor, TouchSensor, useSensor, useSensors } from '@dnd-kit/core';
import DraggedItemPreview from '@/app/components/dragged-item-preview';
import { useCallback, useRef, useState } from 'react';
import { useCallback, useState } from 'react';
import { Distribution, isDistribution, isDistributionRecord, isSoftwareModule, isSoftwareModuleRecord, SoftwareModule } from '@/entities';
import { useConfirmDialog } from '@/app/hooks';
import ConfirmationModal from '@/app/components/confirmation-modal';
Expand All @@ -30,10 +30,10 @@ export default function DistributionSetsLayoutContainer() {
const { refetch: refetchDistributionSets } = useGetPaginatedDistributionSets({ queryOptions: { enabled: false } });

const [isDragging, setIsDragging] = useState(false);
const draggedEntity = useRef<'Distribution' | 'Module' | undefined>(undefined);
const [draggedEntity, setDraggedEntity] = useState<'Distribution' | 'Module' | undefined>(undefined);

const draggedDistributions = useRef<Distribution[]>([]);
const draggedModules = useRef<SoftwareModule[]>([]);
const [draggedDistributions, setDraggedDistributions] = useState<Distribution[]>([]);
const [draggedModules, setDraggedModules] = useState<SoftwareModule[]>([]);

const assignConfirmationModal = useConfirmDialog<{ softwareModules: SoftwareModule[]; distributions: Distribution[] }>();

Expand All @@ -42,14 +42,14 @@ export default function DistributionSetsLayoutContainer() {
setIsDragging(true);

if (isSoftwareModuleRecord(draggedData)) {
draggedEntity.current = 'Module';
draggedModules.current = Object.values(draggedData);
draggedDistributions.current = [];
setDraggedEntity('Module');
setDraggedModules(Object.values(draggedData));
setDraggedDistributions([]);
}
if (isDistributionRecord(draggedData)) {
draggedEntity.current = 'Distribution';
draggedDistributions.current = Object.values(draggedData);
draggedModules.current = [];
setDraggedEntity('Distribution');
setDraggedDistributions(Object.values(draggedData));
setDraggedModules([]);
}
}

Expand Down Expand Up @@ -88,7 +88,7 @@ export default function DistributionSetsLayoutContainer() {

function resetDragging() {
setIsDragging(false);
draggedEntity.current = undefined;
setDraggedEntity(undefined);
}

async function handleAssignModulesToDistributions(params: { distributions: Distribution[]; softwareModules: SoftwareModule[] }) {
Expand All @@ -106,19 +106,19 @@ export default function DistributionSetsLayoutContainer() {
}

const getDraggedItemPreviewProps = useCallback(() => {
if (draggedEntity.current === 'Distribution') {
if (draggedEntity === 'Distribution') {
return {
name: draggedDistributions.current[0]?.name,
count: draggedDistributions.current.length,
name: draggedDistributions[0]?.name,
count: draggedDistributions.length,
};
}
if (draggedEntity.current === 'Module') {
if (draggedEntity === 'Module') {
return {
name: draggedModules.current[0]?.name,
count: draggedModules.current.length,
name: draggedModules[0]?.name,
count: draggedModules.length,
};
}
}, []);
}, [draggedEntity, draggedDistributions, draggedModules]);

const getDraggedItemsContent = useCallback(() => {
if (!assignConfirmationModal.data) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,15 @@ export function SelectTargetFilterContainer({ targetFilters, disabled = false, o
formState: { errors },
} = useFormContext<CreateRolloutFormData>();

const [prevHandledCount, setPrevHandledCount] = useState<number | undefined>(undefined);
if (selectedTargetsCount !== undefined && selectedTargetsCount !== prevHandledCount) {
setPrevHandledCount(selectedTargetsCount);
setTargetFilterQuery(undefined);
}

useEffect(() => {
if (selectedTargetsCount) {
onSelectedTargetsChange(selectedTargetsCount);
setTargetFilterQuery(undefined);
}
}, [selectedTargetsCount, onSelectedTargetsChange]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,11 @@ export function TotalTargetsPerStatusCell({ totalTargetsPerStatus }: TotalTarget
return null;
}

for (const status in totalTargetsPerStatus) {
const statusKey = status as TotalTargetCountStatus;
if (totalTargetsPerStatus[statusKey] === 0) {
delete totalTargetsPerStatus[statusKey];
}
}
const nonZeroTargetsPerStatus = Object.entries(totalTargetsPerStatus).filter(([, count]) => count !== 0);

return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
{Object.entries(totalTargetsPerStatus).map(([status, count]) => (
{nonZeroTargetsPerStatus.map(([status, count]) => (
<TotalTargetsPerStatus key={status} status={status as TotalTargetCountStatus} count={count} />
))}
</div>
Expand Down
Loading