Skip to content

Commit 13db224

Browse files
authored
refactor: view components & hooks to be modular (#520)
* refactor: auth hooks * refactor: activities hook * refactor: container hooks * refactor: extension detail hooks * refactor: register page hooks * feat: support for typed translations (#521) * feat: add types support for translation files * fix: import path to use @ in use-translations
1 parent 4b2412b commit 13db224

File tree

19 files changed

+840
-702
lines changed

19 files changed

+840
-702
lines changed
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
'use client';
2+
3+
import React, { useState, useEffect, useMemo } from 'react';
4+
import { useTranslation } from '@/hooks/use-translation';
5+
import { useSearchable } from '@/hooks/use-searchable';
6+
import { SortOption } from '@/components/ui/sort-selector';
7+
import { useGetActivitiesQuery } from '@/redux/services/audit';
8+
import { ActivityMessage } from '@/redux/types/audit';
9+
10+
export const getActionColor = (actionColor: string) => {
11+
switch (actionColor) {
12+
case 'green':
13+
return 'bg-green-500';
14+
case 'blue':
15+
return 'bg-blue-500';
16+
case 'red':
17+
return 'bg-red-500';
18+
default:
19+
return 'bg-gray-500';
20+
}
21+
};
22+
23+
export const resourceTypeOptions: { value: string; label: string }[] = [
24+
{ value: 'all', label: 'All Resources' },
25+
{ value: 'user', label: 'Users' },
26+
{ value: 'organization', label: 'Organization' },
27+
{ value: 'application', label: 'Applications' },
28+
{ value: 'deployment', label: 'Deployments' },
29+
{ value: 'domain', label: 'Domains' },
30+
{ value: 'file_manager', label: 'Files' },
31+
{ value: 'container', label: 'Containers' },
32+
{ value: 'role', label: 'Roles' },
33+
{ value: 'permission', label: 'Permissions' },
34+
{ value: 'feature_flag', label: 'Feature Flags' },
35+
{ value: 'notification', label: 'Notifications' },
36+
{ value: 'integration', label: 'Integrations' },
37+
{ value: 'terminal', label: 'Terminal' },
38+
{ value: 'audit', label: 'Audit' },
39+
];
40+
41+
export interface ActivityListProps {
42+
title?: string;
43+
description?: string;
44+
showFilters?: boolean;
45+
pageSize?: number;
46+
}
47+
48+
49+
function useActivities() {
50+
const { t } = useTranslation();
51+
const [resourceType, setResourceType] = useState('all');
52+
const [currentPage, setCurrentPage] = useState(1);
53+
const ITEMS_PER_PAGE = 20;
54+
55+
const { data: activitiesData, isLoading, error } = useGetActivitiesQuery({
56+
page: 1,
57+
pageSize: 100,
58+
search: '',
59+
resource_type: resourceType === 'all' ? '' : resourceType,
60+
});
61+
62+
const {
63+
filteredAndSortedData: filteredAndSortedActivities,
64+
searchTerm,
65+
handleSearchChange,
66+
handleSortChange,
67+
sortConfig
68+
} = useSearchable<ActivityMessage>(
69+
activitiesData?.activities || [],
70+
['message', 'actor', 'resource', 'action'],
71+
{ key: 'timestamp', direction: 'desc' }
72+
);
73+
74+
const totalPages = Math.ceil((filteredAndSortedActivities?.length || 0) / ITEMS_PER_PAGE);
75+
76+
const paginatedActivities = useMemo(
77+
() =>
78+
filteredAndSortedActivities?.slice(
79+
(currentPage - 1) * ITEMS_PER_PAGE,
80+
currentPage * ITEMS_PER_PAGE
81+
) || [],
82+
[currentPage, filteredAndSortedActivities, ITEMS_PER_PAGE]
83+
);
84+
85+
const handlePageChange = (pageNumber: number) => {
86+
setCurrentPage(pageNumber);
87+
};
88+
89+
const handleResourceTypeChange = (value: string) => {
90+
setResourceType(value);
91+
setCurrentPage(1);
92+
};
93+
94+
useEffect(() => {
95+
setCurrentPage(1);
96+
}, [searchTerm, sortConfig]);
97+
98+
const onSortChange = (newSort: SortOption<ActivityMessage>) => {
99+
handleSortChange(newSort.value as keyof ActivityMessage);
100+
};
101+
102+
const sortOptions: SortOption<ActivityMessage>[] = React.useMemo(
103+
() => [
104+
{ label: 'Timestamp', value: 'timestamp', direction: 'desc' },
105+
{ label: 'Message', value: 'message', direction: 'asc' },
106+
{ label: 'Actor', value: 'actor', direction: 'asc' },
107+
{ label: 'Resource', value: 'resource', direction: 'asc' },
108+
{ label: 'Action', value: 'action', direction: 'asc' }
109+
],
110+
[]
111+
);
112+
113+
const activities = paginatedActivities;
114+
115+
return {
116+
resourceType,
117+
activities,
118+
sortOptions,
119+
onSortChange,
120+
handleResourceTypeChange,
121+
handlePageChange,
122+
handleSearchChange,
123+
isLoading,
124+
error,
125+
totalPages,
126+
sortConfig,
127+
searchTerm,
128+
currentPage,
129+
}
130+
}
131+
132+
export default useActivities

view/app/activities/page.tsx

Lines changed: 15 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
'use client';
22

3-
import React, { useState, useEffect, useMemo } from 'react';
3+
import React, { } from 'react';
44
import { useTranslation } from '@/hooks/use-translation';
5-
import { useSearchable } from '@/hooks/use-searchable';
6-
import { SortOption } from '@/components/ui/sort-selector';
75
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
8-
import { useGetActivitiesQuery } from '@/redux/services/audit';
96
import { formatDistanceToNow } from 'date-fns';
107
import { Loader2 } from 'lucide-react';
118
import { TypographySmall, TypographyMuted } from '@/components/ui/typography';
@@ -20,6 +17,7 @@ import {
2017
TableRow
2118
} from '@/components/ui/table';
2219
import { ActivityMessage } from '@/redux/types/audit';
20+
import useActivities, { ActivityListProps, getActionColor, resourceTypeOptions } from './hooks/use-activities';
2321

2422
export default function ActivitiesPage() {
2523
const { t } = useTranslation();
@@ -36,113 +34,27 @@ export default function ActivitiesPage() {
3634
);
3735
}
3836

39-
const getActionColor = (actionColor: string) => {
40-
switch (actionColor) {
41-
case 'green':
42-
return 'bg-green-500';
43-
case 'blue':
44-
return 'bg-blue-500';
45-
case 'red':
46-
return 'bg-red-500';
47-
default:
48-
return 'bg-gray-500';
49-
}
50-
};
51-
52-
const resourceTypeOptions: { value: string; label: string }[] = [
53-
{ value: 'all', label: 'All Resources' },
54-
{ value: 'user', label: 'Users' },
55-
{ value: 'organization', label: 'Organization' },
56-
{ value: 'application', label: 'Applications' },
57-
{ value: 'deployment', label: 'Deployments' },
58-
{ value: 'domain', label: 'Domains' },
59-
{ value: 'file_manager', label: 'Files' },
60-
{ value: 'container', label: 'Containers' },
61-
{ value: 'role', label: 'Roles' },
62-
{ value: 'permission', label: 'Permissions' },
63-
{ value: 'feature_flag', label: 'Feature Flags' },
64-
{ value: 'notification', label: 'Notifications' },
65-
{ value: 'integration', label: 'Integrations' },
66-
{ value: 'terminal', label: 'Terminal' },
67-
{ value: 'audit', label: 'Audit' },
68-
];
69-
70-
interface ActivityListProps {
71-
title?: string;
72-
description?: string;
73-
showFilters?: boolean;
74-
pageSize?: number;
75-
}
76-
7737
function ActivityList({
7838
title = 'Team Activities',
7939
showFilters = true,
8040
pageSize = 10
8141
}: ActivityListProps) {
82-
const { t } = useTranslation();
83-
const [resourceType, setResourceType] = useState('all');
84-
const [currentPage, setCurrentPage] = useState(1);
85-
const ITEMS_PER_PAGE = 20;
86-
87-
const { data: activitiesData, isLoading, error } = useGetActivitiesQuery({
88-
page: 1,
89-
pageSize: 100,
90-
search: '',
91-
resource_type: resourceType === 'all' ? '' : resourceType,
92-
});
93-
9442
const {
95-
filteredAndSortedData: filteredAndSortedActivities,
96-
searchTerm,
43+
resourceType,
44+
activities,
45+
sortOptions,
46+
onSortChange,
47+
handleResourceTypeChange,
48+
handlePageChange,
9749
handleSearchChange,
98-
handleSortChange,
99-
sortConfig
100-
} = useSearchable<ActivityMessage>(
101-
activitiesData?.activities || [],
102-
['message', 'actor', 'resource', 'action'],
103-
{ key: 'timestamp', direction: 'desc' }
104-
);
105-
106-
const totalPages = Math.ceil((filteredAndSortedActivities?.length || 0) / ITEMS_PER_PAGE);
107-
108-
const paginatedActivities = useMemo(
109-
() =>
110-
filteredAndSortedActivities?.slice(
111-
(currentPage - 1) * ITEMS_PER_PAGE,
112-
currentPage * ITEMS_PER_PAGE
113-
) || [],
114-
[currentPage, filteredAndSortedActivities, ITEMS_PER_PAGE]
115-
);
116-
117-
const handlePageChange = (pageNumber: number) => {
118-
setCurrentPage(pageNumber);
119-
};
120-
121-
const handleResourceTypeChange = (value: string) => {
122-
setResourceType(value);
123-
setCurrentPage(1);
124-
};
125-
126-
useEffect(() => {
127-
setCurrentPage(1);
128-
}, [searchTerm, sortConfig]);
129-
130-
const onSortChange = (newSort: SortOption<ActivityMessage>) => {
131-
handleSortChange(newSort.value as keyof ActivityMessage);
132-
};
133-
134-
const sortOptions: SortOption<ActivityMessage>[] = React.useMemo(
135-
() => [
136-
{ label: 'Timestamp', value: 'timestamp', direction: 'desc' },
137-
{ label: 'Message', value: 'message', direction: 'asc' },
138-
{ label: 'Actor', value: 'actor', direction: 'asc' },
139-
{ label: 'Resource', value: 'resource', direction: 'asc' },
140-
{ label: 'Action', value: 'action', direction: 'asc' }
141-
],
142-
[]
143-
);
50+
isLoading,
51+
error,
52+
totalPages,
53+
sortConfig,
54+
searchTerm,
55+
currentPage,
14456

145-
const activities = paginatedActivities;
57+
} = useActivities()
14658

14759
return (
14860
<div className="space-y-4">

view/app/auth/[[...path]]/page.tsx

Lines changed: 10 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,18 @@
11
'use client';
2-
3-
import { useEffect, useState } from 'react';
4-
import { useRouter } from 'next/navigation';
5-
import { useSessionContext } from 'supertokens-auth-react/recipe/session';
6-
import { signIn } from 'supertokens-auth-react/recipe/emailpassword';
72
import { ResetPasswordUsingToken } from 'supertokens-auth-react/recipe/emailpassword/prebuiltui';
83
import { LoginForm } from '@/components/features/login-form';
9-
import { useTranslation } from '@/hooks/use-translation';
10-
import { toast } from 'sonner';
11-
import { useAppDispatch } from '@/redux/hooks';
12-
import { initializeAuth } from '@/redux/features/users/authSlice';
4+
import useAuth from '../hooks/use-auth';
135

146
export default function Auth() {
15-
const router = useRouter();
16-
const session = useSessionContext();
17-
const { t } = useTranslation();
18-
const dispatch = useAppDispatch();
19-
const [loaded, setLoaded] = useState(false);
20-
const [email, setEmail] = useState('');
21-
const [password, setPassword] = useState('');
22-
const [isLoading, setIsLoading] = useState(false);
23-
24-
useEffect(() => {
25-
if (session.loading === false) {
26-
if (session.doesSessionExist) {
27-
router.push('/dashboard');
28-
} else {
29-
setLoaded(true);
30-
}
31-
}
32-
}, [session, router]);
33-
34-
const handleEmailChange = (event: React.ChangeEvent<HTMLInputElement>) => {
35-
setEmail(event.target.value);
36-
};
37-
38-
const handlePasswordChange = (event: React.ChangeEvent<HTMLInputElement>) => {
39-
setPassword(event.target.value);
40-
};
41-
42-
const handleLogin = async () => {
43-
if (!email || !password) {
44-
toast.error(t('auth.login.errors.requiredFields'));
45-
return;
46-
}
47-
48-
setIsLoading(true);
49-
try {
50-
const response = await signIn({
51-
formFields: [
52-
{ id: 'email', value: email },
53-
{ id: 'password', value: password }
54-
]
55-
});
56-
57-
if (response.status === 'FIELD_ERROR') {
58-
response.formFields.forEach(field => {
59-
toast.error(field.error);
60-
});
61-
} else if (response.status === 'WRONG_CREDENTIALS_ERROR') {
62-
toast.error(t('auth.login.errors.loginFailed'));
63-
} else {
64-
dispatch(initializeAuth() as any);
65-
router.push('/dashboard');
66-
}
67-
} catch (error) {
68-
toast.error(t('auth.login.errors.loginFailed'));
69-
} finally {
70-
setIsLoading(false);
71-
}
72-
};
7+
const {
8+
isLoading,
9+
handleEmailChange,
10+
handleLogin,
11+
email,
12+
password,
13+
handlePasswordChange,
14+
loaded,
15+
} = useAuth()
7316

7417
if (!loaded) {
7518
return (

0 commit comments

Comments
 (0)