Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added url search params #391

Merged
merged 5 commits into from
Mar 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 25 additions & 9 deletions app/components/form/WoW/WoWScanForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -378,12 +378,22 @@ const itemClasses: Array<{
}
]

export const ItemClassSelect = () => {
const [itemClass, setItemClass] = useState(-1)
const [itemSubClass, setItemSubClass] = useState(-1)
interface ItemClassSelectProps {
itemClass?: number
itemSubClass?: number
onChange?: (itemClassValue: number, itemSubClassValue: number) => void
}

export const ItemClassSelect: React.FC<ItemClassSelectProps> = ({
itemClass,
itemSubClass,
onChange
}) => {
const [selectedItemClass, setSelectedItemClass] = useState(itemClass)
const [selectedItemSubClass, setSelectedItemSubClass] = useState(itemSubClass)

const subClassItems = itemClasses.find(
(item) => item.value === itemClass
(item) => item.value === selectedItemClass
)?.subClasses

return (
Expand All @@ -395,10 +405,12 @@ export const ItemClassSelect = () => {
<Select
id={'itemClass'}
name={'itemClass'}
value={itemClass}
value={selectedItemClass}
onChange={(event) => {
setItemClass(parseInt(event.target.value))
setItemSubClass(-1)
const newValue = parseInt(event.target.value)
setSelectedItemClass(newValue)
setSelectedItemSubClass(-1)
onChange?.(newValue, -1)
}}>
<option value={-1}>All</option>
{itemClasses.map(({ name, value }) => (
Expand All @@ -414,8 +426,12 @@ export const ItemClassSelect = () => {
<Select
id={'itemSubClass'}
name={'itemSubClass'}
value={itemSubClass}
onChange={(event) => setItemSubClass(parseInt(event.target.value))}>
value={selectedItemSubClass}
onChange={(event) => {
const newValue = parseInt(event.target.value)
setSelectedItemSubClass(newValue)
onChange?.(selectedItemClass ?? -1, newValue)
}}>
<option value={-1}>All</option>
{subClassItems &&
subClassItems.map(({ name, value }) => (
Expand Down
103 changes: 92 additions & 11 deletions app/routes/wow.best-deals.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import type { ActionFunction } from '@remix-run/cloudflare'
import type { ActionFunction, LoaderFunction } from '@remix-run/cloudflare'
import { json } from '@remix-run/cloudflare'
import { useEffect } from 'react'
import { useEffect, useState } from 'react'
import { PageWrapper } from '~/components/Common'
import SmallFormContainer from '~/components/form/SmallFormContainer'
import type { DealItem, WoWDealResponse } from '~/requests/WoW/BestDeals'
import WoWBestDeals from '~/requests/WoW/BestDeals'
import { getUserSessionData } from '~/sessions'
import z from 'zod'
import { useActionData, useNavigation } from '@remix-run/react'
import { useActionData, useLoaderData, useNavigation } from '@remix-run/react'
import { InputWithLabel } from '~/components/form/InputWithLabel'
import Select from '~/components/form/select'
import NoResults from '~/components/Common/NoResults'
Expand All @@ -19,11 +19,28 @@ import {
parseStringToNumber,
parseZodErrorsToDisplayString
} from '~/utils/zodHelpers'
import {
getActionUrl,
handleSearchParamChange
} from '~/utils/urlSeachParamsHelpers'

const PAGE_URL = '/wow/best-deals'

const defaultFormValues = {
type: 'df',
discount: '90',
minPrice: '2000',
salesPerDay: '1.1',
itemClass: '-1',
itemSubClass: '-1'
}

const inputMap: Record<string, string> = {
discount: 'Discount Percentage',
minPrice: 'Minimum TSM Average Price',
salesPerDay: 'Sales Per Day'
salesPerDay: 'Sales Per Day',
itemClass: 'Item Class',
itemSubClass: 'Item Subclass'
}

const validateInput = z.object({
Expand All @@ -38,6 +55,26 @@ const validateInput = z.object({
itemSubClass: parseStringToNumber
})

export const loader: LoaderFunction = async ({ request }) => {
const params = new URL(request.url).searchParams

const values = {
type: params.get('type') || defaultFormValues.type,
discount: params.get('discount') || defaultFormValues.discount,
minPrice: params.get('minPrice') || defaultFormValues.minPrice,
salesPerDay: params.get('salesPerDay') || defaultFormValues.salesPerDay,
itemClass: params.get('itemClass') || defaultFormValues.itemClass,
itemSubClass: params.get('itemSubClass') || defaultFormValues.itemSubClass
}
const validParams = validateInput.safeParse(values)
if (!validParams.success) {
return json({
exception: parseZodErrorsToDisplayString(validParams.error, inputMap)
})
}
return json(validParams.data)
}

export const action: ActionFunction = async ({ request }) => {
const session = await getUserSessionData(request)

Expand Down Expand Up @@ -74,15 +111,33 @@ type ActionResponseType =
| (WoWDealResponse & { sortby: string })

const BestDeals = () => {
const loaderData = useLoaderData<typeof defaultFormValues>()
const result = useActionData<ActionResponseType>()
const transistion = useNavigation()

const isSubmitting = transistion.state === 'submitting'

const [searchParams, setSearchParams] = useState<typeof defaultFormValues>({
...loaderData,
itemClass: loaderData.itemClass, // Ensure itemClass is 'all' if not provided
itemSubClass: loaderData.itemSubClass // Ensure itemSubClass is 'all' if not provided
})
const error = result && 'exception' in result ? result.exception : undefined

useEffect(() => {
const updatedSearchParams = new URLSearchParams()
Object.entries(searchParams).forEach(([key, value]) => {
updatedSearchParams.set(key, value)
})
window.history.pushState(
{},
'',
`${PAGE_URL}?${updatedSearchParams.toString()}`
)
}, [searchParams])

if (result && !Object.keys(result).length) {
return <NoResults href="/wow/best-deals" />
return <NoResults href={PAGE_URL} />
}

if (result && 'data' in result && !error) {
Expand All @@ -97,48 +152,74 @@ const BestDeals = () => {
}
}

const handleFormChange = (
name: keyof typeof defaultFormValues,
value: string
) => {
setSearchParams((prevParams) => ({ ...prevParams, [name]: value }))
handleSearchParamChange(name, value)
}

return (
<PageWrapper>
<SmallFormContainer
title="Best Deals"
description="Find the best deals on your server and region wide with our Best Deals search!"
onClick={handleSubmit}
error={error}
loading={isSubmitting}>
loading={isSubmitting}
action={getActionUrl(PAGE_URL, searchParams)}>
<div className="pt-3 flex flex-col">
<Select
title="Item Type"
name="type"
defaultValue={'df'}
defaultValue={searchParams.type}
options={[
{ label: 'Dragonflight Only', value: 'df' },
{ label: 'Pets Only', value: 'pets' },
{ label: 'Legacy Only', value: 'legacy' },
{ label: 'All', value: 'all' }
]}
onChange={(e) => handleFormChange('type', e.target.value)}
/>
<ItemClassSelect
itemClass={+searchParams.itemClass}
itemSubClass={+searchParams.itemSubClass}
onChange={(itemClassValue, itemSubClassValue) => {
handleFormChange('itemClass', String(itemClassValue))
handleFormChange('itemSubClass', String(itemSubClassValue))
}}
/>
<ItemClassSelect />
<InputWithLabel
labelTitle="Discount Percentage"
name="discount"
type="number"
defaultValue={90}
defaultValue={searchParams.discount}
min={0}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
handleFormChange('discount', e.currentTarget.value)
}
/>
<InputWithLabel
labelTitle="Minimum TSM Average Price"
name="minPrice"
type="number"
defaultValue={2000}
defaultValue={searchParams.minPrice}
min={0}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
handleFormChange('minPrice', e.currentTarget.value)
}
/>
<InputWithLabel
labelTitle="Sales Per Day"
name="salesPerDay"
type="number"
defaultValue={1.1}
defaultValue={searchParams.salesPerDay}
step={0.1}
min={0}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
handleFormChange('salesPerDay', e.currentTarget.value)
}
/>
</div>
</SmallFormContainer>
Expand Down
Loading