mirror of
https://github.com/skidoodle/erettsegi-browser.git
synced 2025-02-15 05:39:15 +01:00
minor changes pt.2
This commit is contained in:
parent
6275b083db
commit
76f75a2f60
17 changed files with 416 additions and 276 deletions
35
.eslintrc.cjs
Normal file
35
.eslintrc.cjs
Normal 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
|
22
package.json
22
package.json
|
@ -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
407
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
8
postcss.config.cjs
Normal file
8
postcss.config.cjs
Normal file
|
@ -0,0 +1,8 @@
|
|||
const config = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = config
|
|
@ -1,6 +0,0 @@
|
|||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
6
prettier.config.js
Normal file
6
prettier.config.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
/** @type {import('prettier').Config & import('prettier-plugin-tailwindcss').PluginOptions} */
|
||||
const config = {
|
||||
plugins: ['prettier-plugin-tailwindcss'],
|
||||
}
|
||||
|
||||
export default config
|
|
@ -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} />
|
||||
)
|
||||
|
|
|
@ -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'>
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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
29
src/pages/api/validate.ts
Normal 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:' })
|
||||
}
|
||||
}
|
|
@ -39,7 +39,7 @@ export default function Home() {
|
|||
|
||||
useEffect(() => {
|
||||
if (selectedLevel && selectedPeriod && selectedSubject && selectedYear) {
|
||||
fetchData(
|
||||
void fetchData(
|
||||
selectedSubject,
|
||||
selectedYear,
|
||||
selectedPeriod,
|
||||
|
|
|
@ -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.')
|
||||
|
|
|
@ -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
8
src/utils/types.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
export type ButtonColor =
|
||||
| 'primary'
|
||||
| 'danger'
|
||||
| 'default'
|
||||
| 'secondary'
|
||||
| 'success'
|
||||
| 'warning'
|
||||
| undefined
|
|
@ -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: {
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue