Skip to content

Commit

Permalink
feat: add randomize button and links to questions
Browse files Browse the repository at this point in the history
  • Loading branch information
okplanbo committed Sep 21, 2024
1 parent 808df37 commit c2ecb47
Show file tree
Hide file tree
Showing 10 changed files with 18,161 additions and 15,848 deletions.
4 changes: 3 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
{}
{
"plugins": ["prettier-plugin-tailwindcss"]
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"lint-staged": "^15.2.10",
"postcss": "^8.4.47",
"prettier": "3.3.2",
"prettier-plugin-tailwindcss": "^0.6.6",
"semantic-release": "^24.1.1",
"tailwindcss": "^3.4.11",
"vite": "^5.4.5"
Expand Down
62 changes: 62 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions src/App.css
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#root {
max-width: 1280px;
margin: 0 auto;
width: 100%;
padding: 1rem 0;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
}

@media screen and (min-width: 768px) {
Expand Down
78 changes: 58 additions & 20 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useEffect, useState } from "react";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import { ChakraProvider } from "@chakra-ui/react";

import { getExcludedIds } from "./db";
import { getExcludedFromDB, updateExcludedToDB } from "./db";
import data from "./static_data_ru.json";

import { Home } from "./pages/Home";
Expand All @@ -16,42 +16,80 @@ import "./App.css";
const appName = packageCfg.name;

/* TBD:
* random, next, prev buttons. set id to url
*
* links to tickets in 'excluded' list and a small image
* no such ticket page or/and general 404 page
* load q separately and save them to db with versioning to reuse later
* start, finish view, back/next/reset controls for test set of 30
* 3 mistakes and fail
* table of all q with pagination and filters
* i18n: UI, language switcher
* font consistency for i18n
* offline mode
* offline mode, pwa
*/

const getRandomId = (storedExcludedIds) => {
const activeIds = data.map((item) => {
return storedExcludedIds.includes(item.id) && item.id;
});
return Math.floor(Math.random() * activeIds.length);
};

function App() {
const [excludedIds, setExcludedIds] = useState([]);
const [excludedIds, setExcludedIds] = useState();
const [currentQuestionNumber, setCurrentQuestionNumber] = useState();

const loadExclusionStatus = async () => {
const queryParams = new URLSearchParams(location.search);
const questionParam = queryParams.get("q");
const storedExcludedIds = await getExcludedFromDB();
setExcludedIds(storedExcludedIds);
setCurrentQuestionNumber(
questionParam ? Number(questionParam) : getRandomId(storedExcludedIds),
);
};

useEffect(() => {
const loadExclusionStatus = async () => {
const storedExcludedIds = await getExcludedIds();
setExcludedIds(storedExcludedIds);
};
loadExclusionStatus();
}, []);

const handleRandomize = () => {
const queryParams = new URLSearchParams(location.search);
const questionNumber = getRandomId(excludedIds) + 1;
queryParams.set("q", questionNumber);
const newUrl = `${window.location.pathname}?${queryParams.toString()}`;
window.history.pushState({}, "", newUrl);
setCurrentQuestionNumber(questionNumber);
};

const updateExcluded = (newIds) => {
updateExcludedToDB(newIds);
setExcludedIds(newIds);
};

return (
<ChakraProvider>
<Router>
<Routes>
<Route
path={`${appName}/`}
element={<Home excludedIds={excludedIds} data={data} />}
/>
<Route path={`${appName}/excluded`} element={<ExcludedList excludedIds={excludedIds} data={data} />} />
<Route path={`${appName}/about`} element={<About />} />
</Routes>
</Router>
{currentQuestionNumber && (
<Router>
<Routes>
<Route
path={`${appName}/`}
element={
<Home
excludedIds={excludedIds}
updateExcluded={updateExcluded} // yes, still refusing to use context api :>
currentQuestionNumber={currentQuestionNumber}
handleRandomize={handleRandomize}
data={data}
/>
}
/>
<Route
path={`${appName}/excluded`}
element={<ExcludedList excludedIds={excludedIds} data={data} />}
/>
<Route path={`${appName}/about`} element={<About />} />
</Routes>
</Router>
)}
</ChakraProvider>
);
}
Expand Down
24 changes: 15 additions & 9 deletions src/components/QuestionCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,35 @@ import {
Divider,
} from "@chakra-ui/react";

import { updateExcludedIds } from "../db";

