Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6,634 changes: 3,722 additions & 2,912 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

57 changes: 57 additions & 0 deletions src/components/questions/bulk-status-update-dialog.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { useSelector } from "react-redux";
import { store } from "@/store";
import { useBulkUpdateQuestionsMutation } from "@/store/api";
import { toggleBulkQuestionStatusUpdateDialog } from "@/store/reducers/ui/question";
import {
AlertDialog,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
Button,
toast
} from "@sliit-foss/bashaway-ui/components";

const close = () => store.dispatch(toggleBulkQuestionStatusUpdateDialog({ open: false }));

const BulkQuestionStatusUpdateDialog = ({ refresh }) => {
const { open, enabled } = useSelector((store) => store.ui.question.bulkStatusDialog);

const [bulkUpdateQuestions, { isLoading }] = useBulkUpdateQuestionsMutation();

const handleConfirm = async () => {
await bulkUpdateQuestions({ enabled })
.unwrap()
.then((res) => {
toast({ title: res.message });
close();
refresh();
});
};

const action = enabled ? "enable" : "disable";

return (
<AlertDialog open={open} onOpenChange={(open) => !open && close()}>
<AlertDialogContent overlayClassName="z-[201]" className="z-[201]">
<AlertDialogHeader>
<AlertDialogTitle>Confirm Bulk {enabled ? "Enable" : "Disable"}</AlertDialogTitle>
<AlertDialogDescription>
Are you sure you that want to {action} ALL questions? This will affect all questions in the system.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter className="mt-4">
<Button onClick={handleConfirm} loading={isLoading}>
Yes, {action} all
</Button>
<Button variant="secondary" onClick={close}>
Cancel
</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
};

export default BulkQuestionStatusUpdateDialog;
1 change: 1 addition & 0 deletions src/components/questions/index.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./question";
export * from "./question-details";
export { default as QuestionDialog } from "./dialog";
export { default as BulkQuestionStatusUpdateDialog } from "./bulk-status-update-dialog";
6 changes: 4 additions & 2 deletions src/components/questions/question/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@ export const Question = ({ question }) => {
cardStyles
)}
>
<Body3 className="font-bold transition-all duration-medium">{question.name}</Body3>
<Body3 className={twMerge("font-bold transition-all duration-medium", !question.enabled && "line-through")}>
{question.name}
</Body3>
<ReactMarkdown className="markdown [&>p]:font-semibold line-clamp-3">{cleanedDescription}</ReactMarkdown>
<div className="flex flex-wrap gap-3 [&>span]:px-3 [&>span]:py-2 [&>span]:rounded-lg [&>span]:transition-all [&>span]:duration-medium">
<Footnote>{startCase(question.difficulty.toLowerCase())}</Footnote>
<Footnote>{question.max_score}PT</Footnote>
{question.constraints?.length && <Footnote>{question.constraints?.join(", ")}</Footnote>}
{question.constraints?.length > 0 && <Footnote>{question.constraints?.join(", ")}</Footnote>}
</div>
</div>
</Link>
Expand Down
2 changes: 1 addition & 1 deletion src/components/submissions/grade-dialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const GradeDialog = ({ refresh }) => {
})
.unwrap()
.then(() => {
toast({ title: `Question updated successfully` });
toast({ title: `Submission graded successfully` });
close();
refresh();
});
Expand Down
4 changes: 4 additions & 0 deletions src/components/users/dialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ const UserDialog = ({ refresh }) => {

const handleSubmit = async (e) => {
e.preventDefault();
if (!role) {
toast({ variant: "destructive", title: "Please select a role" });
return;
}
await addUser({
name: e.target.name.value,
email: e.target.email.value,
Expand Down
19 changes: 19 additions & 0 deletions src/filters/question.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,29 @@ export const questionFilters = [
label: "Extreme"
}
]
},
{
key: "enabled",
label: "Status",
options: [
{
key: "true",
label: "Enabled"
},
{
key: "false",
label: "Disabled"
}
]
}
];

export const questionSorts = [
{
key: "name",
label: "Sort by name",
direction: 0
},
{
key: "max_score",
label: "Sort by points",
Expand Down
21 changes: 20 additions & 1 deletion src/filters/submission.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,20 @@ export const submissionFilters = [
label: "Not Graded"
}
]
},
{
key: "automatically_graded",
label: "Grading Type",
options: [
{
key: "true",
label: "Auto Graded"
},
{
key: "false",
label: "Manually Graded"
}
]
}
];

Expand All @@ -29,6 +43,11 @@ export const submissionSorts = [
{
key: "score",
label: "Sort by score",
direction: 0
direction: -1
},
{
key: "updated_at",
label: "Sort by last updated",
direction: -1
}
];
2 changes: 1 addition & 1 deletion src/hooks/auth.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const useAuth = () => {
navigate("/");
}
setCompleted(true);
}, [location]);
}, [location, navigate]);

