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 8836a53 commit d47a798
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 62 deletions.
17 changes: 15 additions & 2 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, only: %i[ index show start end ]
before_action :set_question, only: %i[ show update destroy start end reset ]
before_action :set_show_correct, only: %i[ index show start end reset ]

# GET /questions
def index
Expand Down Expand Up @@ -49,6 +49,7 @@ def destroy
end

def start
reset_question
@question.update!(started_at: Time.current, ended_at: Time.current + 30.seconds)
render :show
end
Expand All @@ -58,6 +59,11 @@ def end
render :show
end

def reset
reset_question
render :show
end

private
# Use callbacks to share common setup or constraints between actions.
def set_question
Expand All @@ -73,4 +79,11 @@ def question_params
def set_show_correct
@is_show_correct = params[:show_correct].present?
end

def reset_question
@question.update!(started_at: nil, ended_at: nil)
@question.choices.each do |choice|
choice.answers.destroy_all
end
end
end
8 changes: 6 additions & 2 deletions api/app/models/question.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ class Question < ApplicationRecord

scope :ordered, -> { order(display_order: :asc) }
scope :active, -> { where("started_at <= ? AND ? < ended_at", Time.current, Time.current) }
scope :finished, -> { where("ended_at <= ?", Time.current) }
scope :last_finished, -> { finished.order(ended_at: :desc).limit(1) }
scope :finished, -> { where("ended_at <= ?", Time.current).order(ended_at: :desc)}
scope :last_finished, -> { finished.limit(1) }

def correct_answers
answers.where(choice_id: correct_choices.ids)
end

def finished?
ended_at.present? && ended_at <= Time.current
end

end
1 change: 1 addition & 0 deletions api/app/views/questions/_question.json.jbuilder
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
json.extract! question, :id, :title, :image, :question_type, :point
json.is_finished question.finished?
json.choices do
json.array! question.choices.ordered, partial: "choices/choice", as: :choice
end
Expand Down
1 change: 1 addition & 0 deletions api/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@

get "questions/:id/start" => "questions#start"
get "questions/:id/end" => "questions#end"
get "questions/:id/reset" => "questions#reset"
end
14 changes: 14 additions & 0 deletions client/src/api/questions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type Question = {
point: number;
choices: Choice[];
until_end?: number;
is_finished: boolean;
};

export const createEmptyQuestion = (): Question => {
Expand All @@ -21,6 +22,7 @@ export const createEmptyQuestion = (): Question => {
question_type: "single",
point: 1,
choices: [createEmptyChoice()],
is_finished: false,
};
};

Expand Down Expand Up @@ -110,6 +112,18 @@ export const endQuestion = (
}).then((res) => res.json() as Promise<Question>);
};

export const resetQuestion = (
id: string | number,
params?: URLSearchParams,
controller?: AbortController
) => {
return fetch(`${host}/questions/${id}/reset?${params}`, {
method: "GET",
headers: getHeaders,
signal: controller?.signal,
}).then((res) => res.json() as Promise<Question>);
};

