diff --git a/app/(stats)/compositions/page.tsx b/app/(stats)/compositions/[[...query]]/page.tsx similarity index 83% rename from app/(stats)/compositions/page.tsx rename to app/(stats)/compositions/[[...query]]/page.tsx index 10eeee4e..e309718d 100644 --- a/app/(stats)/compositions/page.tsx +++ b/app/(stats)/compositions/[[...query]]/page.tsx @@ -4,6 +4,7 @@ import { pointsUpdateDate } from '@/lib/config'; import { getFactionCount, getSquads } from '@/lib/db/squads'; import { getTournamentsCount } from '@/lib/db/tournaments'; import { formatDate, fromDate, toDate, today } from '@/lib/utils/date.utils'; +import { pq } from '@/lib/utils/url.utils'; import { Caption, Card, Inline, Message, Title } from '@/ui'; import { Calendar, Rocket, Trophy } from '@/ui/icons'; @@ -13,11 +14,12 @@ import { CompositionFilterProvider, CompositionTable, } from '@/ui/stats/composition-stats'; -import { Filter } from '@/ui/stats/filter'; +import { StatsFilter } from '@/ui/stats/stats-filter'; import { createMetadata } from '@/lib/metadata'; import { StatsHint } from '@/ui/stats/stats-hint'; import { setup } from '@/lib/stats'; import { CompositionData, composition } from '@/lib/stats/module'; +import { QueryFilter } from '@/ui/filter/query-filter'; // Config // --------------- @@ -81,20 +83,18 @@ const getStats = async ( // Props // --------------- -interface AnalyzePageProps { - searchParams: { - from: string; - to: string; - 'small-samples': 'show' | 'hide'; +interface PageProps { + params: { + query?: string[]; }; } // Page // --------------- -const AnalyzeCompositionPage = async ({ searchParams }: AnalyzePageProps) => { - const params = schema.safeParse(searchParams); +const CompositionsPage = async ({ params }: PageProps) => { + const query = schema.safeParse(pq(params.query?.[0])); - if (!params.success) { + if (!query.success) { return (
@@ -106,13 +106,12 @@ const AnalyzeCompositionPage = async ({ searchParams }: AnalyzePageProps) => { } const from = - params.data && params.data.from - ? fromDate(params.data.from) + query.data && query.data.from + ? fromDate(query.data.from) : fromDate(pointsUpdateDate); - const to = - params.data && params.data.to ? fromDate(params.data.to) : undefined; + const to = query.data && query.data.to ? fromDate(query.data.to) : undefined; - const { stats, meta } = await getStats(from, to, params.data.smallSamples); + const { stats, meta } = await getStats(from, to, query.data.smallSamples); return ( <> @@ -133,13 +132,14 @@ const AnalyzeCompositionPage = async ({ searchParams }: AnalyzePageProps) => {
+ - - +
@@ -160,4 +160,4 @@ const AnalyzeCompositionPage = async ({ searchParams }: AnalyzePageProps) => { ); }; -export default AnalyzeCompositionPage; +export default CompositionsPage; diff --git a/app/analyze/composition/page.tsx b/app/analyze/composition/page.tsx index 9fe4141f..b26ffa8d 100644 --- a/app/analyze/composition/page.tsx +++ b/app/analyze/composition/page.tsx @@ -14,7 +14,7 @@ import { CompositionFilterProvider, CompositionTable, } from '@/ui/stats/composition-stats'; -import { Filter } from '@/ui/stats/filter'; +import { StatsFilter } from '@/ui/stats/stats-filter'; import { createMetadata } from '@/lib/metadata'; import { StatsHint } from '@/ui/stats/stats-hint'; import { setup } from '@/lib/stats'; @@ -133,12 +133,12 @@ const AnalyzeCompositionPage = async ({ searchParams }: AnalyzePageProps) => {
- - +
diff --git a/app/analyze/page.tsx b/app/analyze/page.tsx index 0944c645..7ddf8d54 100644 --- a/app/analyze/page.tsx +++ b/app/analyze/page.tsx @@ -33,7 +33,7 @@ import { CompositionStats } from '@/ui/stats/composition-stats'; import { FactionDistribution } from '@/ui/stats/faction-distribution'; import { FactionPerformance } from '@/ui/stats/faction-performance'; import { FactionVictories } from '@/ui/stats/faction-victories'; -import { Filter } from '@/ui/stats/filter'; +import { StatsFilter } from '@/ui/stats/stats-filter'; import { PilotCostDistribution } from '@/ui/stats/pilot-cost-distribution'; import { PilotSkillDistribution } from '@/ui/stats/pilot-skill-distribution'; import { PilotStats } from '@/ui/stats/pilot-stats'; @@ -171,7 +171,7 @@ const AnalyzePage = async ({ searchParams }: AnalyzePageProps) => {
- diff --git a/lib/stats/details/composition.ts b/lib/stats/details/composition.ts index fd9ec21a..9288e6d6 100644 --- a/lib/stats/details/composition.ts +++ b/lib/stats/details/composition.ts @@ -7,13 +7,7 @@ import type { XWSUpgrades, } from '@/lib/types'; import { fromDate, toMonth } from '@/lib/utils/date.utils'; -import { - average, - deviation, - percentile, - round, - winrate, -} from '@/lib/utils/math.utils'; +import { average, deviation, round, winrate } from '@/lib/utils/math.utils'; // Types // --------------- diff --git a/lib/utils/index.ts b/lib/utils/index.ts index 97b5cf2c..32ba6306 100644 --- a/lib/utils/index.ts +++ b/lib/utils/index.ts @@ -4,3 +4,4 @@ export * from './fetch.utils'; export * from './math.utils'; export * from './react.utils'; export * from './string.utils'; +export * from './url.utils'; diff --git a/lib/utils/url.utils.ts b/lib/utils/url.utils.ts new file mode 100644 index 00000000..bb9e7e93 --- /dev/null +++ b/lib/utils/url.utils.ts @@ -0,0 +1,9 @@ +import qs from 'query-string'; + +/** + * Used to parse a dynamic route segment and use it as query params. + * + * This is done because using `searchParams` (the real query params) will cause + * pages to switch to dynamic mode. + */ +export const pq = (query: string = '') => qs.parse(decodeURIComponent(query)); diff --git a/package.json b/package.json index 1d55dda2..1938bb61 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "mysql2": "3.6.2", "next": "13.5.4", "p-limit": "4.0.0", + "query-string": "^8.1.0", "react": "18.2.0", "react-dom": "18.2.0", "react-is": "18.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 94af6dd9..edde9c4e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -59,6 +59,9 @@ dependencies: p-limit: specifier: 4.0.0 version: 4.0.0 + query-string: + specifier: ^8.1.0 + version: 8.1.0 react: specifier: 18.2.0 version: 18.2.0 @@ -3019,6 +3022,11 @@ packages: resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} dev: true + /decode-uri-component@0.4.1: + resolution: {integrity: sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==} + engines: {node: '>=14.16'} + dev: false + /dedent@1.5.1: resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==} peerDependencies: @@ -3786,6 +3794,11 @@ packages: dependencies: to-regex-range: 5.0.1 + /filter-obj@5.1.0: + resolution: {integrity: sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==} + engines: {node: '>=14.16'} + dev: false + /find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -5787,6 +5800,15 @@ packages: resolution: {integrity: sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg==} dev: true + /query-string@8.1.0: + resolution: {integrity: sha512-BFQeWxJOZxZGix7y+SByG3F36dA0AbTy9o6pSmKFcFz7DAj0re9Frkty3saBn3nHo3D0oZJ/+rx3r8H8r8Jbpw==} + engines: {node: '>=14.16'} + dependencies: + decode-uri-component: 0.4.1 + filter-obj: 5.1.0 + split-on-first: 3.0.0 + dev: false + /querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} dev: true @@ -6194,6 +6216,11 @@ packages: deprecated: Please use @jridgewell/sourcemap-codec instead dev: false + /split-on-first@3.0.0: + resolution: {integrity: sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==} + engines: {node: '>=12'} + dev: false + /split@0.3.3: resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} dependencies: diff --git a/ui/filter/query-filter.tsx b/ui/filter/query-filter.tsx new file mode 100644 index 00000000..6c6dda2a --- /dev/null +++ b/ui/filter/query-filter.tsx @@ -0,0 +1,19 @@ +'use client'; + +import { useTransition } from 'react'; +import type { ReactNode } from 'react'; +import { usePathname, useRouter } from 'next/navigation'; + +export interface QueryFilterProps { + children?: ReactNode; +} + +export const QueryFilter = ({ children }: QueryFilterProps) => { + const { replace } = useRouter(); + const pathname = usePathname(); + const [pending, startTransition] = useTransition(); + + console.log(pathname); + + return <>{children}; +}; diff --git a/ui/stats/filter.tsx b/ui/stats/stats-filter.tsx similarity index 94% rename from ui/stats/filter.tsx rename to ui/stats/stats-filter.tsx index 1bc1773d..619e4d8b 100644 --- a/ui/stats/filter.tsx +++ b/ui/stats/stats-filter.tsx @@ -15,7 +15,11 @@ export interface FilterProps { // Component // --------------- -export const Filter = ({ children, dateRange, smallSamples }: FilterProps) => { +export const StatsFilter = ({ + children, + dateRange, + smallSamples, +}: FilterProps) => { const { replace } = useRouter(); const pathname = usePathname(); const [pending, startTransition] = useTransition();