Skip to content

Commit

Permalink
正答と誤答の表示を改善
Browse files Browse the repository at this point in the history
  • Loading branch information
naoki1510 committed Mar 7, 2024
1 parent 8dd1363 commit 8836a53
Show file tree
Hide file tree
Showing 10 changed files with 74 additions and 41 deletions.
7 changes: 1 addition & 6 deletions api/app/controllers/questions_controller.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class QuestionsController < ApplicationController
before_action :set_question, only: %i[ show update destroy start end ]
before_action :set_show_correct, :set_show_answers, only: %i[ index show start end ]
before_action :set_show_correct, only: %i[ index show start end ]

# GET /questions
def index
Expand Down Expand Up @@ -73,9 +73,4 @@ def question_params
def set_show_correct
@is_show_correct = params[:show_correct].present?
end

# Whether to display answers
def set_show_answers
@is_show_answers = params[:show_answers].present?
end
end
4 changes: 2 additions & 2 deletions api/app/views/choices/_choice.json.jbuilder
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
json.extract! choice, :id, :title, :description, :image, :question_id
json.is_correct choice.is_correct if @is_show_correct
json.answer do
json.array! choice.answers, partial: 'answers/answer', as: :answer if @is_show_answers
json.answers do
json.array! choice.answers, partial: 'answers/answer', as: :answer
end
3 changes: 3 additions & 0 deletions client/src/api/choice.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { postHeaders } from "api/headers";
import host from "api/host";
import { Answer } from "./answer";

