minor changes pt.2

This commit is contained in:
skidoodle 2023-12-12 13:16:08 +01:00
parent 6275b083db
commit 76f75a2f60
17 changed files with 416 additions and 276 deletions

35
.eslintrc.cjs Normal file
View file

@ -0,0 +1,35 @@
/** @type {import("eslint").Linter.Config} */
const config = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: true,
},
plugins: ['@typescript-eslint'],
extends: [
'plugin:@next/next/recommended',
'plugin:@typescript-eslint/recommended-type-checked',
'plugin:@typescript-eslint/stylistic-type-checked',
],
rules: {
'@typescript-eslint/array-type': 'off',
'@typescript-eslint/consistent-type-definitions': 'off',
'@typescript-eslint/consistent-type-imports': [
'warn',
{
prefer: 'type-imports',
fixStyle: 'inline-type-imports',
},
],
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
'@typescript-eslint/require-await': 'off',
'@typescript-eslint/no-misused-promises': [
'error',
{
checksVoidReturn: { attributes: false },
},
],
},
}
module.exports = config

View file

@ -11,20 +11,28 @@
},
"dependencies": {
"@nextui-org/react": "^2.2.9",
"@types/node": "20.10.4",
"@types/react": "18.2.43",
"@types/react-dom": "18.2.17",
"@vercel/analytics": "^1.1.1",
"autoprefixer": "10.4.16",
"eslint": "8.55.0",
"eslint-config-next": "14.0.4",
"framer-motion": "^10.16.16",
"next": "14.0.4",
"next-themes": "^0.2.1",
"postcss": "8.4.32",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-icons": "^4.12.0",
"react-icons": "^4.12.0"
},
"devDependencies": {
"@next/eslint-plugin-next": "^14.0.4",
"@types/eslint": "^8.44.8",
"@types/node": "20.10.4",
"@types/react": "18.2.43",
"@types/react-dom": "18.2.17",
"@typescript-eslint/eslint-plugin": "^6.14.0",
"@typescript-eslint/parser": "^6.14.0",
"autoprefixer": "10.4.16",
"eslint": "8.55.0",
"postcss": "8.4.32",
"prettier": "^3.1.1",
"prettier-plugin-tailwindcss": "^0.5.9",
"tailwindcss": "3.3.6",
"typescript": "5.3.3"
}

407
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

8
postcss.config.cjs Normal file
View file

@ -0,0 +1,8 @@
const config = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
module.exports = config

View file

@ -1,6 +0,0 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

6
prettier.config.js Normal file
View file

@ -0,0 +1,6 @@
/** @type {import('prettier').Config & import('prettier-plugin-tailwindcss').PluginOptions} */
const config = {
plugins: ['prettier-plugin-tailwindcss'],
}
export default config

View file

