Skip to content

Commit

Permalink
improvements and fixes: enable JIT mode in Tailwind, refactor authent…
Browse files Browse the repository at this point in the history
…ication components, and enhance reports layout
  • Loading branch information
LeonardoMeireles55 committed Feb 6, 2025
1 parent 8ad0646 commit dd307c6
Show file tree
Hide file tree
Showing 25 changed files with 505 additions and 404 deletions.
32 changes: 32 additions & 0 deletions src/components/analytics-table/hooks/useAnalyticsOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useMemo } from 'react';

export const useAnalyticsOptions = (analyticsType: string) => {
const analyticsOptions = [
{ value: 'biochemistry-analytics', label: 'BIOCHEMISTRY' },
{ value: 'hematology-analytics', label: 'HEMATOLOGY' },
{ value: 'coagulation-analytics', label: 'COAGULATION' },
];

const levelOptions = useMemo(() => {
switch (analyticsType) {
case 'hematology-analytics':
return [
{ value: '0', label: '-' },
{ value: '1', label: '1' },
{ value: '2', label: '2' },
{ value: '3', label: '3' },
];
case 'biochemistry-analytics':
case 'coagulation-analytics':
return [
{ value: '0', label: '-' },
{ value: '1', label: '1' },
{ value: '2', label: '2' },
];
default:
return [{ value: '0', label: '-' }];
}
}, [analyticsType]);

return { analyticsOptions, levelOptions };
};
95 changes: 32 additions & 63 deletions src/components/analytics-table/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useAnalyticsData } from '@/components/analytics-table/hooks/useAnalyticsData';
import ListingTable from '@/components/analytics-table/listing-table';
import useDateSelector from '@/components/shared/date-selector/hooks/useDateSelector';
import { useEffect, useMemo, useState } from 'react';
import NavBar from '../shared/navigation-bar';
import Footer from '../shared/ui/footer';
import { useEffect, useState } from 'react';
import useWindowDimensions from '../shared/ui/hooks/useWindowDimensions';
import { useAnalyticsOptions } from './hooks/useAnalyticsOptions';
import MainLayout from './layouts/MainLayout';
import ListingTable from './listing-table';
import AnalyticsFilters from './util/analytics-filters';
import AnalyticsPagination from './util/analytics-pagination';

Expand All @@ -17,6 +17,7 @@ const AnalyticsTableIndex = () => {
const [isFiltered, setFiltered] = useState(false);

const { width } = useWindowDimensions();
const { analyticsOptions, levelOptions } = useAnalyticsOptions(analyticsType);

const {
data: dataFetched,
Expand Down Expand Up @@ -58,70 +59,38 @@ const AnalyticsTableIndex = () => {
dateSelector.endYear,
]);

useEffect(() => {
setItemsPerPage(width >= 1800 ? 14 : 8);
}, [width]);

const handlePageChange = async (url: string): Promise<void> => {
await fetchData(url);
};

useEffect(() => {
setItemsPerPage(width >= 1800 ? 12 : 8);
}, [fetchData, width]);

const analyticsOptions = [
{ value: 'biochemistry-analytics', label: 'BIOCHEMISTRY' },
{ value: 'hematology-analytics', label: 'HEMATOLOGY' },
{ value: 'coagulation-analytics', label: 'COAGULATION' },
];

const levelOptions = useMemo(() => {
switch (analyticsType) {
case 'hematology-analytics':
return [
{ value: '0', label: '-' },
{ value: '1', label: '1' },
{ value: '2', label: '2' },
{ value: '3', label: '3' },
];
case 'biochemistry-analytics':
case 'coagulation-analytics':
return [
{ value: '0', label: '-' },
{ value: '1', label: '1' },
{ value: '2', label: '2' },
];
default:
return [{ value: '0', label: '-' }];
}
}, [analyticsType]);

return (
<div className='min-h bg-background'>
<div className='min-h flex flex-col content-center items-center justify-center'>
<title>{`LabGraph - ${analyticsType || 'Quality-Lab-Pro'}`}</title>
<NavBar jsonData={dataFetched} fileName={analyticsType} />
<div className='w-full max-w-7xl'>
<AnalyticsFilters
dateSelector={dateSelector}
analyticsOptions={analyticsOptions}
analyticsType={analyticsType}
setAnalyticsType={setAnalyticsType}
levelOptions={levelOptions}
level={level}
setLevel={setLevel}
setFiltered={setFiltered}
/>
<ListingTable items={dataFetched} isLoading={isLoading} onPageChange={handlePageChange} />
</div>
<AnalyticsPagination
currentPage={currentPage}
totalPages={totalPages}
dataFetched={dataFetched}
setCurrentPage={setCurrentPage}
/>
<div className='flex flex-col items-center justify-end'>
<Footer />
</div>
</div>
</div>
<MainLayout
title={`LabGraph - ${analyticsType || 'Quality-Lab-Pro'}`}
jsonData={dataFetched}
fileName={analyticsType}
>
<AnalyticsFilters
dateSelector={dateSelector}
analyticsOptions={analyticsOptions}
analyticsType={analyticsType}
setAnalyticsType={setAnalyticsType}
levelOptions={levelOptions}
level={level}
setLevel={setLevel}
setFiltered={setFiltered}
/>
<ListingTable items={dataFetched} isLoading={isLoading} onPageChange={handlePageChange} />
<AnalyticsPagination
currentPage={currentPage}
totalPages={totalPages}
dataFetched={dataFetched}
setCurrentPage={setCurrentPage}
/>
</MainLayout>
);
};