export const useQuestions = (params?: URLSearchParams) => {
const [questions, setQuestions] = useState<Question[]>();
const [loading, setLoading] = useState<boolean>(false);
Expand Down
42 changes: 20 additions & 22 deletions client/src/components/answers/CreateAnswer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export default memo(function CreateAnswer() {
);
const { questions: lastQuestions, fetchQuestions: fetchLastQuestions } =
useQuestions(new URLSearchParams({ last: "true", show_correct: "true" }));
const { user, fetchUser } = useUser(userId || "");
const { user } = useUser(userId || "");

useEffect(() => {
if (!questions) return;
Expand Down Expand Up @@ -104,27 +104,25 @@ export default memo(function CreateAnswer() {
</HStack>
</CardBody>
</Card>
{questions ? (
questions.length ? (
questions.map((question) => (
<AnswerForm
question={question}
key={question.id}
selectedChoices={selectedChoices}
setSelectedChoices={setSelectedChoices}
/>
))
) : lastQuestions?.length ? (
<>
{lastQuestions.map((question) => (
<VStack key={question.id} alignItems={"stretch"}>
<QuestionCard question={question} showChoices />
</VStack>
))}
</>
) : (
<Text>現在回答を受け付けている問題はありません。</Text>
)
{questions?.length ? (
questions.map((question) => (
<AnswerForm
question={question}
key={question.id}
selectedChoices={selectedChoices}
setSelectedChoices={setSelectedChoices}
/>
))
) : lastQuestions?.length ? (
<>
{lastQuestions.map((question) => (
<VStack key={question.id} alignItems={"stretch"}>
<QuestionCard question={question} showChoices />
</VStack>
))}
</>
) : questions && lastQuestions ? (
<Text>クイズが開始されるまでお待ちください。</Text>
) : (
<Loading />
)}
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/questions/ListQuestions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ export default memo(function ListQuestions() {
href={locations.showQuestion.replace(":id", String(question.id))}
key={question.id}
showChoices
showStartButton
showActions
/>
))
) : (
<Text>問題は存在しません</Text>
<Text>問題がありません</Text>
)
) : (
<Loading />
Expand Down
69 changes: 60 additions & 9 deletions client/src/components/questions/QuestionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,41 @@ import {
ListItem,
Text,
} from "@chakra-ui/react";
import { Question, endQuestion, startQuestion } from "api/questions";
import React, { memo, useCallback } from "react";
import {
IoCaretForward,
Question,
deleteQuestion,
endQuestion,
resetQuestion,
startQuestion,
} from "api/questions";
import locations from "locations";
import { memo, useCallback } from "react";
import {
IoCheckmark,
IoChevronForward,
IoClose,
IoPencil,
IoPlayCircle,
IoRemove,
IoStopCircle,
IoTrash,
} from "react-icons/io5";
import { Link } from "react-router-dom";
import { Link, useNavigate } from "react-router-dom";
import { useRecoilValue } from "recoil";
import userIdState from "recoil/userIdState";

export type ShowQuestionProps = {
question: Question;
setQuestion?: (question: Question) => void;
showChoices?: boolean;
showStartButton?: boolean;
showActions?: boolean;
href?: string;
};

export default memo(function QuestionCard(props: ShowQuestionProps) {
const { question, setQuestion, href, showChoices, showStartButton } = props;
const { question, setQuestion, href, showChoices, showActions } = props;
const { title, image, point } = question;
const userId = useRecoilValue(userIdState);
const navigate = useNavigate();

const handleStart = useCallback(() => {
if (question.id !== undefined)
Expand All @@ -56,6 +64,21 @@ export default memo(function QuestionCard(props: ShowQuestionProps) {
).then(setQuestion);
}, [question.id]);

const handleDelete = useCallback(() => {
if (question.id !== undefined)
deleteQuestion(question.id).then(() => {
navigate(locations.listQuestions);
});
}, [question.id]);

const handleReset = useCallback(() => {
if (question.id !== undefined)
resetQuestion(
question.id,
new URLSearchParams({ show_correct: "true" })
).then(setQuestion);
}, [question.id]);

return (
<Card>
<CardBody
Expand Down Expand Up @@ -90,8 +113,8 @@ export default memo(function QuestionCard(props: ShowQuestionProps) {
</List>
)}
</CardBody>
{showStartButton && (
<CardFooter gap={4} alignItems={"center"}>
{showActions && (
<CardFooter gap={2} alignItems={"center"}>
{question.until_end ? (
<>
<Button
Expand All @@ -112,8 +135,36 @@ export default memo(function QuestionCard(props: ShowQuestionProps) {
>
開始
</Button>
{question.is_finished && (
<Button
colorScheme="red"
onClick={handleReset}
leftIcon={<IoClose />}
variant={"outline"}
>
リセット
</Button>
)}
</>
)}
<Button
as={Link}
to={locations.updateQuestion.replace(":id", String(question.id))}
leftIcon={<IoPencil />}
variant={"outline"}
colorScheme="blue"
ml={"auto"}
>
編集
</Button>
<Button
onClick={handleDelete}
colorScheme="red"
leftIcon={<IoTrash />}
variant={"outline"}
>
削除
</Button>
</CardFooter>
)}
</Card>
Expand Down
31 changes: 6 additions & 25 deletions client/src/components/questions/ShowQuestion.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { deleteQuestion, useQuestion } from "api/questions";
import { Button, HStack, VStack } from "@chakra-ui/react";
import { useQuestion } from "api/questions";
import Loading from "components/common/Loading";
import locations from "locations";
import { memo, useCallback } from "react";
import { memo } from "react";
import { IoChevronBack } from "react-icons/io5";
import { Link, useNavigate, useParams } from "react-router-dom";
import QuestionCard from "./QuestionCard";
import { Button, HStack, List, VStack } from "@chakra-ui/react";
import { IoChevronBack, IoPencil, IoTrash } from "react-icons/io5";

export default memo(function ShowQuestion() {
const navigate = useNavigate();
Expand All @@ -16,24 +16,18 @@ export default memo(function ShowQuestion() {
}

const { question, setQuestion } = useQuestion(
Number(id),
id,
new URLSearchParams({ show_correct: "true" })
);

const handleDelete = useCallback(() => {
deleteQuestion(id).then(() => {
navigate(locations.listQuestions);
});
}, []);

return question ? (
<VStack alignItems={"stretch"} gap={4}>
<QuestionCard
question={question}
setQuestion={setQuestion}
href={locations.updateQuestion.replace(":id", id)}
showChoices
showStartButton
showActions
/>
<HStack>
<Button
Expand All @@ -44,20 +38,7 @@ export default memo(function ShowQuestion() {
>
戻る
</Button>
<Button
as={Link}
to={locations.updateQuestion.replace(":id", String(question.id))}
leftIcon={<IoPencil />}
colorScheme="blue"
ml={"auto"}
>
編集
</Button>
<Button onClick={handleDelete} colorScheme="red" leftIcon={<IoTrash />}>
削除
</Button>
</HStack>
<List>{}</List>
</VStack>
) : (
<Loading />
Expand Down

0 comments on commit d47a798

Please sign in to comment.