return completed;
};
Expand Down
8 changes: 6 additions & 2 deletions src/pages/question-details.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,16 @@ export default function QuestionDetails() {
submitted
<CheckCircle2 size={16} />
</Badge>
<Body3 className="font-bold transition-all duration-medium">{question?.name}</Body3>
<Body3
className={twMerge("font-bold transition-all duration-medium", !question.enabled && "line-through")}
>
{question?.name}
</Body3>
<ReactMarkdown className="markdown [&>p]:font-semibold">{question?.description}</ReactMarkdown>
<div className="flex flex-wrap gap-3 [&>span]:px-3 [&>span]:py-2 [&>span]:rounded-lg [&>span]:transition-all [&>span]:duration-medium">
<Footnote>{startCase(question?.difficulty?.toLowerCase())}</Footnote>
<Footnote>{question?.max_score}PT</Footnote>
{question?.constraints?.length && <Footnote>{question?.constraints?.join(", ")}</Footnote>}
{question?.constraints?.length > 0 && <Footnote>{question?.constraints?.join(", ")}</Footnote>}
</div>
<ActionButtons question={question} />
</div>
Expand Down
46 changes: 36 additions & 10 deletions src/pages/questions.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { useState } from "react";
import { Plus } from "lucide-react";
import { Question, QuestionDialog, QuestionGridSkeleton } from "@/components/questions";
import { Ban, Plus, Power } from "lucide-react";
import { BulkQuestionStatusUpdateDialog, Question, QuestionDialog, QuestionGridSkeleton } from "@/components/questions";
import { questionFilters, questionSorts } from "@/filters";
import { useTitle } from "@/hooks";
import { store } from "@/store";
import { useAuthUserQuery, useGetQuestionsQuery, useLazyGetQuestionsQuery } from "@/store/api";
import { setSelectedQuestion, toggleAddQuestionDialog } from "@/store/reducers/ui/question";
import {
setSelectedQuestion,
toggleAddQuestionDialog,
toggleBulkQuestionStatusUpdateDialog
} from "@/store/reducers/ui/question";
import { AnimatedSwitcher, Button, Filters, NoRecords, Pagination, Sorts } from "@sliit-foss/bashaway-ui/components";
import { computeFilterQuery, computeSortQuery } from "@sliit-foss/bashaway-ui/utils";

Expand All @@ -29,20 +33,41 @@ const Questions = () => {

const refresh = () => trigger({ filters, sorts, page });

const openBulkStatusDialog = (enabled) => {
store.dispatch(toggleBulkQuestionStatusUpdateDialog({ open: true, enabled }));
};

useTitle("Challenges | Bashaway");

return (
<>
<div className="w-full flex flex-col justify-center items-center gap-6 mb-8 mt-4">
<div className="w-full flex flex-col md:flex-row gap-6">
{authUser?.role === "ADMIN" && (
<Button
className="w-full md:w-5/12 lg:w-4/12 xl:w-3/12 2xl:w-2/12 py-[13px] sm:py-4 md:py-1.5"
onClick={onAddClick}
>
<Plus strokeWidth="2.5" />
Add Question
</Button>
<div className="flex gap-3 shrink-0 justify-end">
<Button className="w-full py-[13px] sm:py-4 md:py-1.5 whitespace-nowrap" onClick={onAddClick}>
<Plus strokeWidth="2.5" />
Add Question
</Button>
<div className="rounded-full border-gray-100 border bg-black/10 p-2 flex gap-2">
<Button
className="py-[13px] sm:py-4 md:py-1.5 whitespace-nowrap"
variant="secondary"
onClick={() => openBulkStatusDialog(false)}
>
<Ban size={18} strokeWidth="2.5" />
<span className="hidden xl:block">Disable All</span>
</Button>
<Button
className="py-[13px] sm:py-4 md:py-1.5 whitespace-nowrap"
variant="secondary"
onClick={() => openBulkStatusDialog(true)}
>
<Power size={18} strokeWidth="2.5" />
<span className="hidden xl:block">Enable All</span>
</Button>
</div>
</div>
)}
<Filters filters={questionFilters} setFilterQuery={setFilters} />
</div>
Expand Down Expand Up @@ -73,6 +98,7 @@ const Questions = () => {
</div>
</div>
<QuestionDialog refresh={refresh} />
<BulkQuestionStatusUpdateDialog refresh={refresh} />
</>
);
};
Expand Down
6 changes: 5 additions & 1 deletion src/store/api/question.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ export const questionApi = createApi({
}),
updateQuestion: builder.mutation({
query: ({ id, data }) => patch(`/api/questions/${id}`, data)
}),
bulkUpdateQuestions: builder.mutation({
query: (data) => patch(`/api/questions`, data)
})
})
});
Expand All @@ -39,5 +42,6 @@ export const {
useGetQuestionByIdQuery,
useLazyGetQuestionByIdQuery,
useAddQuestionMutation,
useUpdateQuestionMutation
useUpdateQuestionMutation,
useBulkUpdateQuestionsMutation
} = questionApi;
11 changes: 9 additions & 2 deletions src/store/reducers/ui/question.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import { createSlice } from "@reduxjs/toolkit";

const initialState = {
selected: null,
showAddQuestionDialog: false
showAddQuestionDialog: false,
bulkStatusDialog: {
open: false,
enabled: false
}
};

export const slice = createSlice({
Expand All @@ -14,10 +18,13 @@ export const slice = createSlice({
},
toggleAddQuestionDialog(state, action) {
state.showAddQuestionDialog = action.payload;
},
toggleBulkQuestionStatusUpdateDialog(state, action) {
state.bulkStatusDialog = action.payload;
}
}
});

export const { setSelectedQuestion, toggleAddQuestionDialog } = slice.actions;
export const { setSelectedQuestion, toggleAddQuestionDialog, toggleBulkQuestionStatusUpdateDialog } = slice.actions;

export default slice.reducer;