Expand Down
27 changes: 27 additions & 0 deletions src/components/analytics-table/layouts/MainLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import NavBar from '@/components/shared/navigation-bar';
import Footer from '@/components/shared/ui/footer';
import { ReactNode } from 'react';

interface MainLayoutProps {
children: ReactNode;
title: string;
jsonData: any;
fileName: string;
}

const MainLayout = ({ children, title, jsonData, fileName }: MainLayoutProps) => {
return (
<div className='min-h bg-background'>
<div className='min-h flex flex-col content-center items-center justify-center'>
<title>{title}</title>
<NavBar jsonData={jsonData} fileName={fileName} />
<div className='w-full max-w-7xl'>{children}</div>
<div className='flex flex-col items-center justify-end'>
<Footer />
</div>
</div>
</div>
);
};

export default MainLayout;
6 changes: 3 additions & 3 deletions src/components/analytics-table/listing-table/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ const tableHeaders = [

const ListingTable: React.FC<ListingTableProps> = ({ items }) => {
return (
<div className='flex flex-col justify-between w-full h-min rounded-lg shadow-xl'>
<table className='hidden bg-surface md:table'>
<div className='flex flex-col justify-between w-full h-min'>
<table className='hidden bg-surface md:table shadow-md shadow-shadow'>
<thead className='bg-muted rounded-lg'>
<tr>
{tableHeaders.map((header, index) => (
Expand All @@ -37,7 +37,7 @@ const ListingTable: React.FC<ListingTableProps> = ({ items }) => {
))}
</tbody>
</table>
<div className='grid content-center justify-center grid-cols-4 px-2 text-center md:hidden'>
<div className='grid content-center justify-center gap-4 grid-cols-4 px-2 text-center md:hidden'>
{items.map((item, index) => (
<MobileItemCard key={index} item={item} />
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const AnalyticsPagination: React.FC<AnalyticsPaginationProps> = ({
setCurrentPage,
}) => {
return (
<div className='flex items-center justify-center py-4 space-x-2 fixed bottom-12 w-full bg-background'>
<div className='flex items-center justify-center py-4 space-x-2 w-full bg-background'>
<button
onClick={() => setCurrentPage((prev) => prev - 1)}
disabled={currentPage === 0}
Expand Down
18 changes: 18 additions & 0 deletions src/components/authentication/common/AuthLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Link from 'next/link';

interface AuthLinkProps {
text: string;
linkText: string;
href: string;
}

const AuthLink = ({ text, linkText, href }: AuthLinkProps) => (
<p className='text-center text-xs sm:text-sm text-textSecondary'>
{text}{' '}
<Link href={href} className='font-medium text-textPrimary transition-colors duration-200'>
{linkText}
</Link>
</p>
);

export default AuthLink;
19 changes: 19 additions & 0 deletions src/components/authentication/common/Form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Logo from '@/components/shared/ui/logo';
import ThemeToggle from '@/components/shared/ui/theme';

export default function Form() {
return (
<div className='px-2 h-screen from-primary/20 bg-gradient-to-br to-background flex items-center justify-center'>
<div className='w-full max-w-md transform rounded-xl border border-borderColor px-8 py-6 shadow-xl backdrop-blur-sm transition-all duration-300 ease-in-out hover:shadow-2xl sm:px-12 sm:py-8 mx-auto'>
<div className='absolute right-4 top-4 z-50'>
<ThemeToggle />
</div>
<div className='mb-6 text-center'>
<div className='flex justify-center text-secondary opacity-95 transition-transform duration-300 ease-in-out'>
<Logo className='w-32 sm:w-40 md:w-48 lg:w-56 opacity-90' />
</div>
</div>
</div>
</div>
);
}
6 changes: 1 addition & 5 deletions src/components/authentication/common/InputField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ const InputField: React.FC<InputFieldProps> = ({
label,
value,
onChange,
autoCompleteProps,
placeholder,
icon,
}) => {
Expand All @@ -28,12 +27,9 @@ const InputField: React.FC<InputFieldProps> = ({
<input
type={type === 'password' ? (showPassword ? 'text' : 'password') : type}
id={id}
autoComplete={autoCompleteProps ? 'off' : 'on'}
value={value}
onChange={onChange}
className={`input-modern placeholder:text-textSecondary placeholder:opacity-25 hover:border-textPrimary focus:ring-textSecondary w-full rounded-lg border border-borderColor bg-surface p-1.5 text-bgText transition-all duration-200 ease-in-out focus:border-textPrimary focus:outline-none focus:ring-1 ${
icon ? 'pl-10' : 'pl-3'
}`}
className={`input-modern ${icon ? 'pl-10' : 'pl-3'}`}
required
placeholder={placeholder}
/>
Expand Down
5 changes: 1 addition & 4 deletions src/components/authentication/common/SubmitButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ interface SubmitButtonProps {

const SubmitButton = ({ text, icon = true }: SubmitButtonProps) => {
return (
<button
type='submit'
className='w-full transform rounded-lg bg-buttonMuted px-3 py-1.5 text-base font-semibold text-white transition-all duration-200 ease-in-out hover:shadow-lg focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 active:scale-95 disabled:opacity-70 flex items-center justify-center gap-1'
>
<button type='submit' className='button-modern'>
{icon ? <LogIn className='w-5 h-5' /> : text}
</button>
);
Expand Down
20 changes: 11 additions & 9 deletions src/components/authentication/hooks/useAuthentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,21 @@ export const useAuth = (isLogin: boolean) => {
if (!formData.identifier) {
errors.push({ field: 'identifier', message: 'Identifier is required.' });
}
if (!formData.password) {
errors.push({ field: 'password', message: 'Password is required.' });
}
} else {
if (!formData.email || !emailRegex.test(formData.email)) {
errors.push({ field: 'email', message: 'A valid email address is required.' });
}
if (!formData.password) {
errors.push({ field: 'password', message: 'Password is required.' });
} else if (formData.password.length < 4 || !/[!@#$%^&*]/.test(formData.password)) {
errors.push({
field: 'password',
message: 'Password must be at least 4 characters long and include one special character.',
});
}
if (!formData.confirmPassword) {
errors.push({ field: 'confirmPassword', message: 'Confirm Password is required.' });
} else if (formData.password !== formData.confirmPassword) {
Expand All @@ -42,15 +53,6 @@ export const useAuth = (isLogin: boolean) => {
}
}

if (!formData.password) {
errors.push({ field: 'password', message: 'Password is required.' });
} else if (formData.password.length < 4 || !/[!@#$%^&*]/.test(formData.password)) {
errors.push({
field: 'password',
message: 'Password must be at least 4 characters long and include one special character.',
});
}

return errors;
};

Expand Down
26 changes: 26 additions & 0 deletions src/components/authentication/layout/AuthFormContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import Logo from '../../shared/ui/logo';
import ThemeToggle from '../../shared/ui/theme';

interface AuthFormContainerProps {
children: React.ReactNode;
}

const AuthFormContainer = ({ children }: AuthFormContainerProps) => {
return (
<div className='px-2 h-screen from-primary/20 bg-gradient-to-br to-background flex items-center justify-center'>
<div className='w-full max-w-md transform rounded-xl border border-borderColor px-8 py-6 shadow-xl backdrop-blur-sm transition-all duration-300 ease-in-out hover:shadow-2xl sm:px-12 sm:py-8 mx-auto'>
<div className='absolute right-4 top-4 z-50'>
<ThemeToggle />
</div>
<div className='mb-6 text-center'>
<div className='flex justify-center text-secondary opacity-95 transition-transform duration-300 ease-in-out'>
<Logo className='w-32 sm:w-40 md:w-48 lg:w-56 opacity-90' />
</div>
</div>
{children}
</div>
</div>
);
};

export default AuthFormContainer;
Loading

0 comments on commit dd307c6

Please sign in to comment.