Skip to content

Commit 8ceba83

Browse files
committed
ENTITIES: used an FSD (feature-sliced design) to rework everything.
1 parent 0c1dd3c commit 8ceba83

File tree

75 files changed

+11285
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+11285
-0
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { api } from '@/shared/api/api';
2+
import type { components, operations } from '@/shared/types/api-types';
3+
import { useInfiniteQuery } from '@tanstack/react-query';
4+
5+
type AuditLogDto = components['schemas']['AuditLogDto'];
6+
type AuditAction = operations['AuditController_findAll_v2']['parameters']['query']['action'];
7+
8+
export interface AuditLogFilters {
9+
actorId?: string;
10+
targetId?: string;
11+
action?: AuditAction;
12+
}
13+
14+
export const AUDIT_LOGS_QUERY_KEY = 'auditLogs';
15+
16+
/**
17+
* Fetches the system-wide audit trail for admins, with support for filtering and infinite scrolling.
18+
*/
19+
export const useAuditLogs = (filters: AuditLogFilters, limit = 25) => {
20+
return useInfiniteQuery({
21+
queryKey: [AUDIT_LOGS_QUERY_KEY, filters],
22+
queryFn: async ({ pageParam = 1 }) => {
23+
const response = await api.get<AuditLogDto[]>('/admin/audit-logs', {
24+
params: {
25+
page: pageParam,
26+
limit,
27+
...filters,
28+
},
29+
});
30+
return response;
31+
},
32+
initialPageParam: 1,
33+
getNextPageParam: (lastPage) => {
34+
if (!lastPage || !lastPage.meta) return undefined;
35+
const { page, lastPage: totalPages } = lastPage.meta;
36+
if (page >= totalPages) {
37+
return undefined;
38+
}
39+
return page + 1;
40+
},
41+
});
42+
};
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import type { operations } from '@/shared/types/api-types';
2+
3+
type AuditAction = operations['AuditController_findAll_v2']['parameters']['query']['action'];
4+
5+
/**
6+
* A static list of all possible audit actions in the system.
7+
* This is used to populate dropdowns for filtering the audit trail.
8+
* It should be kept in sync with the backend API specification.
9+
*/
10+
export const auditActions: AuditAction[] = [
11+
'PROJECT_CREATED',
12+
'PROJECT_UPDATED',
13+
'PROJECT_DELETED',
14+
'PROJECT_ACCESS_UPDATED',
15+
'PROJECT_SPEC_IMPORTED',
16+
'ENDPOINT_STATUS_UPDATED',
17+
'ENVIRONMENT_CREATED',
18+
'ENVIRONMENT_UPDATED',
19+
'ENVIRONMENT_DELETED',
20+
'SECRET_CREATED',
21+
'SECRET_UPDATED',
22+
'SECRET_DELETED',
23+
'SCHEMA_COMPONENT_CREATED',
24+
'SCHEMA_COMPONENT_UPDATED',
25+
'SCHEMA_COMPONENT_DELETED',
26+
'TEAM_CREATED',
27+
'TEAM_UPDATED',
28+
'TEAM_DELETED',
29+
'USER_CREATED',
30+
'USER_UPDATED',
31+
'USER_DELETED',
32+
'USER_PICTURE_UPDATED',
33+
];
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { api } from '@/shared/api/api';
2+
import type { components } from '@/shared/types/api-types';
3+
import { useInfiniteQuery } from '@tanstack/react-query';
4+
5+
type ChangelogDto = components['schemas']['ChangelogDto'];
6+
7+
export const useChangelog = (projectId: string | undefined, limit = 20) => {
8+
return useInfiniteQuery({
9+
queryKey: ['changelog', projectId],
10+
queryFn: async ({ pageParam = 1 }) => {
11+
if (!projectId) return null;
12+
13+
const response = await api.get<ChangelogDto[]>(`/projects/${projectId}/changelog`, {
14+
params: { page: pageParam, limit },
15+
});
16+
17+
return response;
18+
},
19+
initialPageParam: 1,
20+
getNextPageParam: (lastPage) => {
21+
if (!lastPage || !lastPage.meta) return undefined;
22+
23+
const currentPage = lastPage.meta.page;
24+
const totalPages = lastPage.meta.lastPage;
25+
26+
if (currentPage >= totalPages) {
27+
return undefined;
28+
}
29+
return currentPage + 1;
30+
},
31+
enabled: !!projectId,
32+
});
33+
};
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import { AUDIT_LOGS_QUERY_KEY } from '@/entities/AuditLog/api/useAuditLogs';
2+
import { api } from '@/shared/api/api';
3+
import type { components } from '@/shared/types/api-types';
4+
import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
5+
6+
type EndpointSummaryDto = components['schemas']['EndpointSummaryDto'];
7+
type CreateEndpointDto = components['schemas']['CreateEndpointDto'];
8+
type UpdateEndpointDto = components['schemas']['UpdateEndpointDto'];
9+
type EndpointDto = components['schemas']['EndpointDto'];
10+
type UpdateEndpointStatusDto = components['schemas']['UpdateEndpointStatusDto'];
11+
12+
export const useEndpoints = (projectId: string | undefined, limit = 50) => {
13+
return useInfiniteQuery({
14+
queryKey: ['endpoints', projectId],
15+
queryFn: async ({ pageParam = 1 }) => {
16+
if (!projectId) return null;
17+
const response = await api.get<EndpointSummaryDto[]>(
18+
`/projects/${projectId}/endpoints`,
19+
{ params: { page: pageParam, limit } },
20+
);
21+
return response;
22+
},
23+
initialPageParam: 1,
24+
getNextPageParam: (lastPage) => {
25+
if (!lastPage || !lastPage.meta) return undefined;
26+
const { page, lastPage: totalPages } = lastPage.meta;
27+
if (page >= totalPages) return undefined;
28+
return page + 1;
29+
},
30+
enabled: !!projectId,
31+
});
32+
};
33+
34+
export const useEndpoint = (projectId: string | undefined, endpointId: string | undefined) => {
35+
return useQuery({
36+
queryKey: ['endpoint', projectId, endpointId],
37+
queryFn: async () => {
38+
if (!projectId || !endpointId) return null;
39+
const response = await api.get<EndpointDto>(
40+
`/projects/${projectId}/endpoints/${endpointId}`,
41+
);
42+
return response.data;
43+
},
44+
enabled: !!projectId && !!endpointId,
45+
});
46+
};
47+
48+
export const useCreateEndpoint = () => {
49+
const queryClient = useQueryClient();
50+
return useMutation({
51+
mutationFn: async ({
52+
projectId,
53+
endpointData,
54+
}: {
55+
projectId: string;
56+
endpointData: CreateEndpointDto;
57+
}) => {
58+
const response = await api.post<EndpointDto>(
59+
`/projects/${projectId}/endpoints`,
60+
endpointData,
61+
);
62+
return response.data;
63+
},
64+
onSuccess: (_data, variables) => {
65+
queryClient.invalidateQueries({ queryKey: ['endpoints', variables.projectId] });
66+
queryClient.invalidateQueries({ queryKey: ['openapi', variables.projectId] });
67+
queryClient.invalidateQueries({ queryKey: ['project', variables.projectId] });
68+
queryClient.invalidateQueries({ queryKey: ['changelog', variables.projectId] });
69+
},
70+
});
71+
};
72+
73+
export const useUpdateEndpoint = () => {
74+
const queryClient = useQueryClient();
75+
return useMutation({
76+
mutationFn: async ({
77+
projectId,
78+
endpointId,
79+
endpointData,
80+
}: {
81+
projectId: string;
82+
endpointId: string;
83+
endpointData: UpdateEndpointDto;
84+
}) => {
85+
const response = await api.put<EndpointDto>(
86+
`/projects/${projectId}/endpoints/${endpointId}`,
87+
endpointData,
88+
);
89+
return response.data;
90+
},
91+
onSuccess: (_data, variables) => {
92+
queryClient.invalidateQueries({
93+
queryKey: ['endpoint', variables.projectId, variables.endpointId],
94+
});
95+
queryClient.invalidateQueries({ queryKey: ['endpoints', variables.projectId] });
96+
queryClient.invalidateQueries({ queryKey: ['openapi', variables.projectId] });
97+
queryClient.invalidateQueries({ queryKey: ['project', variables.projectId] });
98+
queryClient.invalidateQueries({ queryKey: ['changelog', variables.projectId] });
99+
},
100+
});
101+
};
102+
103+
export const useDeleteEndpoint = () => {
104+
const queryClient = useQueryClient();
105+
return useMutation({
106+
mutationFn: async ({
107+
projectId,
108+
endpointId,
109+
}: {
110+
projectId: string;
111+
endpointId: string;
112+
}) => {
113+
await api.delete<null>(`/projects/${projectId}/endpoints/${endpointId}`);
114+
return endpointId;
115+
},
116+
onSuccess: (_data, variables) => {
117+
queryClient.invalidateQueries({ queryKey: ['endpoints', variables.projectId] });
118+
queryClient.invalidateQueries({ queryKey: ['openapi', variables.projectId] });
119+
queryClient.invalidateQueries({ queryKey: ['project', variables.projectId] });
120+
queryClient.invalidateQueries({ queryKey: ['changelog', variables.projectId] });
121+
},
122+
});
123+
};
124+
125+
export const useUpdateEndpointStatus = () => {
126+
const queryClient = useQueryClient();
127+
return useMutation({
128+
mutationFn: async ({
129+
projectId,
130+
endpointId,
131+
statusData,
132+
}: {
133+
projectId: string;
134+
endpointId: string;
135+
statusData: UpdateEndpointStatusDto;
136+
}) => {
137+
const response = await api.post<EndpointDto>(
138+
`/projects/${projectId}/endpoints/${endpointId}/status`,
139+
statusData,
140+
);
141+
return response.data;
142+
},
143+
onSuccess: (_data, variables) => {
144+
queryClient.invalidateQueries({
145+
queryKey: ['endpoint', variables.projectId, variables.endpointId],
146+
});
147+
queryClient.invalidateQueries({ queryKey: ['endpoints', variables.projectId] });
148+
queryClient.invalidateQueries({ queryKey: ['openapi', variables.projectId] });
149+
queryClient.invalidateQueries({ queryKey: ['project', variables.projectId] });
150+
queryClient.invalidateQueries({ queryKey: ['changelog', variables.projectId] });
151+
queryClient.invalidateQueries({ queryKey: [AUDIT_LOGS_QUERY_KEY] });
152+
},
153+
});
154+
};
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { api } from '@/shared/api/api';
2+
import type { components } from '@/shared/types/api-types';
3+
import { useMutation } from '@tanstack/react-query';
4+
5+
type LockDto = components['schemas']['LockDto'];
6+
7+
/**
8+
* Mutation hook to acquire or refresh a lock on an endpoint.
9+
* On success, returns the lock details.
10+
* Throws an ApiError on failure, which can be caught to handle conflicts (409).
11+
*/
12+
export const useAcquireLock = () => {
13+
return useMutation({
14+
mutationFn: async ({
15+
projectId,
16+
endpointId,
17+
}: {
18+
projectId: string;
19+
endpointId: string;
20+
}) => {
21+
const response = await api.post<LockDto>(
22+
`/projects/${projectId}/endpoints/${endpointId}/lock`,
23+
);
24+
return response.data;
25+
},
26+
});
27+
};
28+
29+
/**
30+
* Mutation hook to release a lock on an endpoint.
31+
* This should be called when the user is finished editing.
32+
*/
33+
export const useReleaseLock = () => {
34+
return useMutation({
35+
mutationFn: async ({
36+
projectId,
37+
endpointId,
38+
}: {
39+
projectId: string;
40+
endpointId: string;
41+
}) => {
42+
await api.delete<null>(`/projects/${projectId}/endpoints/${endpointId}/unlock`);
43+
},
44+
});
45+
};
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import EmptyEndpointMessage from '@/components/general/EmptyEndpointMessage';
2+
import ParametersTable from '@/entities/Endpoint/ui/ParametersTable';
3+
import { OpenAPISpec, ParameterObject } from '@/shared/types/types';
4+
import React from 'react';
5+
6+
interface ParametersTabContentProps {
7+
parameters: ParameterObject[];
8+
openApiSpec: OpenAPISpec;
9+
paramTypeLabel: string;
10+
}
11+
12+
const ParametersTabContent: React.FC<ParametersTabContentProps> = ({
13+
parameters,
14+
openApiSpec,
15+
paramTypeLabel,
16+
}) => {
17+
if (!parameters || parameters.length === 0) {
18+
return <EmptyEndpointMessage type={paramTypeLabel} />;
19+
}
20+
21+
return <ParametersTable parameters={parameters} openApiSpec={openApiSpec} />;
22+
};
23+
24+
export default ParametersTabContent;

0 commit comments

Comments
 (0)