From 8333525c1f365ca445f69e50814cd336670a3dd7 Mon Sep 17 00:00:00 2001 From: Ben Webb Date: Thu, 6 Jul 2023 19:25:45 +0100 Subject: [PATCH 1/2] Update form methods to be uppercase --- app/components/form/SmallFormContainer/index.tsx | 2 +- app/components/form/ffxiv/FullScanForm.tsx | 2 +- app/components/navigation/sidebar/Sidebar.tsx | 2 +- app/routes/options/index.tsx | 2 +- remix.config.js | 12 ++++++++---- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/app/components/form/SmallFormContainer/index.tsx b/app/components/form/SmallFormContainer/index.tsx index f176e83b..31fd159c 100644 --- a/app/components/form/SmallFormContainer/index.tsx +++ b/app/components/form/SmallFormContainer/index.tsx @@ -31,7 +31,7 @@ const SmallFormContainer = ({ ) ) : undefined return ( -
+

{title} diff --git a/app/components/form/ffxiv/FullScanForm.tsx b/app/components/form/ffxiv/FullScanForm.tsx index 3273f32b..e7e0bba7 100644 --- a/app/components/form/ffxiv/FullScanForm.tsx +++ b/app/components/form/ffxiv/FullScanForm.tsx @@ -71,7 +71,7 @@ const FullScanForm = ({ return ( <> - +
diff --git a/app/components/navigation/sidebar/Sidebar.tsx b/app/components/navigation/sidebar/Sidebar.tsx index 64ea451d..3672037a 100644 --- a/app/components/navigation/sidebar/Sidebar.tsx +++ b/app/components/navigation/sidebar/Sidebar.tsx @@ -749,7 +749,7 @@ const ItemSearch = () => { {isOpen && (
- +
diff --git a/app/routes/options/index.tsx b/app/routes/options/index.tsx index 54fa6dc7..d9ea23c8 100644 --- a/app/routes/options/index.tsx +++ b/app/routes/options/index.tsx @@ -177,7 +177,7 @@ export default function Options() { return ( { if (transition.state === 'submitting') { e.preventDefault() diff --git a/remix.config.js b/remix.config.js index 40bc410b..ee7fbbb3 100644 --- a/remix.config.js +++ b/remix.config.js @@ -1,10 +1,14 @@ /** @type {import('@remix-run/dev').AppConfig} */ -module.exports = {serverBuildTarget: "cloudflare-pages", - server: "./server.js", +module.exports = { + serverBuildTarget: 'cloudflare-pages', + server: './server.js', devServerBroadcastDelay: 1000, - ignoredRouteFiles: ["**/.*"], + ignoredRouteFiles: ['**/.*'], // appDirectory: "app", // assetsBuildDirectory: "public/build", // serverBuildPath: "build/index.js", // publicPath: "/build/", -}; + future: { + v2_normalizeFormMethod: true + } +} From 9fa5cc69e9413a91790b72a90ef34db0b86dabda Mon Sep 17 00:00:00 2001 From: Ben Webb Date: Thu, 6 Jul 2023 21:34:03 +0100 Subject: [PATCH 2/2] task/remix-v2-routes --- .../index.tsx => allagan-data.tsx} | 0 ...ries.tsx => ffxiv.marketshare.queries.tsx} | 0 .../index.tsx => ffxiv.marketshare.tsx} | 0 .../index.tsx => ffxiv.self-purchase.tsx} | 0 app/routes/ffxiv.tsx | 9 - app/routes/options.tsx | 291 ++++++++++++++++- app/routes/options/index.tsx | 292 ------------------ app/routes/price-sniper.tsx | 277 ++++++++++++++++- app/routes/price-sniper/index.tsx | 276 ----------------- app/routes/undercut.tsx | 129 +++++++- app/routes/undercut/index.tsx | 128 -------- ...ertisements.tsx => why.advertisements.tsx} | 0 app/routes/why.tsx | 9 - app/routes/wow.tsx | 120 ++++++- app/routes/wow/index.tsx | 115 ------- remix.config.js | 3 +- 16 files changed, 793 insertions(+), 856 deletions(-) rename app/routes/{allagan-data/index.tsx => allagan-data.tsx} (100%) rename app/routes/{ffxiv/marketshare/queries.tsx => ffxiv.marketshare.queries.tsx} (100%) rename app/routes/{ffxiv/marketshare/index.tsx => ffxiv.marketshare.tsx} (100%) rename app/routes/{ffxiv/self-purchase/index.tsx => ffxiv.self-purchase.tsx} (100%) delete mode 100644 app/routes/ffxiv.tsx delete mode 100644 app/routes/options/index.tsx delete mode 100644 app/routes/price-sniper/index.tsx delete mode 100644 app/routes/undercut/index.tsx rename app/routes/{why/advertisements.tsx => why.advertisements.tsx} (100%) delete mode 100644 app/routes/why.tsx delete mode 100644 app/routes/wow/index.tsx diff --git a/app/routes/allagan-data/index.tsx b/app/routes/allagan-data.tsx similarity index 100% rename from app/routes/allagan-data/index.tsx rename to app/routes/allagan-data.tsx diff --git a/app/routes/ffxiv/marketshare/queries.tsx b/app/routes/ffxiv.marketshare.queries.tsx similarity index 100% rename from app/routes/ffxiv/marketshare/queries.tsx rename to app/routes/ffxiv.marketshare.queries.tsx diff --git a/app/routes/ffxiv/marketshare/index.tsx b/app/routes/ffxiv.marketshare.tsx similarity index 100% rename from app/routes/ffxiv/marketshare/index.tsx rename to app/routes/ffxiv.marketshare.tsx diff --git a/app/routes/ffxiv/self-purchase/index.tsx b/app/routes/ffxiv.self-purchase.tsx similarity index 100% rename from app/routes/ffxiv/self-purchase/index.tsx rename to app/routes/ffxiv.self-purchase.tsx diff --git a/app/routes/ffxiv.tsx b/app/routes/ffxiv.tsx deleted file mode 100644 index 02d8a893..00000000 --- a/app/routes/ffxiv.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { Outlet } from '@remix-run/react' - -export default function FFXIV() { - return ( -
- -
- ) -} diff --git a/app/routes/options.tsx b/app/routes/options.tsx index 9d23276e..d9ea23c8 100644 --- a/app/routes/options.tsx +++ b/app/routes/options.tsx @@ -1,9 +1,292 @@ -import { Outlet } from '@remix-run/react' +import { CheckIcon } from '@heroicons/react/solid' +import { + Form, + useActionData, + useLoaderData, + useTransition +} from '@remix-run/react' +import type { ActionFunction, LoaderFunction } from '@remix-run/cloudflare' +import { json, redirect } from '@remix-run/cloudflare' +import { z } from 'zod' +import { + commitSession, + DATA_CENTER, + FF14_WORLD, + getSession, + getUserSessionData, + WOW_REALM_ID, + WOW_REALM_NAME, + WOW_REGION +} from '~/sessions' +import { Switch } from '@headlessui/react' +import { classNames } from '~/utils' +import { useDispatch } from 'react-redux' +import { + setFFxivWorld, + setWoWRealmData, + toggleDarkMode +} from '~/redux/reducers/userSlice' +import { useTypedSelector } from '~/redux/useTypedSelector' +import React, { useState } from 'react' +import { validateServerAndRegion } from '~/utils/WoWServers' +import { validateWorldAndDataCenter } from '~/utils/locations' +import RegionAndServerSelect from '~/components/form/WoW/RegionAndServerSelect' +import SelectDCandWorld from '~/components/form/select/SelectWorld' +import type { WoWServerData, WoWServerRegion } from '~/requests/WoW/types' +import { PageWrapper } from '~/components/Common' -export default function Options() { +export const validator = z.object({ + data_center: z.string().min(1), + world: z.string().min(1), + region: z.union([z.literal('NA'), z.literal('EU')]), + homeRealm: z.string().min(1) +}) + +export const action: ActionFunction = async ({ request }) => { + const formData = await request.formData() + const formPayload = Object.fromEntries(formData) + + const result = validator.safeParse(formPayload) + + if (!result.success) { + return json(result) + } + + const [homeRealmId, homeRealmName] = result.data.homeRealm.split('---') + + const session = await getSession(request.headers.get('Cookie')) + + const { server, region } = validateServerAndRegion( + result.data.region, + homeRealmId, + homeRealmName + ) + + const { data_center, world } = validateWorldAndDataCenter( + result.data.world, + result.data.data_center + ) + + session.set(DATA_CENTER, data_center) + session.set(FF14_WORLD, world) + session.set(WOW_REALM_ID, server.id) + session.set(WOW_REALM_NAME, server.name) + session.set(WOW_REGION, region) + + // Set the new option, yeet back to index (but save against session data within the cookie) + return redirect('/', { + headers: { + 'Set-Cookie': await commitSession(session) + } + }) +} + +const OptionSection = ({ + title, + description, + children, + hideHRule +}: { + title: string + description: string + children: React.ReactNode + hideHRule?: boolean +}) => { return ( -
- +
+
+ <> +
+
+
+
+

+ {title} +

+

+ {description} +

+
+
+
+
+
+ {children} +
+
+
+
+
+ + {!hideHRule && ( +
) } + +export const loader: LoaderFunction = async ({ request }) => { + const session = await getSession(request.headers.get('Cookie')) + const { getWorld, getDataCenter, getWoWSessionData } = + await getUserSessionData(request) + + const data_center = getDataCenter() + const world = getWorld() + const { server, region } = getWoWSessionData() + + return json({ + ...session.data, + data_center, + world, + wowRealm: server, + wowRegion: region + }) +} + +export default function Options() { + const data = useLoaderData() + const transition = useTransition() + const actionData = useActionData() + + const dispatch = useDispatch() + const { darkmode } = useTypedSelector((state) => state.user) + + const [ffxivWorld, setFfxivWorld] = useState<{ + data_center: string + world: string + }>({ data_center: data.data_center, world: data.world }) + + const [wowRealm, setWoWRealm] = useState<{ + server: WoWServerData + region: WoWServerRegion + }>({ + region: data.wowRegion, + server: data.wowRealm + }) + + const handleDarkModeToggle = () => { + dispatch(toggleDarkMode()) + } + + return ( + + { + if (transition.state === 'submitting') { + e.preventDefault() + return + } + dispatch(setFFxivWorld(ffxivWorld)) + dispatch(setWoWRealmData(wowRealm)) + }}> +
+
+

+ Options +

+
+
+
+
+

+ Site Configuration +

+
+
+ + + +
+
+
+
+ + { + setFfxivWorld(newWorld) + }} + /> + + + { + if (newServer) { + setWoWRealm((state) => ({ ...state, server: newServer })) + } + }} + regionOnChange={(newRegion) => { + if (newRegion) { + setWoWRealm((state) => ({ ...state, region: newRegion })) + } + }} + /> + + + + + + Enable Dark Mode + + + I confirm, I have weak eyeballs. + + + {typeof document !== 'undefined' && ( + + + + )} + + + +
+ ) +} diff --git a/app/routes/options/index.tsx b/app/routes/options/index.tsx deleted file mode 100644 index d9ea23c8..00000000 --- a/app/routes/options/index.tsx +++ /dev/null @@ -1,292 +0,0 @@ -import { CheckIcon } from '@heroicons/react/solid' -import { - Form, - useActionData, - useLoaderData, - useTransition -} from '@remix-run/react' -import type { ActionFunction, LoaderFunction } from '@remix-run/cloudflare' -import { json, redirect } from '@remix-run/cloudflare' -import { z } from 'zod' -import { - commitSession, - DATA_CENTER, - FF14_WORLD, - getSession, - getUserSessionData, - WOW_REALM_ID, - WOW_REALM_NAME, - WOW_REGION -} from '~/sessions' -import { Switch } from '@headlessui/react' -import { classNames } from '~/utils' -import { useDispatch } from 'react-redux' -import { - setFFxivWorld, - setWoWRealmData, - toggleDarkMode -} from '~/redux/reducers/userSlice' -import { useTypedSelector } from '~/redux/useTypedSelector' -import React, { useState } from 'react' -import { validateServerAndRegion } from '~/utils/WoWServers' -import { validateWorldAndDataCenter } from '~/utils/locations' -import RegionAndServerSelect from '~/components/form/WoW/RegionAndServerSelect' -import SelectDCandWorld from '~/components/form/select/SelectWorld' -import type { WoWServerData, WoWServerRegion } from '~/requests/WoW/types' -import { PageWrapper } from '~/components/Common' - -export const validator = z.object({ - data_center: z.string().min(1), - world: z.string().min(1), - region: z.union([z.literal('NA'), z.literal('EU')]), - homeRealm: z.string().min(1) -}) - -export const action: ActionFunction = async ({ request }) => { - const formData = await request.formData() - const formPayload = Object.fromEntries(formData) - - const result = validator.safeParse(formPayload) - - if (!result.success) { - return json(result) - } - - const [homeRealmId, homeRealmName] = result.data.homeRealm.split('---') - - const session = await getSession(request.headers.get('Cookie')) - - const { server, region } = validateServerAndRegion( - result.data.region, - homeRealmId, - homeRealmName - ) - - const { data_center, world } = validateWorldAndDataCenter( - result.data.world, - result.data.data_center - ) - - session.set(DATA_CENTER, data_center) - session.set(FF14_WORLD, world) - session.set(WOW_REALM_ID, server.id) - session.set(WOW_REALM_NAME, server.name) - session.set(WOW_REGION, region) - - // Set the new option, yeet back to index (but save against session data within the cookie) - return redirect('/', { - headers: { - 'Set-Cookie': await commitSession(session) - } - }) -} - -const OptionSection = ({ - title, - description, - children, - hideHRule -}: { - title: string - description: string - children: React.ReactNode - hideHRule?: boolean -}) => { - return ( -
-
- <> -
-
-
-
-

- {title} -

-

- {description} -

-
-
-
-
-
- {children} -
-
-
-
-
- - {!hideHRule && ( - -
- ) -} - -export const loader: LoaderFunction = async ({ request }) => { - const session = await getSession(request.headers.get('Cookie')) - const { getWorld, getDataCenter, getWoWSessionData } = - await getUserSessionData(request) - - const data_center = getDataCenter() - const world = getWorld() - const { server, region } = getWoWSessionData() - - return json({ - ...session.data, - data_center, - world, - wowRealm: server, - wowRegion: region - }) -} - -export default function Options() { - const data = useLoaderData() - const transition = useTransition() - const actionData = useActionData() - - const dispatch = useDispatch() - const { darkmode } = useTypedSelector((state) => state.user) - - const [ffxivWorld, setFfxivWorld] = useState<{ - data_center: string - world: string - }>({ data_center: data.data_center, world: data.world }) - - const [wowRealm, setWoWRealm] = useState<{ - server: WoWServerData - region: WoWServerRegion - }>({ - region: data.wowRegion, - server: data.wowRealm - }) - - const handleDarkModeToggle = () => { - dispatch(toggleDarkMode()) - } - - return ( - -
{ - if (transition.state === 'submitting') { - e.preventDefault() - return - } - dispatch(setFFxivWorld(ffxivWorld)) - dispatch(setWoWRealmData(wowRealm)) - }}> -
-
-

- Options -

-
-
-
-
-

- Site Configuration -

-
-
- - - -
-
-
-
- - { - setFfxivWorld(newWorld) - }} - /> - - - { - if (newServer) { - setWoWRealm((state) => ({ ...state, server: newServer })) - } - }} - regionOnChange={(newRegion) => { - if (newRegion) { - setWoWRealm((state) => ({ ...state, region: newRegion })) - } - }} - /> - - - - - - Enable Dark Mode - - - I confirm, I have weak eyeballs. - - - {typeof document !== 'undefined' && ( - - - - )} - - -
-
- ) -} diff --git a/app/routes/price-sniper.tsx b/app/routes/price-sniper.tsx index a5c4f1d6..c1e4bdbe 100644 --- a/app/routes/price-sniper.tsx +++ b/app/routes/price-sniper.tsx @@ -1,9 +1,276 @@ -import { Outlet } from '@remix-run/react' +import { useLoaderData } from '@remix-run/react' +import { PageWrapper } from '~/components/Common' +import SmallFormContainer from '~/components/form/SmallFormContainer' +import type { LoaderFunction } from '@remix-run/cloudflare' +import { json } from '@remix-run/cloudflare' +import { getSession } from '~/sessions' +import { validateWorldAndDataCenter } from '~/utils/locations' +import { useState } from 'react' +import { InputWithLabel } from '~/components/form/InputWithLabel' +import Label from '~/components/form/Label' +import HQCheckbox from '~/components/form/HQCheckbox' +import CodeBlock from '~/components/Common/CodeBlock' +import ItemSelect from '~/components/form/select/ItemSelect' + +export const loader: LoaderFunction = async ({ request }) => { + const session = await getSession(request.headers.get('Cookie')) + const { world, data_center } = validateWorldAndDataCenter( + session.get('world'), + session.get('data_center') + ) + return json({ ...session.data, world, data_center }) +} + +interface Auction { + itemID: number + price: number + desiredState: 'above' | 'below' + hq: boolean +} + +interface SubFormItem { + itemName: string + itemID: number + desiredState: 'above' | 'below' + hq: boolean + price: number +} + +export const parseJSONInput = (input: Array, isPrice = true) => { + if (!input.length) { + return '' + } + + return `,\n "user_auctions": [${input + .map(({ itemID, price, desiredState, hq }) => { + return `\n { "itemID": ${itemID}, "${ + isPrice ? 'price' : 'quantity' + }": ${price}, "desired_state": "${desiredState}", "hq": ${hq} }` + }) + .join(',')}\n ]` +} + +const Index = () => { + const { world } = useLoaderData() + const [jsonData, setJsonData] = useState<{ + server: string + userAuctions: Array + }>({ server: world, userAuctions: [] }) + const [subForm, setSubForm] = useState(null) + const [error, setError] = useState(undefined) + const [isPrice, setIsPrice] = useState(true) + + const jsonToDisplay = `{\n "home_server": "${ + jsonData.server + }"${parseJSONInput(jsonData.userAuctions, isPrice)}\n}` + + const isPriceValue = isPrice ? 'price' : 'quantity' + + const description = `To setup ${isPriceValue} alerts search, enter items and a desired + ${isPriceValue}. Copy and paste the below input for the Discord Bot in the + Saddlebage Exchange discord server.` -export default function PriceSniper() { return ( -
- -
+ + <> + { + e.preventDefault() + if (!subForm) { + setError('No item selected') + return + } + + const userAuctions = [...jsonData.userAuctions] + + const { itemID, price, desiredState, hq } = subForm + + userAuctions.push({ + itemID, + price, + desiredState, + hq + }) + + setJsonData({ ...jsonData, userAuctions }) + setSubForm(null) + }} + buttonTitle="Add"> +
+ { + if (error) { + setError(undefined) + } + }} + onSelectChange={(item) => { + if (error) { + setError(undefined) + } + + if (!item) { + setSubForm(null) + return + } + + setSubForm({ + itemName: item.name, + itemID: parseInt(item.id, 10), + desiredState: 'below', + hq: false, + price: 1000 + }) + }} + /> + + {subForm && ( +
+
) => { + const value = (event.target as HTMLInputElement).value + if (value === 'price') setIsPrice(true) + if (value === 'quantity') setIsPrice(false) + }}> + +
+ + +
+
+ { + const value = event.target.value + if (!value || isNaN(parseInt(value, 10))) { + setSubForm({ + ...subForm, + price: 1000 + }) + return + } + + const price = parseInt(value, 10) + + if (price < 0) { + setSubForm({ + ...subForm, + price: 0 + }) + return + } + + setSubForm({ + ...subForm, + price: parseInt(value, 10) + }) + }} + onChange={(e) => { + setSubForm({ + ...subForm, + price: parseInt(e.target.value, 10) + }) + }} + /> +
) => { + const value = (event.target as HTMLInputElement).value + if (value === 'above' || value === 'below') { + setSubForm({ + ...subForm, + desiredState: value + }) + } + }}> + + + +
+ + setSubForm({ + ...subForm, + hq: event.target.checked + }) + } + /> +
+ )} +
+
+ +
+ alert('Copied to clipboard!')}> +

+ For the discord bot, use the{' '} + {isPrice ? ( + + commands '/ff price-register' or{' '} + '/ff price-update' + + ) : ( + + command '/ff quantity-register' + + )} + ! +

+
+
+ +
) } + +export default Index diff --git a/app/routes/price-sniper/index.tsx b/app/routes/price-sniper/index.tsx deleted file mode 100644 index c1e4bdbe..00000000 --- a/app/routes/price-sniper/index.tsx +++ /dev/null @@ -1,276 +0,0 @@ -import { useLoaderData } from '@remix-run/react' -import { PageWrapper } from '~/components/Common' -import SmallFormContainer from '~/components/form/SmallFormContainer' -import type { LoaderFunction } from '@remix-run/cloudflare' -import { json } from '@remix-run/cloudflare' -import { getSession } from '~/sessions' -import { validateWorldAndDataCenter } from '~/utils/locations' -import { useState } from 'react' -import { InputWithLabel } from '~/components/form/InputWithLabel' -import Label from '~/components/form/Label' -import HQCheckbox from '~/components/form/HQCheckbox' -import CodeBlock from '~/components/Common/CodeBlock' -import ItemSelect from '~/components/form/select/ItemSelect' - -export const loader: LoaderFunction = async ({ request }) => { - const session = await getSession(request.headers.get('Cookie')) - const { world, data_center } = validateWorldAndDataCenter( - session.get('world'), - session.get('data_center') - ) - return json({ ...session.data, world, data_center }) -} - -interface Auction { - itemID: number - price: number - desiredState: 'above' | 'below' - hq: boolean -} - -interface SubFormItem { - itemName: string - itemID: number - desiredState: 'above' | 'below' - hq: boolean - price: number -} - -export const parseJSONInput = (input: Array, isPrice = true) => { - if (!input.length) { - return '' - } - - return `,\n "user_auctions": [${input - .map(({ itemID, price, desiredState, hq }) => { - return `\n { "itemID": ${itemID}, "${ - isPrice ? 'price' : 'quantity' - }": ${price}, "desired_state": "${desiredState}", "hq": ${hq} }` - }) - .join(',')}\n ]` -} - -const Index = () => { - const { world } = useLoaderData() - const [jsonData, setJsonData] = useState<{ - server: string - userAuctions: Array - }>({ server: world, userAuctions: [] }) - const [subForm, setSubForm] = useState(null) - const [error, setError] = useState(undefined) - const [isPrice, setIsPrice] = useState(true) - - const jsonToDisplay = `{\n "home_server": "${ - jsonData.server - }"${parseJSONInput(jsonData.userAuctions, isPrice)}\n}` - - const isPriceValue = isPrice ? 'price' : 'quantity' - - const description = `To setup ${isPriceValue} alerts search, enter items and a desired - ${isPriceValue}. Copy and paste the below input for the Discord Bot in the - Saddlebage Exchange discord server.` - - return ( - - <> - { - e.preventDefault() - if (!subForm) { - setError('No item selected') - return - } - - const userAuctions = [...jsonData.userAuctions] - - const { itemID, price, desiredState, hq } = subForm - - userAuctions.push({ - itemID, - price, - desiredState, - hq - }) - - setJsonData({ ...jsonData, userAuctions }) - setSubForm(null) - }} - buttonTitle="Add"> -
- { - if (error) { - setError(undefined) - } - }} - onSelectChange={(item) => { - if (error) { - setError(undefined) - } - - if (!item) { - setSubForm(null) - return - } - - setSubForm({ - itemName: item.name, - itemID: parseInt(item.id, 10), - desiredState: 'below', - hq: false, - price: 1000 - }) - }} - /> - - {subForm && ( -
-
) => { - const value = (event.target as HTMLInputElement).value - if (value === 'price') setIsPrice(true) - if (value === 'quantity') setIsPrice(false) - }}> - -
- - -
-
- { - const value = event.target.value - if (!value || isNaN(parseInt(value, 10))) { - setSubForm({ - ...subForm, - price: 1000 - }) - return - } - - const price = parseInt(value, 10) - - if (price < 0) { - setSubForm({ - ...subForm, - price: 0 - }) - return - } - - setSubForm({ - ...subForm, - price: parseInt(value, 10) - }) - }} - onChange={(e) => { - setSubForm({ - ...subForm, - price: parseInt(e.target.value, 10) - }) - }} - /> -
) => { - const value = (event.target as HTMLInputElement).value - if (value === 'above' || value === 'below') { - setSubForm({ - ...subForm, - desiredState: value - }) - } - }}> - - - -
- - setSubForm({ - ...subForm, - hq: event.target.checked - }) - } - /> -
- )} -
-
- -
- alert('Copied to clipboard!')}> -