@ -1,27 +1,68 @@
import { Button } from '@nextui-org/react'
import { useState, useEffect } from 'react'
import type { ButtonProps } from '@/utils/props'
import type { ButtonColor } from '@/utils/types'
export const PdfButton: React.FC<{ label: string; link: string }> = ({
label,
link,
}) => (
<Button
isDisabled={!link}
className='w-24 mt-3 text-sm bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-2'
onClick={link ? () => window.open(link) : () => {}}
>
{label}
</Button>
const CustomButton: React.FC<ButtonProps> = ({ label, link }) => {
const [status, setStatus] = useState<number>(0)
const [isLoading, setIsLoading] = useState<boolean>(false)
const checkLinkStatus = async (): Promise<void> => {
if (link) {
try {
setIsLoading(true)
const response = await fetch(
`/api/validate?link=${encodeURIComponent(link)}`
)
const data = (await response.json()) as { status: number }
setStatus(data.status)
} catch (error) {
setStatus(500)
} finally {
setIsLoading(false)
}
}
}
useEffect(() => {
void checkLinkStatus()
}, [link])
const getColor = (): ButtonColor => {
if (status === 200) {
return 'primary'
} else if (status === 404) {
return 'danger'
} else {
return 'default'
}
}
const handleClick = () => {
if (status === 200 && link) {
window.open(link)
} else {
console.error('A hivatkozás nem elérhető.')
}
}
return (
<Button
isDisabled={status !== 200 || !link || isLoading}
isLoading={isLoading}
className='w-24 mt-3 text-sm font-bold py-2 px-2'
color={getColor()}
onClick={handleClick}
>
{label}
</Button>
)
}
export const PdfButton: React.FC<ButtonProps> = ({ label, link }) => (
<CustomButton label={label} link={link} />
)
export const ZipButton: React.FC<{ label: string; link: string }> = ({
label,
link,
}) => (
<Button
isDisabled={!link}
className='w-24 mt-3 text-sm bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-2'
onClick={link ? () => window.open(link) : () => {}}
>
{label}
</Button>
export const ZipButton: React.FC<ButtonProps> = ({ label, link }) => (
<CustomButton label={label} link={link} />
)

View file

@ -1,5 +1,5 @@
import { Select, SelectItem } from '@nextui-org/react'
import { SelectorProps } from '@/utils/props'
import type { SelectorProps } from '@/utils/props'
export const SubjectSelector: React.FC<
Pick<SelectorProps, 'selectedSubject' | 'setSelectedSubject' | 'subjects'>

View file

@ -2,7 +2,7 @@ import { ThemeProvider as NextThemesProvider } from 'next-themes'
import { Analytics } from '@vercel/analytics/react'
import { NextUIProvider } from '@nextui-org/react'
import { Inter } from 'next/font/google'
import { AppProps } from 'next/app'
import type { AppProps } from 'next/app'
import Head from 'next/head'
import '@/styles/globals.css'

View file

@ -1,8 +1,14 @@
import { NextApiRequest, NextApiResponse } from 'next'
import type { NextApiRequest, NextApiResponse } from 'next'
import { subjects } from '@/utils/subjects'
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const { vizsgatargy, ev, idoszak, szint } = req.query
const { vizsgatargy, ev, idoszak, szint } = req.query as {
vizsgatargy: string
ev: string
idoszak: string
szint: string
}
const baseUrl = `https://dload-oktatas.educatio.hu/erettsegi/feladatok_${ev}${idoszak}_${szint}/`
const missingParams = []
@ -17,16 +23,16 @@ export default function handler(req: NextApiRequest, res: NextApiResponse) {
.json({ error: `Hiányzó paraméterek: ${missingParams.join(', ')}` })
}
if (ev! <= '2012') {
if (ev <= '2012') {
return res.status(400).json({ error: 'Érvénytelen év' })
}
const validSubjects = subjects.map((subject) => subject.value)
if (!vizsgatargy || !validSubjects.includes(vizsgatargy as string)) {
if (!vizsgatargy || !validSubjects.includes(vizsgatargy)) {
return res.status(400).json({ error: 'Érvénytelen vizsgatárgy' })
}
let honap
let honap: string
switch (idoszak) {
case 'osz':
honap = 'okt'
@ -38,7 +44,7 @@ export default function handler(req: NextApiRequest, res: NextApiResponse) {
return res.status(400).json({ error: 'Érvénytelen időszak' })
}
let prefix
let prefix: string
switch (szint) {
case 'emelt':
prefix = `e_${vizsgatargy}`
@ -54,7 +60,7 @@ export default function handler(req: NextApiRequest, res: NextApiResponse) {
const utmutato = 'ut'
const forras = 'for'
const megoldas = 'meg'
const shortev = ev!.slice(-2)
const shortev = ev.slice(-2)
let flPdfUrl, utPdfUrl, flZipUrl, utZipUrl
switch (vizsgatargy) {

29
src/pages/api/validate.ts Normal file
View file

@ -0,0 +1,29 @@
import type { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { link } = req.query as { link: string }
let MissingParam: string | null = null
if (!link) {
MissingParam = 'link'
}
if (MissingParam) {
return res.status(400).json({ error: `Hiányzó paraméter: ${MissingParam}` })
}
const domain = link.split('/')[2]
if (domain !== 'dload-oktatas.educatio.hu') {
return res.status(400).json({ error: 'Érvénytelen link' })
}
try {
const response = await fetch(link, { method: 'HEAD' })
const status = response.status
res.status(200).json({ status })
} catch (error) {
res.status(500).json({ error: 'Hiányzó paraméterek:' })
}
}

View file

@ -39,7 +39,7 @@ export default function Home() {
useEffect(() => {
if (selectedLevel && selectedPeriod && selectedSubject && selectedYear) {
fetchData(
void fetchData(
selectedSubject,
selectedYear,
selectedPeriod,

View file

@ -9,25 +9,26 @@ export const fetchData = async (
setutPdfLink: (link: string) => void
) => {
try {
let url = `/api/erettsegi?vizsgatargy=${selectedSubject}&ev=${selectedYear}&idoszak=${selectedPeriod}&szint=${selectedLevel}`
const url = `/api/erettsegi?vizsgatargy=${selectedSubject}&ev=${selectedYear}&idoszak=${selectedPeriod}&szint=${selectedLevel}`
const response = await fetch(url)
if (response.ok) {
const data = await response.json()
const data = (await response.json()) as {
flZipUrl: string
utZipUrl: string
flPdfUrl: string
utPdfUrl: string
}
if (data.utZipUrl && data.flZipUrl) {
setflZipLink(data.flZipUrl)
setutZipLink(data.utZipUrl)
} else {
console.error('Nincs érvényes ZIP link a válaszban.')
}
if (data.utPdfUrl && data.flPdfUrl) {
setflPdfLink(data.flPdfUrl)
setutPdfLink(data.utPdfUrl)
} else {
console.error('Nincs érvényes PDF link a válaszban.')
}
} else {
console.error('Hiba történt az API hívás során.')

View file

@ -10,3 +10,8 @@ export interface SelectorProps {
setSelectedPeriod: React.Dispatch<React.SetStateAction<string>>
setSelectedLevel: React.Dispatch<React.SetStateAction<string>>
}
export interface ButtonProps {
label: string
link: string
}

8
src/utils/types.ts Normal file
View file

@ -0,0 +1,8 @@
export type ButtonColor =
| 'primary'
| 'danger'
| 'default'
| 'secondary'
| 'success'
| 'warning'
| undefined

View file

@ -3,9 +3,7 @@ import { nextui } from '@nextui-org/react'
const config: Config = {
content: [
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
'./src/**/*.tsx',
'./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}',
],
theme: {

View file

@ -1,22 +1,42 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
/* Base Options: */
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"skipLibCheck": true,
"target": "es2022",
"allowJs": true,
"resolveJsonModule": true,
"moduleDetection": "force",
"isolatedModules": true,
/* Strictness */
"strict": true,
"noUncheckedIndexedAccess": true,
"checkJs": true,
/* Bundled projects */
"lib": ["dom", "dom.iterable", "ES2022"],
"noEmit": true,
"module": "ESNext",
"moduleResolution": "Bundler",
"jsx": "preserve",
"plugins": [{ "name": "next" }],
"incremental": true,
/* Path Aliases */
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"include": [
".eslintrc.cjs",
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
"**/*.cjs",
"**/*.js",
".next/types/**/*.ts"
],
"exclude": ["node_modules"]
}