export type Choice = {
id?: number;
Expand All @@ -8,6 +9,7 @@ export type Choice = {
image: string;
display_order?: number;
is_correct?: boolean;
answers: Answer[];
};

export const createEmptyChoice = (questionId?: number): Choice => {
Expand All @@ -16,6 +18,7 @@ export const createEmptyChoice = (questionId?: number): Choice => {
description: "",
image: "",
is_correct: false,
answers: [],
};
};

Expand Down
34 changes: 19 additions & 15 deletions client/src/components/answers/CreateAnswer.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import {
Box,
Button,
Card,
CardBody,
HStack,
Heading,
IconButton,
Text,
VStack
VStack,
} from "@chakra-ui/react";
import { createAnswer } from "api/answer";
import { Choice } from "api/choice";
Expand All @@ -18,14 +18,17 @@ import locations from "locations";
import { memo, useCallback, useEffect, useState } from "react";
import { IoExit } from "react-icons/io5";
import { useNavigate } from "react-router-dom";
import { useRecoilState } from "recoil";
import userIdState from "recoil/userIdState";
import AnswerForm from "./AnswerForm";

let previousQuestionIds: number[] = [];

export default memo(function CreateAnswer() {
const navigate = useNavigate();
const [userId, setUserID] = useRecoilState(userIdState);
useEffect(() => {
if (!localStorage.getItem("userId") === null) {
if (userId === undefined) {
navigate(locations.createUser);
}
}, [navigate]);
Expand All @@ -37,24 +40,24 @@ export default memo(function CreateAnswer() {
);
const { questions: lastQuestions, fetchQuestions: fetchLastQuestions } =
useQuestions(new URLSearchParams({ last: "true", show_correct: "true" }));
const { user, fetchUser } = useUser(localStorage.getItem("userId") || "");
const { user, fetchUser } = useUser(userId || "");

useEffect(() => {
if (!questions) return;

previousQuestionIds.forEach((id) => {
previousQuestionIds.map(async (id) => {
if (questions.every((question) => question.id !== id)) {
Promise.all(
await Promise.all(
selectedChoices
.filter((choice) => choice.question_id === id)
.map((choice) => {
if (choice.id && localStorage.getItem("userId"))
if (choice.id && userId)
return createAnswer({
choice_id: choice.id,
user_id: Number(localStorage.getItem("userId")),
user_id: userId,
});
})
).then(() => fetchUser());
);
fetchLastQuestions();
}
});
Expand All @@ -74,7 +77,7 @@ export default memo(function CreateAnswer() {
}, [fetchQuestions, questions]);

const handleLogout = useCallback(() => {
localStorage.removeItem("userId");
setUserID(undefined);
navigate(locations.createUser);
}, [navigate]);

Expand All @@ -91,11 +94,13 @@ export default memo(function CreateAnswer() {
</Text>
</Heading>
</Box>
<IconButton
aria-label="logout"
icon={<IoExit />}
<Button
leftIcon={<IoExit />}
onClick={handleLogout}
/>
colorScheme="red"
>
ログアウト
</Button>
</HStack>
</CardBody>
</Card>
Expand All @@ -111,7 +116,6 @@ export default memo(function CreateAnswer() {
))
) : lastQuestions?.length ? (
<>
<Text>回答済みの問題</Text>
{lastQuestions.map((question) => (
<VStack key={question.id} alignItems={"stretch"}>
<QuestionCard question={question} showChoices />
Expand Down
22 changes: 17 additions & 5 deletions client/src/components/common/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,28 @@ import { useAuthorize } from "./Authorize";
type MenuData = {
label: string;
href: string;
admin?: boolean;
};

const menus: MenuData[] = [
{
label: "Play",
href: locations.createUser,
},
{
label: "Admin",
href: locations.listQuestions,
admin: false,
},
{
label: "Questions",
href: locations.listQuestions,
admin: true,
},
{
label: "Users",
href: locations.listQuestions,
admin: true,
},
];

Expand All @@ -42,11 +50,15 @@ export default memo(function Header() {
Quiz
</Heading>
<HStack divider={<Box w={"1px"} />} alignItems={"stretch"}>
{menus.map((menu) => (
<Button variant={"link"} key={menu.label} as={Link} to={menu.href}>
{menu.label}
</Button>
))}
{menus
.filter(
(menu) => menu.admin === undefined || menu.admin === authorized
)
.map((menu) => (
<Button variant={"link"} key={menu.label} as={Link} to={menu.href}>
{menu.label}
</Button>
))}
{authorized && (
<Button variant={"link"} onClick={handleLogout}>
Exit
Expand Down
23 changes: 15 additions & 8 deletions client/src/components/questions/QuestionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,19 @@ import {
Text,
} from "@chakra-ui/react";
import { Question, endQuestion, startQuestion } from "api/questions";
import { memo, useCallback } from "react";
import React, { memo, useCallback } from "react";
import {
IoCaretForward,
IoCheckmarkCircle,
IoCloseCircleOutline,
IoCheckmark,
IoChevronForward,
IoClose,
IoPlayCircle,
IoRemove,
IoStopCircle,
} from "react-icons/io5";
import { Link } from "react-router-dom";
import { useRecoilValue } from "recoil";
import userIdState from "recoil/userIdState";

export type ShowQuestionProps = {
question: Question;
Expand All @@ -34,6 +38,7 @@ export type ShowQuestionProps = {
export default memo(function QuestionCard(props: ShowQuestionProps) {
const { question, setQuestion, href, showChoices, showStartButton } = props;
const { title, image, point } = question;
const userId = useRecoilValue(userIdState);

const handleStart = useCallback(() => {
if (question.id !== undefined)
Expand Down Expand Up @@ -70,12 +75,14 @@ export default memo(function QuestionCard(props: ShowQuestionProps) {
<List>
{question.choices.map((choice) => (
<ListItem key={choice.id}>
{choice.is_correct === undefined ? (
<ListIcon as={IoCaretForward} color={"gray.500"} />
) : choice.is_correct ? (
<ListIcon as={IoCheckmarkCircle} color={"teal.500"} />
{choice.is_correct ? (
<ListIcon as={IoCheckmark} color={"teal.500"} />
) : choice.answers.some(
(answer) => answer.user_id === userId
) ? (
<ListIcon as={IoClose} color={"red.500"} />
) : (
<ListIcon as={IoCloseCircleOutline} color={"red.500"} />
<ListIcon as={IoRemove} color={"gray.500"} />
)}
{choice.description}
</ListItem>
Expand Down
9 changes: 6 additions & 3 deletions client/src/components/users/CreateUser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,21 @@ import { FormEvent, memo, useCallback, useEffect, useState } from "react";
import { createUser, useUsers } from "api/users";
import { useNavigate } from "react-router-dom";
import locations from "locations";
import { useRecoilState } from "recoil";
import userIdState from "recoil/userIdState";

export default memo(function CreateUser() {
const navigate = useNavigate();
const [name, setName] = useState("");
const { users } = useUsers();
const [userId, setUserID] = useRecoilState(userIdState);

const handleCreateUser = useCallback(
(e: FormEvent) => {
e.preventDefault();
createUser({ name })
.then((user) => {
localStorage.setItem("userId", String(user.id));
setUserID(user.id);
navigate(locations.createAnswer);
})
.catch(console.error);
Expand All @@ -33,7 +36,7 @@ export default memo(function CreateUser() {
);

useEffect(() => {
if (localStorage.getItem("userId")) {
if (userId !== undefined) {
navigate(locations.createAnswer);
}
}, [navigate]);
Expand Down Expand Up @@ -68,7 +71,7 @@ export default memo(function CreateUser() {
<Button
key={user.id}
onClick={() => {
localStorage.setItem("userId", String(user.id));
setUserID(user.id);
navigate(locations.createAnswer);
}}
colorScheme="teal"
Expand Down
1 change: 1 addition & 0 deletions client/src/recoil/localStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { AtomEffect, AtomOptions } from "recoil";

export const storageKeys = {
password: "password",
userId: "userId",
};

export const getSavedValue = <T>(key: string, defaultValue: T) => {
Expand Down
6 changes: 4 additions & 2 deletions client/src/recoil/passwordState.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { atom } from "recoil";
import { atomOptionsWithLocalStorage } from "recoil/localStorage";
import { atomOptionsWithLocalStorage, storageKeys } from "recoil/localStorage";

export default atom<string>(atomOptionsWithLocalStorage("password", ""));
export default atom<string>(
atomOptionsWithLocalStorage(storageKeys.password, "")
);
6 changes: 6 additions & 0 deletions client/src/recoil/userIdState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { atom } from "recoil";
import { atomOptionsWithLocalStorage, storageKeys } from "./localStorage";

export default atom(
atomOptionsWithLocalStorage<number | undefined>(storageKeys.userId, undefined)
);

0 comments on commit 8836a53

Please sign in to comment.