- For the discord bot, use the{' '} - {isPrice ? ( - - commands '/ff price-register' or{' '} - '/ff price-update' - - ) : ( - - command '/ff quantity-register' - - )} - ! -

-
-
- -
- ) -} - -export default Index diff --git a/app/routes/undercut.tsx b/app/routes/undercut.tsx index 9d23276e..4067e6c1 100644 --- a/app/routes/undercut.tsx +++ b/app/routes/undercut.tsx @@ -1,9 +1,128 @@ -import { Outlet } from '@remix-run/react' +import { useActionData, useTransition } from '@remix-run/react' +import { PageWrapper } from '~/components/Common' +import { InputWithLabel } from '~/components/form/InputWithLabel' +import SmallFormContainer from '~/components/form/SmallFormContainer' +import type { ActionFunction } from '@remix-run/cloudflare' +import { json } from '@remix-run/cloudflare' +import GetSellerId from '~/requests/GetSellerId' +import { getUserSessionData } from '~/sessions' +import ItemSelect from '~/components/form/select/ItemSelect' +import NoResults from '~/components/Common/NoResults' +import Results from '../components/FFXIVResults/UndercutAlert/Results' -export default function Options() { +export const action: ActionFunction = async ({ request }) => { + const formData = await request.formData() + const session = await getUserSessionData(request) + + const homeServer = session.getWorld() + + const retainerName = formData.get('retainerName') + + if ( + !retainerName || + typeof retainerName !== 'string' || + !retainerName.length + ) { + return json({ exception: 'Missing retainer name' }) + } + + const itemIdData = formData.get('itemId') + if ( + !itemIdData || + typeof itemIdData !== 'string' || + isNaN(parseInt(itemIdData)) + ) { + return json({ exception: 'Missing item' }) + } + + const itemId = parseInt(itemIdData) + + const response = await GetSellerId({ + itemId, + homeServer, + retainerName + }) + if (!response.ok) { + return json({ exception: response.statusText }) + } + + return json({ data: await response.json(), homeServer, itemId }) +} + +const Description = () => { return ( -
- -
+

+ To setup undercut alerts search{' '} + + universalis + {' '} + for one of your retainers by name (with exact capitilization) and an item + they are selling on the Market Board. +

) } + +const Index = () => { + const transition = useTransition() + const results = useActionData() + + const onSubmit = (e: React.MouseEvent) => { + if (transition.state === 'submitting') { + e.preventDefault() + } + } + + const error = + results && results?.data?.exception ? results.data.exception : undefined + + if (results && results.data && !error) { + if (!results.data?.seller_id) { + const itemId = results?.itemId + + const link = itemId + ? `https://universalis.app/market/${itemId}` + : 'https://universalis.app' + + return + } + + return ( + + ) + } + + return ( + + +
+ +
+ +
+
+
+
+ ) +} + +export default Index diff --git a/app/routes/undercut/index.tsx b/app/routes/undercut/index.tsx deleted file mode 100644 index 3488429d..00000000 --- a/app/routes/undercut/index.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import { useActionData, useTransition } from '@remix-run/react' -import { PageWrapper } from '~/components/Common' -import { InputWithLabel } from '~/components/form/InputWithLabel' -import SmallFormContainer from '~/components/form/SmallFormContainer' -import type { ActionFunction } from '@remix-run/cloudflare' -import { json } from '@remix-run/cloudflare' -import GetSellerId from '~/requests/GetSellerId' -import { getUserSessionData } from '~/sessions' -import ItemSelect from '~/components/form/select/ItemSelect' -import NoResults from '~/components/Common/NoResults' -import Results from '../../components/FFXIVResults/UndercutAlert/Results' - -export const action: ActionFunction = async ({ request }) => { - const formData = await request.formData() - const session = await getUserSessionData(request) - - const homeServer = session.getWorld() - - const retainerName = formData.get('retainerName') - - if ( - !retainerName || - typeof retainerName !== 'string' || - !retainerName.length - ) { - return json({ exception: 'Missing retainer name' }) - } - - const itemIdData = formData.get('itemId') - if ( - !itemIdData || - typeof itemIdData !== 'string' || - isNaN(parseInt(itemIdData)) - ) { - return json({ exception: 'Missing item' }) - } - - const itemId = parseInt(itemIdData) - - const response = await GetSellerId({ - itemId, - homeServer, - retainerName - }) - if (!response.ok) { - return json({ exception: response.statusText }) - } - - return json({ data: await response.json(), homeServer, itemId }) -} - -const Description = () => { - return ( -

- To setup undercut alerts search{' '} - - universalis - {' '} - for one of your retainers by name (with exact capitilization) and an item - they are selling on the Market Board. -

- ) -} - -const Index = () => { - const transition = useTransition() - const results = useActionData() - - const onSubmit = (e: React.MouseEvent) => { - if (transition.state === 'submitting') { - e.preventDefault() - } - } - - const error = - results && results?.data?.exception ? results.data.exception : undefined - - if (results && results.data && !error) { - if (!results.data?.seller_id) { - const itemId = results?.itemId - - const link = itemId - ? `https://universalis.app/market/${itemId}` - : 'https://universalis.app' - - return - } - - return ( - - ) - } - - return ( - - -
- -
- -
-
-
-
- ) -} - -export default Index diff --git a/app/routes/why/advertisements.tsx b/app/routes/why.advertisements.tsx similarity index 100% rename from app/routes/why/advertisements.tsx rename to app/routes/why.advertisements.tsx diff --git a/app/routes/why.tsx b/app/routes/why.tsx deleted file mode 100644 index ea73335c..00000000 --- a/app/routes/why.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { Outlet } from '@remix-run/react' - -export default function Why() { - return ( -
- -
- ) -} diff --git a/app/routes/wow.tsx b/app/routes/wow.tsx index e5140d3a..2032201f 100644 --- a/app/routes/wow.tsx +++ b/app/routes/wow.tsx @@ -1,19 +1,115 @@ -import type { ErrorBoundaryComponent } from '@remix-run/cloudflare' -import { Outlet } from '@remix-run/react' +import { DocumentSearchIcon } from '@heroicons/react/outline' +import Banner from '~/components/Common/Banner' +import TileLink from '~/components/Common/TileLink' -export default function Wow() { - return ( -
- -
- ) -} +const recommendedQueries = [ + { + name: 'Alpha Build Cross Server Trade Tools', + description: + 'Check out the latest and greatest Alpha versions of our server to server trade tools just in time for 10.1.5 cross realm trading!', + Icon: DocumentSearchIcon, + href: 'https://temp.saddlebagexchange.com/', + external: true + }, + { + name: 'Ultra Fast Mega-Alerts Sniper', + description: + 'Try our standalone Mega-Alerts Sniper, designed to snipe across all realms for the best deals seconds after the Blizzard AH API updates so you get there first!', + Icon: DocumentSearchIcon, + href: 'https://github.com/ff14-advanced-market-search/mega-alerts', + external: true + }, + { + name: 'Region Wide Undercut Checker', + description: + 'Use our Addon with this search to check all of your undercuts on all your alts on one page!', + Icon: DocumentSearchIcon, + href: '/wow/region-undercut' + }, + { + name: 'Undercut Alerts Curseforge Addon', + description: 'The addon for our Undercut Checks and Alerts!', + Icon: DocumentSearchIcon, + href: 'https://www.curseforge.com/wow/addons/saddlebag-exchange', + external: true + }, + { + name: 'Global Export Item Search', + description: + 'Search other servers to find the best place to sell & buy items', + Icon: DocumentSearchIcon, + href: '/wow/export-search' + }, + { + name: 'Dragonflight Marketshare Overview', + description: + 'Find out what Dragonflight items are actually selling and what are the best items to sell. Shows the top 200 items matching your search.', + Icon: DocumentSearchIcon, + href: '/wow/marketshare' + }, + { + name: 'Dragonflight Commodity Shortage Futures', + description: + 'Find Commodity Shortages and Price Spikes BEFORE they happen and be there first!', + Icon: DocumentSearchIcon, + href: '/wow/shortage-predictor' + }, + { + name: 'Legacy Marketshare Overview', + description: + 'Find out what Legacy items are actually selling and what are the best items to sell. Shows the top 200 items matching your search.', + Icon: DocumentSearchIcon, + href: '/wow/legacy-marketshare' + }, + { + name: 'Price Sniper and Price Spike Alerts', + description: + 'Alerts you when prices for items go above or below a price you pick!', + Icon: DocumentSearchIcon, + href: '/wow/price-alert' + }, + { + name: 'Local Realm Shortage Finder', + description: + 'Searches for items on your local server / realm that you can flip and take over the market!', + Icon: DocumentSearchIcon, + href: '/wow/shortages/single' + }, + { + name: 'Commodity Shortage Finder', + description: + 'Searches for region wide commodities that you can flip and take over the market!', + Icon: DocumentSearchIcon, + href: '/wow/shortages/commodities' + }, + { + name: 'Server Transfer Trading Search', + description: + 'Search for items that can be bought cheaply on a your home server and sold for a profit when transfering realms.', + Icon: DocumentSearchIcon, + href: '/wow/full-scan' + } +] -export const ErrorBoundary: ErrorBoundaryComponent = ({ error }) => { +export default function Index() { return ( <> -

Erg! Something's broken! Maybe try again.

-
{error.message}
+
+ +
+
+

+ World of Warcraft Queries +

+
+ {recommendedQueries.map((query) => { + return + })} +
+
+
+
) } diff --git a/app/routes/wow/index.tsx b/app/routes/wow/index.tsx deleted file mode 100644 index 2032201f..00000000 --- a/app/routes/wow/index.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { DocumentSearchIcon } from '@heroicons/react/outline' -import Banner from '~/components/Common/Banner' -import TileLink from '~/components/Common/TileLink' - -const recommendedQueries = [ - { - name: 'Alpha Build Cross Server Trade Tools', - description: - 'Check out the latest and greatest Alpha versions of our server to server trade tools just in time for 10.1.5 cross realm trading!', - Icon: DocumentSearchIcon, - href: 'https://temp.saddlebagexchange.com/', - external: true - }, - { - name: 'Ultra Fast Mega-Alerts Sniper', - description: - 'Try our standalone Mega-Alerts Sniper, designed to snipe across all realms for the best deals seconds after the Blizzard AH API updates so you get there first!', - Icon: DocumentSearchIcon, - href: 'https://github.com/ff14-advanced-market-search/mega-alerts', - external: true - }, - { - name: 'Region Wide Undercut Checker', - description: - 'Use our Addon with this search to check all of your undercuts on all your alts on one page!', - Icon: DocumentSearchIcon, - href: '/wow/region-undercut' - }, - { - name: 'Undercut Alerts Curseforge Addon', - description: 'The addon for our Undercut Checks and Alerts!', - Icon: DocumentSearchIcon, - href: 'https://www.curseforge.com/wow/addons/saddlebag-exchange', - external: true - }, - { - name: 'Global Export Item Search', - description: - 'Search other servers to find the best place to sell & buy items', - Icon: DocumentSearchIcon, - href: '/wow/export-search' - }, - { - name: 'Dragonflight Marketshare Overview', - description: - 'Find out what Dragonflight items are actually selling and what are the best items to sell. Shows the top 200 items matching your search.', - Icon: DocumentSearchIcon, - href: '/wow/marketshare' - }, - { - name: 'Dragonflight Commodity Shortage Futures', - description: - 'Find Commodity Shortages and Price Spikes BEFORE they happen and be there first!', - Icon: DocumentSearchIcon, - href: '/wow/shortage-predictor' - }, - { - name: 'Legacy Marketshare Overview', - description: - 'Find out what Legacy items are actually selling and what are the best items to sell. Shows the top 200 items matching your search.', - Icon: DocumentSearchIcon, - href: '/wow/legacy-marketshare' - }, - { - name: 'Price Sniper and Price Spike Alerts', - description: - 'Alerts you when prices for items go above or below a price you pick!', - Icon: DocumentSearchIcon, - href: '/wow/price-alert' - }, - { - name: 'Local Realm Shortage Finder', - description: - 'Searches for items on your local server / realm that you can flip and take over the market!', - Icon: DocumentSearchIcon, - href: '/wow/shortages/single' - }, - { - name: 'Commodity Shortage Finder', - description: - 'Searches for region wide commodities that you can flip and take over the market!', - Icon: DocumentSearchIcon, - href: '/wow/shortages/commodities' - }, - { - name: 'Server Transfer Trading Search', - description: - 'Search for items that can be bought cheaply on a your home server and sold for a profit when transfering realms.', - Icon: DocumentSearchIcon, - href: '/wow/full-scan' - } -] - -export default function Index() { - return ( - <> -
- -
-
-

- World of Warcraft Queries -

-
- {recommendedQueries.map((query) => { - return - })} -
-
-
-
- - ) -} diff --git a/remix.config.js b/remix.config.js index ee7fbbb3..69c2c12b 100644 --- a/remix.config.js +++ b/remix.config.js @@ -9,6 +9,7 @@ module.exports = { // serverBuildPath: "build/index.js", // publicPath: "/build/", future: { - v2_normalizeFormMethod: true + v2_normalizeFormMethod: true, + v2_routeConvention: true } }