const QuestionCard = ({ number, testSize, question, excludedIds }) => {
const [isExcluded, setIsExcluded] = useState(false);
const QuestionCard = ({
number,
testSize,
question,
excludedIds,
updateExcluded,
}) => {
const [isExcluded, setIsExcluded] = useState(
excludedIds.includes(question.id),
);
const [selectedAnswer, setSelectedAnswer] = useState(null);

const { id, img, question: questionText, answers } = question;

const handleCheckboxChange = async (e) => {
const checked = e.target.checked;
setIsExcluded(checked);
const updatedExcludedIds = checked
const newExcludedIds = checked
? [...excludedIds, id]
: excludedIds.filter((excludedId) => excludedId !== id);
await updateExcludedIds(updatedExcludedIds);
updateExcluded(newExcludedIds);
};

const correctAnswerText = answers.filter((answer) => answer.correct)[0].text;
const isCorrect = selectedAnswer === correctAnswerText;

return (
<Box
className="border-x-0 border-y-2 pt-2 md:border-2 md:rounded-lg md:p-6"
className="border-x-0 border-y-2 pt-2 md:rounded-lg md:border-2 md:p-6"
overflow="hidden"
display="flex"
flexDirection="column"
Expand All @@ -48,7 +54,7 @@ const QuestionCard = ({ number, testSize, question, excludedIds }) => {
}
borderStyle="solid"
>
<Box className="flex justify-between w-full mb-5">
<Box className="mb-5 flex w-full justify-between">
<Text fontSize="sm" visibility={"hidden"}>
Question {number} of {testSize}
</Text>
Expand Down Expand Up @@ -85,7 +91,7 @@ const QuestionCard = ({ number, testSize, question, excludedIds }) => {
</Stack>
</RadioGroup>
<Divider />
<Box className="flex flex-row w-full justify-between items-center h-12">
<Box className="flex h-12 w-full flex-row items-center justify-between">
{selectedAnswer !== null ? (
<Text color={isCorrect ? "green.400" : "red.400"}>
{isCorrect ? "Correct!" : "Wrong!"}
Expand Down
56 changes: 28 additions & 28 deletions src/db.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
import { openDB } from 'idb';

const DB_NAME = 'drivingTestConfigDB';
const DB_VERSION = 1;
const STORE_NAME = 'mainStore';
const EXCLUDED_KEY = 'excludedQuestions';

export const initDB = async () => {
const db = await openDB(DB_NAME, DB_VERSION, {
upgrade(db) {
if (!db.objectStoreNames.contains(STORE_NAME)) {
db.createObjectStore(STORE_NAME);
}
},
});
return db;
};

export const getExcludedIds = async () => {
const db = await initDB();
const excluded = await db.get(STORE_NAME, EXCLUDED_KEY);
return excluded || [];
};

export const updateExcludedIds = async (ids) => {
const db = await initDB();
await db.put(STORE_NAME, ids, EXCLUDED_KEY);
};
import { openDB } from "idb";

const DB_NAME = "drivingTestConfigDB";
const DB_VERSION = 1;
const STORE_NAME = "mainStore";
const EXCLUDED_KEY = "excludedQuestions";

export const initDB = async () => {
const db = await openDB(DB_NAME, DB_VERSION, {
upgrade(db) {
if (!db.objectStoreNames.contains(STORE_NAME)) {
db.createObjectStore(STORE_NAME);
}
},
});
return db;
};

export const getExcludedFromDB = async () => {
const db = await initDB();
const excluded = await db.get(STORE_NAME, EXCLUDED_KEY);
return excluded || [];
};

export const updateExcludedToDB = async (ids) => {
const db = await initDB();
await db.put(STORE_NAME, ids, EXCLUDED_KEY);
};
47 changes: 29 additions & 18 deletions src/pages/ExcludedList.jsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
export function ExcludedList({ excludedIds, data }) {
return (
<div>
<h2 className="font-bold text-xl">Excluded Questions</h2>
{excludedIds.length > 0 ? (
<ul>
{excludedIds.map((id) => (
<li key={id}>
#{id} {data[id].question}
</li>
))}
</ul>
) : (
<p>No excluded questions yet.</p>
)}
</div>
);
}
import { Link } from "@chakra-ui/react";

export function ExcludedList({ excludedIds, data }) {
return (
<div className="flex w-full flex-col items-center px-6">
<h1 className="text-xl font-bold md:text-3xl">Excluded Questions</h1>
{excludedIds.length > 0 && (
<ul className="flex max-w-full flex-col items-start gap-2 overflow-hidden text-ellipsis pt-3 md:max-w-screen-md md:pt-6">
{excludedIds.map((id) => (
<li
key={id}
className="max-w-full overflow-hidden text-ellipsis text-nowrap md:max-w-screen-md"
>
<Link
mx={2}
fontWeight="normal"
color="teal.500"
href={`/?q=${id}`}
>
#{id} - {data[id - 1].question}
</Link>
</li>
))}
</ul>
)}
{excludedIds.length === 0 && <p>No excluded questions yet.</p>}
</div>
);
}
Loading

0 comments on commit c2ecb47

Please sign in to comment.