Redesign WIP

This commit is contained in:
Jakob Kordež 2023-07-11 15:56:27 +02:00
parent 10c15949c5
commit 601b2b944e
14 changed files with 1871 additions and 1513 deletions

View File

@ -1,13 +1,9 @@
const withPwa = require('next-pwa')({
dest: 'public',
sw: 'service-worker.js',
});
// const withPwa = require('next-pwa')({
// dest: 'public',
// sw: 'service-worker.js',
// });
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
appDir: true,
},
}
const nextConfig = {}
module.exports = withPwa(nextConfig);
module.exports = nextConfig;

View File

@ -10,26 +10,29 @@
"format": "prettier --write ./src"
},
"dependencies": {
"@types/node": "18.14.0",
"@types/react": "18.0.28",
"@types/react-dom": "18.0.11",
"@types/node": "^20.4.1",
"@types/react": "^18.2.14",
"@types/react-dom": "^18.2.6",
"@vercel/analytics": "^0.1.11",
"bulma": "^0.9.4",
"eslint": "8.34.0",
"eslint-config-next": "^13.3.0",
"next": "^13.4.2",
"eslint": "^8.44.0",
"eslint-config-next": "^13.4.9",
"next": "^13.4.9",
"next-pwa": "^5.6.0",
"node-sass": "^8.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-katex": "^3.0.1",
"typescript": "4.9.5",
"typescript": "^5.1.6",
"zustand": "^4.3.3"
},
"devDependencies": {
"@types/react-katex": "^3.0.0",
"eslint-config-prettier": "^8.8.0",
"prettier": "^2.8.4",
"autoprefixer": "^10.4.14",
"postcss": "^8.4.25",
"prettier-plugin-tailwindcss": "^0.3.0",
"tailwindcss": "^3.3.2",
"sass": "^1.58.3"
}
}

6
postcss.config.js Normal file
View File

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

View File

@ -1,3 +1,4 @@
module.exports = {
plugins: [require('prettier-plugin-tailwindcss')],
singleQuote: true,
}

View File

