Skip to content

Commit b432e28

Browse files
hh
1 parent c19ce61 commit b432e28

11 files changed

Lines changed: 620 additions & 124 deletions

File tree

hooks/useSorting.ts

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,33 @@ export const useSorting = <T extends Record<string, any>>( data: T[] ) => {
1212
} );
1313

1414
const handleSort = ( key: string ) => {
15-
let direction = 'ascending';
16-
if ( sortConfig.key === key && sortConfig.direction === 'ascending' ) {
17-
direction = 'descending';
15+
const isSameKey = sortConfig.key === key;
16+
const nextDirection =
17+
isSameKey && sortConfig.direction === 'ascending' ? 'descending' : 'ascending';
18+
setSortConfig( { key, direction: nextDirection } );
19+
};
20+
21+
const parseNumericValue = ( value: unknown ) => {
22+
if ( typeof value === 'number' && Number.isFinite( value ) ) {
23+
return value;
24+
}
25+
26+
if ( value === null || value === undefined ) {
27+
return null;
28+
}
29+
30+
const text = String( value ).trim();
31+
if ( !text ) {
32+
return null;
1833
}
19-
setSortConfig( { key, direction: "descending" } );
34+
35+
const normalized = text.replace( /[^0-9.-]+/g, '' );
36+
if ( !normalized || normalized === '-' || normalized === '.' ) {
37+
return null;
38+
}
39+
40+
const parsed = Number( normalized );
41+
return Number.isFinite( parsed ) ? parsed : null;
2042
};
2143

2244
const sortedData = useMemo( () => {
@@ -25,34 +47,33 @@ export const useSorting = <T extends Record<string, any>>( data: T[] ) => {
2547
return [ ...data ].sort( ( a, b ) => {
2648
const valueA = a[ sortConfig.key! ];
2749
const valueB = b[ sortConfig.key! ];
28-
29-
const isNumericA = !isNaN( parseFloat( valueA ) );
30-
const isNumericB = !isNaN( parseFloat( valueB ) );
50+
const numericA = parseNumericValue( valueA );
51+
const numericB = parseNumericValue( valueB );
52+
const isNumericA = numericA !== null;
53+
const isNumericB = numericB !== null;
3154

3255
if ( isNumericA && isNumericB ) {
33-
const numericA = parseFloat( valueA );
34-
const numericB = parseFloat( valueB );
3556
return sortConfig.direction === 'ascending'
3657
? numericA - numericB
3758
: numericB - numericA;
3859
}
3960

40-
if ( isNumericA ) return sortConfig.direction === 'ascending' ? -1 : 1;
41-
if ( isNumericB ) return sortConfig.direction === 'descending' ? 1 : -1;
61+
if ( isNumericA && !isNumericB ) return -1;
62+
if ( !isNumericA && isNumericB ) return 1;
4263

43-
const result = String( valueA || '' ).localeCompare( String( valueB || '' ) );
64+
const result = String( valueA ?? '' ).localeCompare( String( valueB ?? '' ) );
4465
return sortConfig.direction === 'ascending' ? result : -result;
4566
} );
4667
}, [ data, sortConfig ] );
4768

4869
const getSortIcon = ( key: string ) => {
4970
if ( sortConfig.key !== key ) return null;
50-
return sortConfig.direction === 'ascending' ? '' : '';
71+
return sortConfig.direction === 'ascending' ? '^' : 'v';
5172
};
5273

5374
return {
5475
sortedData,
5576
handleSort,
5677
getSortIcon
5778
};
58-
};
79+
};

next.config.js

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,20 @@
11
/** @type {import('next').NextConfig} */
2+
const isDev = process.env.NODE_ENV === "development";
3+
4+
const cspHeader = `
5+
default-src 'self';
6+
script-src 'self' 'unsafe-inline'${isDev ? " 'unsafe-eval'" : ""} https://va.vercel-scripts.com;
7+
style-src 'self' 'unsafe-inline';
8+
img-src 'self' blob: data: https://images.ygoprodeck.com https://images.unsplash.com https://tailwindcss.com;
9+
font-src 'self' data:;
10+
connect-src 'self' https://mpapi.tcgplayer.com https://infinite-api.tcgplayer.com https://db.ygoprodeck.com https://www.sportscardspro.com;
11+
object-src 'none';
12+
base-uri 'self';
13+
form-action 'self';
14+
frame-ancestors 'none';
15+
upgrade-insecure-requests;
16+
`;
17+
218
const nextConfig = {
319
reactStrictMode: true,
420
productionBrowserSourceMaps: false,
@@ -10,36 +26,36 @@ const nextConfig = {
1026
headers: [
1127
{
1228
key: "Content-Security-Policy",
13-
value: "script-src 'self' 'unsafe-inline' 'unsafe-eval' mpapi.tcgplayer.com infinite-api.tcgplayer.com card-test-ashy.vercel.app va.vercel-scripts.com db.ygoprodeck.com sportscardspro.com; style-src 'self' 'unsafe-inline' card-test-ashy.vercel.app; img-src 'self' images.ygoprodeck.com data:; object-src 'none'; default-src 'self' mpapi.tcgplayer.com db.ygoprodeck.com sportscardspro.com card-test-ashy.vercel.app infinite-api.tcgplayer.com va.vercel-scripts.com; font-src 'self'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; upgrade-insecure-requests;"
29+
value: cspHeader.replace(/\s{2,}/g, " ").trim(),
1430
},
1531
{
1632
key: "X-Content-Type-Options",
17-
value: "nosniff"
33+
value: "nosniff",
1834
},
1935
{
2036
key: "X-Frame-Options",
21-
value: "DENY"
37+
value: "DENY",
2238
},
2339
{
2440
key: "Referrer-Policy",
25-
value: "strict-origin-when-cross-origin"
41+
value: "strict-origin-when-cross-origin",
2642
},
2743
{
2844
key: "Permissions-Policy",
29-
value: "geolocation=(), camera=(), microphone=(), fullscreen=*, payment=()"
45+
value: "geolocation=(), camera=(), microphone=(), fullscreen=*, payment=()",
3046
},
3147
{
3248
key: "X-Permitted-Cross-Domain-Policies",
33-
value: "none"
49+
value: "none",
3450
},
3551
{
3652
key: "Expect-CT",
37-
value: "max-age=86400, enforce"
38-
}
39-
]
40-
}
53+
value: "max-age=86400, enforce",
54+
},
55+
],
56+
},
4157
];
42-
}
58+
},
4359
};
4460

4561
module.exports = nextConfig;

0 commit comments

Comments
 (0)