diff --git a/IguideME.Web/Controllers/Apps/TileController.cs b/IguideME.Web/Controllers/Apps/TileController.cs index f60245ff..35db0992 100644 --- a/IguideME.Web/Controllers/Apps/TileController.cs +++ b/IguideME.Web/Controllers/Apps/TileController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using IguideME.Web.Models; using IguideME.Web.Models.App; @@ -715,6 +716,27 @@ public ActionResult GetExternalAssignments() return Ok(_databaseManager.GetExternalAssignments(GetCourseID())); } + [Authorize(Policy = "IsInstructor")] + [HttpPatch] + [Route("/external-assignments/{assignmentID}")] + public ActionResult PatchAssignment([FromBody] AppAssignment assignment) + { + assignment.CourseID = this.GetCourseID(); + _databaseManager.UpdateExternalAssignment(assignment); + return Ok(); + } + + [Authorize(Policy = "IsInstructor")] + [HttpPatch] + [Route("/external-assignments/{assignmentID}/title")] + public ActionResult PatchAssignmentTitle(int assignmentID) + { + int courseID = this.GetCourseID(); + var body = new StreamReader(Request.Body).ReadToEnd(); + _databaseManager.UpdateExternalAssignmentTitle(assignmentID, courseID, (string)JObject.Parse(body)["title"]); + return Ok(); + } + [Authorize(Policy = "IsInstructor")] [HttpPost] [Route("/external-assignments")] diff --git a/IguideME.Web/Frontend/src/api/entries.ts b/IguideME.Web/Frontend/src/api/entries.ts index 7d5dd8e1..37ef43e2 100644 --- a/IguideME.Web/Frontend/src/api/entries.ts +++ b/IguideME.Web/Frontend/src/api/entries.ts @@ -47,6 +47,10 @@ export async function deleteRequirement(id: number): Promise { return await apiClient.delete(`learning-goals/requirements/${id}`); } -export async function patchExternalAssignment({ id, title }: { id: number; title: string }): Promise { - return await apiClient.patch(`external-assignments/${id}`, { title }); +export async function patchExternalAssignment(assignment: Assignment): Promise { + return await apiClient.patch(`external-assignments/${assignment.id}`, assignment); +} + +export async function patchExternalAssignmentTitle({ id, title }: { id: number; title: string }): Promise { + return await apiClient.patch(`external-assignments/${id}/title`, { title }); } diff --git a/IguideME.Web/Frontend/src/components/pages/admin/datawizard/assignment-settings-form.tsx b/IguideME.Web/Frontend/src/components/pages/admin/datawizard/assignment-settings-form.tsx index d31e6542..7462d723 100644 --- a/IguideME.Web/Frontend/src/components/pages/admin/datawizard/assignment-settings-form.tsx +++ b/IguideME.Web/Frontend/src/components/pages/admin/datawizard/assignment-settings-form.tsx @@ -30,7 +30,7 @@ const AssignmentSettingsForm: FC = ({ assignment }) }} onFinish={() => updateAssignment({ ...assignment, ...form.getFieldsValue() })} > -
+
{ - return ( - <> - - - - ); -}; - -const Wizard: FC = (): ReactElement => { - const { - data: assignments, - isError: assignmentError, - isLoading: assignmentLoading, - } = useQuery({ - queryKey: ['external-assignments'], - queryFn: getExternalAssignments, - }); - - const { courseId } = useRequiredParams(['courseId']); - const { - data: students, - isLoading: studentsLoading, - isError: studentsError, - } = useQuery({ - queryKey: ['students', courseId], - queryFn: async () => await getStudentsByCourse(courseId), - }); - - const queryClient = useQueryClient(); - const { mutate: postAssignment } = useMutation({ - mutationFn: postExternalAssignment, - onSuccess: async () => { - await queryClient.invalidateQueries({ queryKey: ['external-assignments'] }); - }, - }); - - const addAssignment = (): void => { - postAssignment({ - id: -1, - course_id: -1, - title: 'Title', - published: 2, - muted: false, - due_date: -1, - max_grade: 0, - grading_type: GradingType.Points, - }); - }; - - if (assignmentLoading || studentsLoading) { - return ( - -
- - ); - } else if (assignmentError || studentsError) { - return ( -
- -
- ); - } - - return ( -
- -
- {assignments?.map((ass) => )} -
-
- ); -}; - -interface ViewExternalAssignmentProps { - assignment: Assignment; - students: User[]; -} - -const ViewExternalAssignment: FC = ({ assignment, students }): ReactElement => { - const [editing, setEditing] = useState(false); - const [title, setTitle] = useState(assignment.title); - const [uploadMenuOpen, setUploadMenuOpen] = useState(false); - - const queryClient = useQueryClient(); - const { mutate } = useMutation({ - mutationFn: patchExternalAssignment, - onSuccess: async () => { - await queryClient.invalidateQueries({ queryKey: ['external-assignments'] }); - }, - }); - - return ( -
-
- - {!uploadMenuOpen && ( - - )} -
- {uploadMenuOpen && ( -
- { - setUploadMenuOpen(false); - }} - /> -
- )} -
-

Settings

- -
-
- ); -}; - -export default DataWizard; +import { getStudentsByCourse } from '@/api/courses'; +import { getExternalAssignments, patchExternalAssignmentTitle, postExternalAssignment } from '@/api/entries'; +import AdminTitle from '@/components/atoms/admin-titles/admin-titles'; +import UploadManager from '@/components/crystals/upload-manager/upload-manager'; +import QueryError from '@/components/particles/QueryError'; +import QueryLoading from '@/components/particles/QueryLoading'; +import { GradingType } from '@/types/grades'; +import { type Assignment } from '@/types/tile'; +import { type User } from '@/types/user'; +import { useRequiredParams } from '@/utils/params'; +import { PlusOutlined } from '@ant-design/icons'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { Button, Input, Tooltip } from 'antd'; +import { useState, type FC, type ReactElement } from 'react'; +import AssignmentSettingsForm from './assignment-settings-form'; + +const DataWizard: FC = (): ReactElement => { + return ( + <> + + + + ); +}; + +const Wizard: FC = (): ReactElement => { + const { + data: assignments, + isError: assignmentError, + isLoading: assignmentLoading, + } = useQuery({ + queryKey: ['external-assignments'], + queryFn: getExternalAssignments, + }); + + const { courseId } = useRequiredParams(['courseId']); + const { + data: students, + isLoading: studentsLoading, + isError: studentsError, + } = useQuery({ + queryKey: ['students', courseId], + queryFn: async () => await getStudentsByCourse(courseId), + }); + + const queryClient = useQueryClient(); + const { mutate: postAssignment } = useMutation({ + mutationFn: postExternalAssignment, + onSuccess: async () => { + await queryClient.invalidateQueries({ queryKey: ['external-assignments'] }); + }, + }); + + const addAssignment = (): void => { + postAssignment({ + id: -1, + course_id: -1, + title: 'Title', + published: 2, + muted: false, + due_date: -1, + max_grade: 0, + grading_type: GradingType.Points, + }); + }; + + if (assignmentLoading || studentsLoading) { + return ( + +
+ + ); + } else if (assignmentError || studentsError) { + return ( +
+ +
+ ); + } + + return ( +
+ +
+ {assignments?.map((ass) => )} +
+
+ ); +}; + +interface ViewExternalAssignmentProps { + assignment: Assignment; + students: User[]; +} + +const ViewExternalAssignment: FC = ({ assignment, students }): ReactElement => { + const [editing, setEditing] = useState(false); + const [title, setTitle] = useState(assignment.title); + const [uploadMenuOpen, setUploadMenuOpen] = useState(false); + + const queryClient = useQueryClient(); + const { mutate } = useMutation({ + mutationFn: patchExternalAssignmentTitle, + onSuccess: async () => { + await queryClient.invalidateQueries({ queryKey: ['external-assignments'] }); + }, + }); + + return ( +
+
+ + {!uploadMenuOpen && ( + + )} +
+ {uploadMenuOpen && ( +
+ { + setUploadMenuOpen(false); + }} + /> +
+ )} +
+

Settings

+ +
+
+ ); +}; + +export default DataWizard; diff --git a/IguideME.Web/Services/Constants/DatabaseQueries.cs b/IguideME.Web/Services/Constants/DatabaseQueries.cs index 1e55bec7..e16d1f5f 100644 --- a/IguideME.Web/Services/Constants/DatabaseQueries.cs +++ b/IguideME.Web/Services/Constants/DatabaseQueries.cs @@ -618,6 +618,22 @@ ORDER BY `sync_id` AND `published`=2 ;"; + public const string UPDATE_EXTERNAL_ASSIGNMENT = + @"UPDATE `assignments` + SET `title`=@title, + `max_grade`=@maxGrade, + `grading_type`=@gradingType + WHERE `assignments`.`assignment_id`=@ID + AND `assignments`.`course_id`=@courseID + ;"; + + public const string UPDATE_EXTERNAL_ASSIGNMENT_TITLE = + @"UPDATE `assignments` + SET `title`=@title + WHERE `assignments`.`assignment_id`=@ID + AND `assignments`.`course_id`=@courseID + ;"; + public const string REGISTER_EXTERNAL_ASSIGNMENT = @"INSERT OR REPLACE INTO `assignments` diff --git a/IguideME.Web/Services/DatabaseManager.cs b/IguideME.Web/Services/DatabaseManager.cs index 7541730c..6a510f27 100644 --- a/IguideME.Web/Services/DatabaseManager.cs +++ b/IguideME.Web/Services/DatabaseManager.cs @@ -28,7 +28,7 @@ private DatabaseManager(bool isDev = false) } else { - _connection_string = "Data Source=data/IguideME2.db;Version=3;New=False;Compress=True;"; + _connection_string = "Data Source=/data/IguideME2.db;Version=3;New=False;Compress=True;"; } // DatabaseManager.s_instance.RunMigrations(); @@ -549,7 +549,8 @@ public void RegisterUser(User user) ); } - public List GetExternalAssignments(int courseID) { + public List GetExternalAssignments(int courseID) + { List assignments = new(); using ( @@ -632,6 +633,28 @@ public int RegisterExternalAssignment(AppAssignment assignment) ); return assignment_id; + } + + public void UpdateExternalAssignment(AppAssignment assignment) + { + int assignment_id = IDNonQuery( + DatabaseQueries.UPDATE_EXTERNAL_ASSIGNMENT, + new SQLiteParameter("ID", assignment.ID), + new SQLiteParameter("courseID", assignment.CourseID), + new SQLiteParameter("title", assignment.Title), + new SQLiteParameter("maxGrade", assignment.MaxGrade), + new SQLiteParameter("gradingType", assignment.GradingType) + ); + } + + public void UpdateExternalAssignmentTitle(int assignmentID, int courseID, string title) + { + int assignment_id = IDNonQuery( + DatabaseQueries.UPDATE_EXTERNAL_ASSIGNMENT_TITLE, + new SQLiteParameter("ID", assignmentID), + new SQLiteParameter("courseID", courseID), + new SQLiteParameter("title", title) + ); } public void RegisterDiscussion(AppDiscussionTopic discussion, long syncID) @@ -2741,7 +2764,7 @@ public void CreateLayoutTileGroup(int courseID, string title, int position) // if (cols.Count < 1) return; NonQuery( - DatabaseQueries.REGISTER_TILE_GROUP, + DatabaseQueries.REGISTER_TILE_GROUP, // new SQLiteParameter("columnID", cols[0].ID), new SQLiteParameter("title", title), new SQLiteParameter("order", position),