diff --git a/examples/with-redis/lib/redis.js b/examples/with-redis/lib/redis.ts similarity index 100% rename from examples/with-redis/lib/redis.js rename to examples/with-redis/lib/redis.ts diff --git a/examples/with-redis/next-env.d.ts b/examples/with-redis/next-env.d.ts new file mode 100644 index 000000000000..4f11a03dc6cc --- /dev/null +++ b/examples/with-redis/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/examples/with-redis/package.json b/examples/with-redis/package.json index 3323648bba82..bae8c5799aed 100644 --- a/examples/with-redis/package.json +++ b/examples/with-redis/package.json @@ -19,6 +19,11 @@ "uuid": "^8.3.2" }, "devDependencies": { - "@tailwindcss/jit": "0.1.1" + "@tailwindcss/jit": "0.1.1", + "@types/ioredis": "4.28.8", + "@types/node": "^16.11.25", + "@types/react": "^17.0.2", + "@types/uuid": "8.3.4", + "typescript": "^4.5.5" } } diff --git a/examples/with-redis/pages/_app.js b/examples/with-redis/pages/_app.tsx similarity index 81% rename from examples/with-redis/pages/_app.js rename to examples/with-redis/pages/_app.tsx index 8e5dce4cce7e..77e5950f7944 100644 --- a/examples/with-redis/pages/_app.js +++ b/examples/with-redis/pages/_app.tsx @@ -1,7 +1,9 @@ +import type { AppProps } from 'next/app' + import '../styles/globals.css' import { Toaster } from 'react-hot-toast' -function MyApp({ Component, pageProps }) { +function MyApp({ Component, pageProps }: AppProps) { return ( <> diff --git a/examples/with-redis/pages/api/create.js b/examples/with-redis/pages/api/create.ts similarity index 79% rename from examples/with-redis/pages/api/create.js rename to examples/with-redis/pages/api/create.ts index df777bce298a..1a73a03efb88 100644 --- a/examples/with-redis/pages/api/create.js +++ b/examples/with-redis/pages/api/create.ts @@ -1,8 +1,12 @@ +import type { NextApiRequest, NextApiResponse } from 'next' import { v4 as uuidv4 } from 'uuid' import redis from '../../lib/redis' -export default async function create(req, res) { +export default async function create( + req: NextApiRequest, + res: NextApiResponse +) { const { title } = req.body if (!title) { diff --git a/examples/with-redis/pages/api/features.js b/examples/with-redis/pages/api/features.ts similarity index 56% rename from examples/with-redis/pages/api/features.js rename to examples/with-redis/pages/api/features.ts index afa8546703e7..2f00e991fb07 100644 --- a/examples/with-redis/pages/api/features.js +++ b/examples/with-redis/pages/api/features.ts @@ -1,6 +1,11 @@ +import type { NextApiRequest, NextApiResponse } from 'next' + import redis from '../../lib/redis' -export default async function getAllFeatures(req, res) { +export default async function getAllFeatures( + req: NextApiRequest, + res: NextApiResponse +) { const features = (await redis.hvals('features')) .map((entry) => JSON.parse(entry)) .sort((a, b) => b.score - a.score) diff --git a/examples/with-redis/pages/api/subscribe.js b/examples/with-redis/pages/api/subscribe.ts similarity index 72% rename from examples/with-redis/pages/api/subscribe.js rename to examples/with-redis/pages/api/subscribe.ts index 436ff17642cd..544558c36df3 100644 --- a/examples/with-redis/pages/api/subscribe.js +++ b/examples/with-redis/pages/api/subscribe.ts @@ -1,6 +1,11 @@ +import type { NextApiRequest, NextApiResponse } from 'next' + import redis from '../../lib/redis' -export default async function subscribe(req, res) { +export default async function subscribe( + req: NextApiRequest, + res: NextApiResponse +) { const { email } = req.body if (email && validateEmail(email)) { @@ -15,7 +20,7 @@ export default async function subscribe(req, res) { } } -function validateEmail(email) { +function validateEmail(email: string) { const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ return re.test(String(email).toLowerCase()) diff --git a/examples/with-redis/pages/api/vote.js b/examples/with-redis/pages/api/vote.ts similarity index 80% rename from examples/with-redis/pages/api/vote.js rename to examples/with-redis/pages/api/vote.ts index 60879587b18e..8d58246521a8 100644 --- a/examples/with-redis/pages/api/vote.js +++ b/examples/with-redis/pages/api/vote.ts @@ -1,6 +1,11 @@ +import type { NextApiRequest, NextApiResponse } from 'next' + import redis from '../../lib/redis' -export default async function upvote(req, res) { +export default async function upvote( + req: NextApiRequest, + res: NextApiResponse +) { const { title, id } = req.body const ip = req.headers['x-forwarded-for'] || req.headers['Remote_Addr'] || 'NA' diff --git a/examples/with-redis/pages/index.js b/examples/with-redis/pages/index.tsx similarity index 86% rename from examples/with-redis/pages/index.js rename to examples/with-redis/pages/index.tsx index a53fd9895c0a..a4d5c2defa66 100644 --- a/examples/with-redis/pages/index.js +++ b/examples/with-redis/pages/index.tsx @@ -1,13 +1,22 @@ import { useState, useRef } from 'react' +import type { NextApiRequest } from 'next' +import type { MouseEvent } from 'react' import Head from 'next/head' import clsx from 'clsx' import useSWR, { mutate } from 'swr' import toast from 'react-hot-toast' import redis from '../lib/redis' -const fetcher = (url) => fetch(url).then((res) => res.json()) +type Feature = { + id: string + title: string + score: number + ip: string +} + +const fetcher = (url: string) => fetch(url).then((res) => res.json()) -function LoadingSpinner({ invert }) { +function LoadingSpinner({ invert }: { invert?: boolean }) { return ( { +function Item({ + isFirst, + isLast, + isReleased, + hasVoted, + feature, +}: { + isFirst: boolean + isLast: boolean + isReleased: boolean + hasVoted: boolean + feature: Feature +}) { + const upvote = async (e: MouseEvent) => { e.preventDefault() const res = await fetch('/api/vote', { @@ -85,11 +106,17 @@ function Item({ isFirst, isLast, isReleased, hasVoted, feature }) { ) } -export default function Roadmap({ features, ip }) { +export default function Roadmap({ + features, + ip, +}: { + features: Feature[] + ip: string +}) { const [isCreateLoading, setCreateLoading] = useState(false) const [isEmailLoading, setEmailLoading] = useState(false) - const featureInputRef = useRef(null) - const subscribeInputRef = useRef(null) + const featureInputRef = useRef(null) + const subscribeInputRef = useRef(null) const { data, error } = useSWR('/api/features', fetcher, { initialData: { features }, @@ -99,13 +126,13 @@ export default function Roadmap({ features, ip }) { toast.error(error) } - const addFeature = async (e) => { + const addFeature = async (e: MouseEvent) => { e.preventDefault() setCreateLoading(true) const res = await fetch('/api/create', { body: JSON.stringify({ - title: featureInputRef.current.value, + title: featureInputRef?.current?.value ?? '', }), headers: { 'Content-Type': 'application/json', @@ -122,16 +149,18 @@ export default function Roadmap({ features, ip }) { } mutate('/api/features') - featureInputRef.current.value = '' + if (featureInputRef.current) { + featureInputRef.current.value = '' + } } - const subscribe = async (e) => { + const subscribe = async (e: MouseEvent) => { e.preventDefault() setEmailLoading(true) const res = await fetch('/api/subscribe', { body: JSON.stringify({ - email: subscribeInputRef.current.value, + email: subscribeInputRef?.current?.value ?? '', }), headers: { 'Content-Type': 'application/json', @@ -147,7 +176,10 @@ export default function Roadmap({ features, ip }) { } toast.success('You are now subscribed to feature updates!') - subscribeInputRef.current.value = '' + + if (subscribeInputRef.current) { + subscribeInputRef.current.value = '' + } } return ( @@ -189,7 +221,7 @@ export default function Roadmap({ features, ip }) {
- {data.features.map((feature, index) => ( + {data.features.map((feature: Feature, index: number) => (