@ -69,7 +69,7 @@ export default function IzpitQuiz() {
<>
{state === QuizState.Ready && (
<div className="is-flex">
<button className="button is-primary mx-auto" onClick={load}>
<button className="button is-primary" onClick={load}>
Začni
</button>
</div>
@ -121,7 +121,7 @@ export default function IzpitQuiz() {
</button>
</div>
<h1 className="is-size-3 my-3 has-text-centered">Napačni odgovori</h1>
<h1 className="is-size-3 has-text-centered my-3">Napačni odgovori</h1>
{questions?.map(
(question, qi) =>

View File

@ -45,7 +45,7 @@ export default function RootLayout({
<body>
<Header />
<main className="container">{children}</main>
<main>{children}</main>
<AnalyticsWrapper />
</body>

View File

@ -2,7 +2,8 @@ import Link from 'next/link';
const povezave = [
{
label: 'Priročnik za radioamaterje (3. izdaja)',
label:
'Priročnik za radioamaterje (3. izdaja - vsebuje napake in mankajo vsebine)',
href: 'http://www.homemade.net/ra/prirocnik_novi.pdf',
},
{
@ -29,28 +30,72 @@ const povezave = [
export default function Home() {
return (
<div className="section content">
<h1>Pozdravljen!</h1>
<p>
Na podstrani <Link href="/priprave">Priprave</Link> so vaje za pripravo
na izpit. Vprašanja so razdeljena po kategorijah, pravilni odgovori pa
se razkrivajo sproti.
</p>
<>
<div className="w-full bg-dark text-darker">
<div className="container flex flex-col gap-6 py-6">
<div className="flex flex-col gap-6 text-xl font-bold md:flex-row">
<Link
href="#kako-zaceti"
className="grow rounded-2xl bg-white/90 p-4 text-center shadow"
>
Seznam vprašanj
</Link>
<Link
href="/priprave"
className="grow rounded-2xl bg-white/90 p-4 text-center shadow"
>
Vadi vprašanja
</Link>
<Link
href="/izpit-sim"
className="grow rounded-2xl bg-white/90 p-4 text-center shadow"
>
Reši preizkusni izpit
</Link>
</div>
</div>
</div>
<p>
Na podstrani <Link href="/izpit-sim">Simulator izpita</Link> lahko
preizkusiš svoje znanje. Vprašanja so generirana kot bi bila na
resničnem izpitu. Pravilni odgovori se razkrijejo šele po koncu.
</p>
<div className="container flex flex-col gap-10 py-10">
<div>
<h3 className="mb-3 text-2xl font-bold">Kaj je radioamaterstvo?</h3>
</div>
<h2>Uporabne povezave</h2>
<ul className="list-inside list-disc">
{povezave.map(({ label, href }) => (
<li key={label}>
<Link href={href}>{label}</Link>
</li>
))}
</ul>
</div>
<div>
<h3 className="mb-3 text-2xl font-bold">
Katera kategorija je prava zame?
</h3>
</div>
<div>
<h3 className="mb-3 text-2xl font-bold">Kaj povzema izpit?</h3>
</div>
<div>
<h3 className="mb-3 text-2xl font-bold">Kako začeti?</h3>
</div>
<div>
<h3 className="mb-3 text-2xl font-bold">
Vsebine za pripravo na izpit
</h3>
</div>
<div>
<h3 className="mb-3 text-2xl font-bold">Vaja pred izpitom</h3>
</div>
<div>
<h3 className="mb-3 text-2xl font-bold">Uporabne povezave</h3>
<ul className="list-inside list-disc">
{povezave.map(({ label, href }) => (
<li key={label}>
<Link href={href}>{label}</Link>
</li>
))}
</ul>
</div>
</div>
</>
);
}

View File

@ -6,13 +6,5 @@ export const metadata: Metadata = {
};
export default function Priprave() {
return (
<div className="section">
<div className="content">
<h1>Priprave na izpit</h1>
</div>
<VajaQuiz />
</div>
);
return <VajaQuiz />;
}

View File

@ -83,40 +83,48 @@ export default function VajaQuiz() {
}, [questions.length, selectedCategory]);
return (
<>
<div className="field has-addons mb-5">
<div className="control is-expanded">
<div className="select is-fullwidth">
<select
name="category"
value={selectedCategory}
onChange={(e) => {
const selectedCategory = e.target.value;
useStore.setState({ selectedCategory });
load(selectedCategory);
}}
>
<option value="all">Vse kategorije</option>
{categories.map((category, i) => (
<option key={i} value={category.id}>
{category.title}
</option>
))}
</select>
</div>
</div>
<div className="mb-10 flex flex-col gap-10">
<div className="bg-dark text-white">
<div className="container flex flex-col gap-6 py-8">
<h1 className="text-3xl font-bold">Priprava na izpit</h1>
<div className="control">
<button
className={`button is-primary ${isLoading ? 'is-loading' : ''}`}
onClick={() => load(selectedCategory)}
>
Naloži
</button>
<div>
<label htmlFor="category" className="mb-2 block font-medium">
Izberi kategorijo
</label>
<div className="flex flex-row gap-3">
<select
id="category"
name="category"
className="w-full flex-1 rounded-lg border border-gray-400 bg-white p-2.5 text-darker placeholder-gray-400 focus:border-blue-500 focus:ring-blue-500"
value={selectedCategory}
onChange={(e) => {
const selectedCategory = e.target.value;
useStore.setState({ selectedCategory });
load(selectedCategory);
}}
>
<option value="all">Vse kategorije</option>
{categories.map((category, i) => (
<option key={i} value={category.id}>
{category.title}
</option>
))}
</select>
<button
className="rounded-lg bg-primary px-5 py-2.5 font-medium text-white disabled:opacity-70"
disabled={isLoading}
onClick={!isLoading ? () => load(selectedCategory) : undefined}
>
Naloži
</button>
</div>
</div>
</div>
</div>
<div>
<div className="mx-auto flex max-w-xl flex-col gap-12">
{questions.slice(0, displayed).map((question, qi) => (
<QuestionCard
key={qi}
@ -136,17 +144,25 @@ export default function VajaQuiz() {
))}
</div>
<div className="buttons mt-5 is-justify-content-end">
{questions.length > displayed && (
<button className="button is-primary is-rounded" onClick={loadMore}>
Naloži več
<div className="mx-auto w-full max-w-xl">
<div className="flex flex-row justify-end gap-3">
{questions.length > displayed && (
<button
className="rounded-lg bg-primary px-5 py-2.5 font-medium text-white"
onClick={loadMore}
>
Naloži več
</button>
)}
<button
className="rounded-lg bg-primary px-5 py-2.5 font-medium text-white"
onClick={scrollToTop}
>
Na vrh
</button>
)}
<button className="button is-primary is-rounded" onClick={scrollToTop}>
Na vrh
</button>
</div>
</div>
</>
</div>
);
}

View File

@ -18,74 +18,12 @@ export default function Header() {
const pathname = usePathname();
return (
<section className="hero is-primary">
<div className="hero-body">
<div className="container">
<div className="is-flex is-align-items-center">
<figure className="image is-96x96">
<Image
src="/logo/logo_192.png"
alt="Logo"
height={96}
width={96}
style={{ height: '100%', width: 'auto', margin: 'auto' }}
/>
</figure>
<div className="ml-4">
<h1 className="title is-size-3">Radioamaterski izpit</h1>
<h6 className="subtitle">Pripravil: Jakob [S52KJ]</h6>
</div>
<a
role="button"
className={`navbar-burger ${navbar ? 'is-active' : ''}`}
aria-label="menu"
onClick={() => setNavbar(!navbar)}
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
</div>
</div>
<div className="hero-foot">
<div className="container">
<nav className="navbar">
<div className={`navbar-menu ${navbar ? 'is-active' : ''}`}>
<div className="navbar-start">
{nav.map(({ href, label }) => (
<Link
key={href}
className={`navbar-item ${
href == pathname ? 'is-active' : ''
}`}
href={href}
onClick={() => setNavbar(false)}
>
{label}
</Link>
))}
</div>
<div className="navbar-end">
<Link
className="navbar-item is-flex"
href="http://www.hamradio.si/"
>
<span className="icon mr-2">
<Image
src="/logo/zrs_logo_white.svg"
alt="ZRS Logo"
height={32}
width={32}
/>
</span>
<span>ZRS</span>
</Link>
</div>
</div>
</nav>
</div>
<section className="bg-darker py-6 font-bold text-white">
<div className="container flex flex-row items-center justify-start gap-8">
<Image src="/logo/logo_192.png" alt="Logo" height={32} width={32} />
<Link href="/">
<h1 className="text-4xl">Radioamaterski izpit</h1>
</Link>
</div>
</section>
);

View File

@ -17,26 +17,25 @@ export default function QuestionCard({
onClick,
}: QuestionCardProps) {
return (
<div className="box">
<p>{question.id}</p>
<div className="title is-4">
<h3>{question.question}</h3>
<div className="flex flex-col gap-5">
<div className="text-xl text-gray-700">
<span className="font-bold text-primary">
A{question.id.toString().padStart(3, '0')}:{' '}
</span>
{question.question}
</div>
{question.image && (
<figure className="image">
<Image
className={styles.image}
src={`/question_images/${question.image}`}
alt={question.image}
height={500}
width={500}
/>
</figure>
<Image
className={styles.image}
src={`/question_images/${question.image}`}
alt={question.image}
height={500}
width={500}
/>
)}
<div className="buttons mt-5">
<div className="flex flex-col gap-2">
{question.answers.map((answer, i) => (
<Answer
key={i}
@ -70,27 +69,37 @@ function Answer({
isSelected,
onClick,
}: AnswerProps) {
let className = `button is-fullwidth ${styles.answer}`;
if (!onClick) className += ' is-static';
if (isSelected) {
if (reveal) {
if (isCorrect) className += ' is-success is-light';
else className += ' is-danger is-light';
} else {
className += ' is-info';
}
}
return (
<button className={className} onClick={onClick}>
{String.fromCharCode(65 + index) + '. '}
{answer.startsWith('$') ? (
<span className="ml-2">
<InlineMath math={answer.slice(1, answer.length - 1)} />
</span>
) : (
answer
)}
<button
className={`flex w-full flex-row items-center gap-5 rounded border px-6 py-2 ${
!isSelected
? 'border-gray-300'
: !reveal
? 'border-sky-500 bg-sky-100'
: isCorrect
? 'border-green-500 bg-green-100'
: 'border-red-600 bg-red-100'
}`}
disabled={!onClick}
onClick={onClick}
>
{!reveal && <input type="radio" checked={isSelected} readOnly />}
<div
className={`border-r py-2 pr-5 text-sm font-bold ${
!isSelected ? 'border-gray-200' : 'border-inherit'
}`}
>
{String.fromCharCode(65 + index)}
</div>
<div className="text-left text-lg text-gray-600">
{answer.startsWith('$') ? (
<span className="ml-2">
<InlineMath math={answer.slice(1, answer.length - 1)} />
</span>
) : (
answer
)}
</div>
</button>
);
}

View File

@ -3,19 +3,14 @@ $container-max-width: 1200px;
$section-padding: 2rem 1.5rem;
$body-font-size: 1.1em;
@import 'bulma/sass/utilities/_all.sass';
@import 'bulma/sass/base/_all.sass';
@import 'bulma/sass/elements/_all.sass';
@import 'bulma/sass/form/_all.sass';
@import 'bulma/sass/components/_all.sass';
@import 'bulma/sass/layout/_all.sass';
@import 'bulma/sass/grid/_all.sass';
@import 'bulma/sass/helpers/_all.sass';
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
@import 'katex/dist/katex.min.css';
.navbar {
@include until(1024px) {
min-height: 0;
}
}
// .navbar {
// @include until(1024px) {
// min-height: 0;
// }
// }

26
tailwind.config.js Normal file
View File

@ -0,0 +1,26 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
colors: {
darker: "#222831",
dark: "#393E46",
primary: "#00ADB5",
light: "#EEEEEE",
}
},
container: {
center: true,
screens: {
sm: "640px",
md: "768px",
lg: "1024px",
}
}
},
plugins: [],
}

2943
yarn.lock

File diff suppressed because it is too large Load Diff