mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
fix: misc assignments bugs
This commit is contained in:
parent
d244ff2076
commit
a6b5db29ca
6 changed files with 121 additions and 37 deletions
|
|
@ -162,7 +162,7 @@ class AssignmentTask(AssignmentTaskBase, table=True):
|
||||||
|
|
||||||
class AssignmentTaskSubmissionBase(SQLModel):
|
class AssignmentTaskSubmissionBase(SQLModel):
|
||||||
"""Represents the common fields for an assignment task submission."""
|
"""Represents the common fields for an assignment task submission."""
|
||||||
|
assignment_task_submission_uuid: str
|
||||||
task_submission: Dict = Field(default={}, sa_column=Column(JSON))
|
task_submission: Dict = Field(default={}, sa_column=Column(JSON))
|
||||||
grade: int = 0 # Value is always between 0-100
|
grade: int = 0 # Value is always between 0-100
|
||||||
task_submission_grade_feedback: str
|
task_submission_grade_feedback: str
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ from src.security.rbac.rbac import (
|
||||||
authorization_verify_based_on_roles_and_authorship,
|
authorization_verify_based_on_roles_and_authorship,
|
||||||
authorization_verify_if_element_is_public,
|
authorization_verify_if_element_is_public,
|
||||||
authorization_verify_if_user_is_anon,
|
authorization_verify_if_user_is_anon,
|
||||||
|
authorization_verify_based_on_roles,
|
||||||
)
|
)
|
||||||
from src.services.courses.activities.uploads.sub_file import upload_submission_file
|
from src.services.courses.activities.uploads.sub_file import upload_submission_file
|
||||||
from src.services.courses.activities.uploads.tasks_ref_files import (
|
from src.services.courses.activities.uploads.tasks_ref_files import (
|
||||||
|
|
@ -565,10 +566,17 @@ async def put_assignment_task_submission_file(
|
||||||
org_statement = select(Organization).where(Organization.id == course.org_id)
|
org_statement = select(Organization).where(Organization.id == course.org_id)
|
||||||
org = db_session.exec(org_statement).first()
|
org = db_session.exec(org_statement).first()
|
||||||
|
|
||||||
# RBAC check
|
# RBAC check - only need read permission to submit files
|
||||||
await rbac_check(request, course.course_uuid, current_user, "update", db_session)
|
await rbac_check(request, course.course_uuid, current_user, "read", db_session)
|
||||||
|
|
||||||
# Upload reference file
|
# Check if user is enrolled in the course
|
||||||
|
if not await authorization_verify_based_on_roles(request, current_user.id, "read", course.course_uuid, db_session):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=403,
|
||||||
|
detail="You must be enrolled in this course to submit files"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Upload submission file
|
||||||
if sub_file and sub_file.filename and activity and org:
|
if sub_file and sub_file.filename and activity and org:
|
||||||
name_in_disk = f"{assignment_task_uuid}_sub_{current_user.email}_{uuid4()}.{sub_file.filename.split('.')[-1]}"
|
name_in_disk = f"{assignment_task_uuid}_sub_{current_user.email}_{uuid4()}.{sub_file.filename.split('.')[-1]}"
|
||||||
await upload_submission_file(
|
await upload_submission_file(
|
||||||
|
|
@ -699,7 +707,7 @@ async def handle_assignment_task_submission(
|
||||||
current_user: PublicUser | AnonymousUser,
|
current_user: PublicUser | AnonymousUser,
|
||||||
db_session: Session,
|
db_session: Session,
|
||||||
):
|
):
|
||||||
# TODO: Improve terrible implementation of this function
|
assignment_task_submission_uuid = assignment_task_submission_object.assignment_task_submission_uuid
|
||||||
# Check if assignment task exists
|
# Check if assignment task exists
|
||||||
statement = select(AssignmentTask).where(
|
statement = select(AssignmentTask).where(
|
||||||
AssignmentTask.assignment_task_uuid == assignment_task_uuid
|
AssignmentTask.assignment_task_uuid == assignment_task_uuid
|
||||||
|
|
@ -722,15 +730,59 @@ async def handle_assignment_task_submission(
|
||||||
detail="Assignment not found",
|
detail="Assignment not found",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check if user already submitted the assignment
|
# Check if course exists
|
||||||
|
statement = select(Course).where(Course.id == assignment.course_id)
|
||||||
|
course = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
if not course:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail="Course not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if user has instructor/admin permissions
|
||||||
|
is_instructor = await authorization_verify_based_on_roles(request, current_user.id, "update", course.course_uuid, db_session)
|
||||||
|
|
||||||
|
# For regular users, ensure they can only submit their own work
|
||||||
|
if not is_instructor:
|
||||||
|
# Check if user is enrolled in the course
|
||||||
|
if not await authorization_verify_based_on_roles(request, current_user.id, "read", course.course_uuid, db_session):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=403,
|
||||||
|
detail="You must be enrolled in this course to submit assignments"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Regular users cannot update grades - only check if actual values are being set
|
||||||
|
if (assignment_task_submission_object.grade is not None and assignment_task_submission_object.grade != 0) or \
|
||||||
|
(assignment_task_submission_object.task_submission_grade_feedback is not None and assignment_task_submission_object.task_submission_grade_feedback != ""):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=403,
|
||||||
|
detail="You do not have permission to update grades"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Only need read permission for submissions
|
||||||
|
await rbac_check(request, course.course_uuid, current_user, "read", db_session)
|
||||||
|
else:
|
||||||
|
# Instructors/admins need update permission to grade
|
||||||
|
await rbac_check(request, course.course_uuid, current_user, "update", db_session)
|
||||||
|
|
||||||
|
# Try to find existing submission if UUID is provided
|
||||||
|
assignment_task_submission = None
|
||||||
|
if assignment_task_submission_uuid:
|
||||||
statement = select(AssignmentTaskSubmission).where(
|
statement = select(AssignmentTaskSubmission).where(
|
||||||
AssignmentTaskSubmission.assignment_task_id == assignment_task.id,
|
AssignmentTaskSubmission.assignment_task_submission_uuid == assignment_task_submission_uuid
|
||||||
AssignmentTaskSubmission.user_id == current_user.id,
|
|
||||||
)
|
)
|
||||||
assignment_task_submission = db_session.exec(statement).first()
|
assignment_task_submission = db_session.exec(statement).first()
|
||||||
|
|
||||||
# Update Task submission if it exists
|
# If submission exists, update it
|
||||||
if assignment_task_submission:
|
if assignment_task_submission:
|
||||||
|
# For regular users, ensure they can only update their own submissions
|
||||||
|
if not is_instructor and assignment_task_submission.user_id != current_user.id:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=403,
|
||||||
|
detail="You can only update your own submissions"
|
||||||
|
)
|
||||||
|
|
||||||
# Update only the fields that were passed in
|
# Update only the fields that were passed in
|
||||||
for var, value in vars(assignment_task_submission_object).items():
|
for var, value in vars(assignment_task_submission_object).items():
|
||||||
if value is not None:
|
if value is not None:
|
||||||
|
|
@ -742,9 +794,6 @@ async def handle_assignment_task_submission(
|
||||||
db_session.commit()
|
db_session.commit()
|
||||||
db_session.refresh(assignment_task_submission)
|
db_session.refresh(assignment_task_submission)
|
||||||
|
|
||||||
# return assignment task submission read
|
|
||||||
return AssignmentTaskSubmissionRead.model_validate(assignment_task_submission)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Create new Task submission
|
# Create new Task submission
|
||||||
current_time = str(datetime.now())
|
current_time = str(datetime.now())
|
||||||
|
|
@ -753,10 +802,10 @@ async def handle_assignment_task_submission(
|
||||||
model_data = assignment_task_submission_object.model_dump()
|
model_data = assignment_task_submission_object.model_dump()
|
||||||
|
|
||||||
assignment_task_submission = AssignmentTaskSubmission(
|
assignment_task_submission = AssignmentTaskSubmission(
|
||||||
assignment_task_submission_uuid=f"assignmenttasksubmission_{uuid4()}",
|
assignment_task_submission_uuid=assignment_task_submission_uuid or f"assignmenttasksubmission_{uuid4()}",
|
||||||
task_submission=model_data["task_submission"],
|
task_submission=model_data["task_submission"],
|
||||||
grade=model_data["grade"],
|
grade=0, # Always start with 0 for new submissions
|
||||||
task_submission_grade_feedback=model_data["task_submission_grade_feedback"],
|
task_submission_grade_feedback="", # Start with empty feedback
|
||||||
assignment_task_id=int(assignment_task.id), # type: ignore
|
assignment_task_id=int(assignment_task.id), # type: ignore
|
||||||
assignment_type=assignment_task.assignment_type,
|
assignment_type=assignment_task.assignment_type,
|
||||||
activity_id=assignment.activity_id,
|
activity_id=assignment.activity_id,
|
||||||
|
|
@ -770,6 +819,7 @@ async def handle_assignment_task_submission(
|
||||||
# Insert Assignment Task Submission in DB
|
# Insert Assignment Task Submission in DB
|
||||||
db_session.add(assignment_task_submission)
|
db_session.add(assignment_task_submission)
|
||||||
db_session.commit()
|
db_session.commit()
|
||||||
|
db_session.refresh(assignment_task_submission)
|
||||||
|
|
||||||
# return assignment task submission read
|
# return assignment task submission read
|
||||||
return AssignmentTaskSubmissionRead.model_validate(assignment_task_submission)
|
return AssignmentTaskSubmissionRead.model_validate(assignment_task_submission)
|
||||||
|
|
@ -1096,7 +1146,7 @@ async def create_assignment_submission(
|
||||||
)
|
)
|
||||||
|
|
||||||
# RBAC check
|
# RBAC check
|
||||||
await rbac_check(request, course.course_uuid, current_user, "update", db_session)
|
await rbac_check(request, course.course_uuid, current_user, "read", db_session)
|
||||||
|
|
||||||
# Create Assignment User Submission
|
# Create Assignment User Submission
|
||||||
assignment_user_submission = AssignmentUserSubmission(
|
assignment_user_submission = AssignmentUserSubmission(
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import toast from 'react-hot-toast';
|
||||||
|
|
||||||
type FileSchema = {
|
type FileSchema = {
|
||||||
fileUUID: string;
|
fileUUID: string;
|
||||||
|
assignment_task_submission_uuid?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type TaskFileObjectProps = {
|
type TaskFileObjectProps = {
|
||||||
|
|
@ -64,13 +65,13 @@ export default function TaskFileObject({ view, user_id, assignmentTaskUUID }: Ta
|
||||||
// wait for 1 second to show loading animation
|
// wait for 1 second to show loading animation
|
||||||
await new Promise((r) => setTimeout(r, 1500))
|
await new Promise((r) => setTimeout(r, 1500))
|
||||||
if (res.success === false) {
|
if (res.success === false) {
|
||||||
|
|
||||||
setError(res.data.detail)
|
setError(res.data.detail)
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
} else {
|
} else {
|
||||||
assignmentTaskStateHook({ type: 'reload' })
|
assignmentTaskStateHook({ type: 'reload' })
|
||||||
setUserSubmissions({
|
setUserSubmissions({
|
||||||
fileUUID: res.data.file_uuid,
|
fileUUID: res.data.file_uuid,
|
||||||
|
assignment_task_submission_uuid: res.data.assignment_task_submission_uuid
|
||||||
})
|
})
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
setError('')
|
setError('')
|
||||||
|
|
@ -86,8 +87,14 @@ export default function TaskFileObject({ view, user_id, assignmentTaskUUID }: Ta
|
||||||
if (assignmentTaskUUID) {
|
if (assignmentTaskUUID) {
|
||||||
const res = await getAssignmentTaskSubmissionsMe(assignmentTaskUUID, assignment.assignment_object.assignment_uuid, access_token);
|
const res = await getAssignmentTaskSubmissionsMe(assignmentTaskUUID, assignment.assignment_object.assignment_uuid, access_token);
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
setUserSubmissions(res.data.task_submission);
|
setUserSubmissions({
|
||||||
setInitialUserSubmissions(res.data.task_submission);
|
...res.data.task_submission,
|
||||||
|
assignment_task_submission_uuid: res.data.assignment_task_submission_uuid
|
||||||
|
});
|
||||||
|
setInitialUserSubmissions({
|
||||||
|
...res.data.task_submission,
|
||||||
|
assignment_task_submission_uuid: res.data.assignment_task_submission_uuid
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -101,6 +108,7 @@ export default function TaskFileObject({ view, user_id, assignmentTaskUUID }: Ta
|
||||||
|
|
||||||
// Save the quiz to the server
|
// Save the quiz to the server
|
||||||
const values = {
|
const values = {
|
||||||
|
assignment_task_submission_uuid: userSubmissions.assignment_task_submission_uuid,
|
||||||
task_submission: userSubmissions,
|
task_submission: userSubmissions,
|
||||||
grade: 0,
|
grade: 0,
|
||||||
task_submission_grade_feedback: '',
|
task_submission_grade_feedback: '',
|
||||||
|
|
@ -156,9 +164,15 @@ export default function TaskFileObject({ view, user_id, assignmentTaskUUID }: Ta
|
||||||
if (assignmentTaskUUID && user_id) {
|
if (assignmentTaskUUID && user_id) {
|
||||||
const res = await getAssignmentTaskSubmissionsUser(assignmentTaskUUID, user_id, assignment.assignment_object.assignment_uuid, access_token);
|
const res = await getAssignmentTaskSubmissionsUser(assignmentTaskUUID, user_id, assignment.assignment_object.assignment_uuid, access_token);
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
setUserSubmissions(res.data.task_submission);
|
setUserSubmissions({
|
||||||
|
...res.data.task_submission,
|
||||||
|
assignment_task_submission_uuid: res.data.assignment_task_submission_uuid
|
||||||
|
});
|
||||||
setUserSubmissionObject(res.data);
|
setUserSubmissionObject(res.data);
|
||||||
setInitialUserSubmissions(res.data.task_submission);
|
setInitialUserSubmissions({
|
||||||
|
...res.data.task_submission,
|
||||||
|
assignment_task_submission_uuid: res.data.assignment_task_submission_uuid
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -173,6 +187,7 @@ export default function TaskFileObject({ view, user_id, assignmentTaskUUID }: Ta
|
||||||
|
|
||||||
// Save the grade to the server
|
// Save the grade to the server
|
||||||
const values = {
|
const values = {
|
||||||
|
assignment_task_submission_uuid: userSubmissions.assignment_task_submission_uuid,
|
||||||
task_submission: userSubmissions,
|
task_submission: userSubmissions,
|
||||||
grade: grade,
|
grade: grade,
|
||||||
task_submission_grade_feedback: 'Graded by teacher : @' + session.data.user.username,
|
task_submission_grade_feedback: 'Graded by teacher : @' + session.data.user.username,
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ type QuizSubmitSchema = {
|
||||||
optionUUID: string;
|
optionUUID: string;
|
||||||
answer: boolean
|
answer: boolean
|
||||||
}[];
|
}[];
|
||||||
|
assignment_task_submission_uuid?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type TaskQuizObjectProps = {
|
type TaskQuizObjectProps = {
|
||||||
|
|
@ -175,8 +176,14 @@ function TaskQuizObject({ view, assignmentTaskUUID, user_id }: TaskQuizObjectPro
|
||||||
if (assignmentTaskUUID) {
|
if (assignmentTaskUUID) {
|
||||||
const res = await getAssignmentTaskSubmissionsMe(assignmentTaskUUID, assignment.assignment_object.assignment_uuid, access_token);
|
const res = await getAssignmentTaskSubmissionsMe(assignmentTaskUUID, assignment.assignment_object.assignment_uuid, access_token);
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
setUserSubmissions(res.data.task_submission);
|
setUserSubmissions({
|
||||||
setInitialUserSubmissions(res.data.task_submission);
|
...res.data.task_submission,
|
||||||
|
assignment_task_submission_uuid: res.data.assignment_task_submission_uuid
|
||||||
|
});
|
||||||
|
setInitialUserSubmissions({
|
||||||
|
...res.data.task_submission,
|
||||||
|
assignment_task_submission_uuid: res.data.assignment_task_submission_uuid
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -242,9 +249,15 @@ function TaskQuizObject({ view, assignmentTaskUUID, user_id }: TaskQuizObjectPro
|
||||||
if (assignmentTaskUUID && user_id) {
|
if (assignmentTaskUUID && user_id) {
|
||||||
const res = await getAssignmentTaskSubmissionsUser(assignmentTaskUUID, user_id, assignment.assignment_object.assignment_uuid, access_token);
|
const res = await getAssignmentTaskSubmissionsUser(assignmentTaskUUID, user_id, assignment.assignment_object.assignment_uuid, access_token);
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
setUserSubmissions(res.data.task_submission);
|
setUserSubmissions({
|
||||||
|
...res.data.task_submission,
|
||||||
|
assignment_task_submission_uuid: res.data.assignment_task_submission_uuid
|
||||||
|
});
|
||||||
setUserSubmissionObject(res.data);
|
setUserSubmissionObject(res.data);
|
||||||
setInitialUserSubmissions(res.data.task_submission);
|
setInitialUserSubmissions({
|
||||||
|
...res.data.task_submission,
|
||||||
|
assignment_task_submission_uuid: res.data.assignment_task_submission_uuid
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -271,6 +284,7 @@ function TaskQuizObject({ view, assignmentTaskUUID, user_id }: TaskQuizObjectPro
|
||||||
|
|
||||||
// Save the grade to the server
|
// Save the grade to the server
|
||||||
const values = {
|
const values = {
|
||||||
|
assignment_task_submission_uuid: userSubmissions.assignment_task_submission_uuid,
|
||||||
task_submission: userSubmissions,
|
task_submission: userSubmissions,
|
||||||
grade: finalGrade,
|
grade: finalGrade,
|
||||||
task_submission_grade_feedback: 'Auto graded by system',
|
task_submission_grade_feedback: 'Auto graded by system',
|
||||||
|
|
|
||||||
|
|
@ -41,22 +41,28 @@ function AssignmentSubmissionsSubPage({ assignment_uuid }: { assignment_uuid: st
|
||||||
<X size={18} />
|
<X size={18} />
|
||||||
<h3>Late</h3>
|
<h3>Late</h3>
|
||||||
</div>
|
</div>
|
||||||
|
<div className='flex flex-col gap-4'>
|
||||||
{renderSubmissions('LATE')}
|
{renderSubmissions('LATE')}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div className='flex-1'>
|
<div className='flex-1'>
|
||||||
<div className='flex w-fit mx-auto px-3.5 py-1 bg-amber-600/80 space-x-2 my-5 items-center text-sm font-bold text-white rounded-full'>
|
<div className='flex w-fit mx-auto px-3.5 py-1 bg-amber-600/80 space-x-2 my-5 items-center text-sm font-bold text-white rounded-full'>
|
||||||
<SendHorizonal size={18} />
|
<SendHorizonal size={18} />
|
||||||
<h3>Submitted</h3>
|
<h3>Submitted</h3>
|
||||||
</div>
|
</div>
|
||||||
|
<div className='flex flex-col gap-4'>
|
||||||
{renderSubmissions('SUBMITTED')}
|
{renderSubmissions('SUBMITTED')}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div className='flex-1'>
|
<div className='flex-1'>
|
||||||
<div className='flex w-fit mx-auto px-3.5 py-1 bg-emerald-600/80 space-x-2 my-5 items-center text-sm font-bold text-white rounded-full'>
|
<div className='flex w-fit mx-auto px-3.5 py-1 bg-emerald-600/80 space-x-2 my-5 items-center text-sm font-bold text-white rounded-full'>
|
||||||
<UserCheck size={18} />
|
<UserCheck size={18} />
|
||||||
<h3>Graded</h3>
|
<h3>Graded</h3>
|
||||||
</div>
|
</div>
|
||||||
|
<div className='flex flex-col gap-4'>
|
||||||
{renderSubmissions('GRADED')}
|
{renderSubmissions('GRADED')}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ function EvaluateAssignment({ user_id }: any) {
|
||||||
const assignments = useAssignments() as any;
|
const assignments = useAssignments() as any;
|
||||||
const session = useLHSession() as any;
|
const session = useLHSession() as any;
|
||||||
const org = useOrg() as any;
|
const org = useOrg() as any;
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
async function gradeAssignment() {
|
async function gradeAssignment() {
|
||||||
const res = await putFinalGrade(user_id, assignments?.assignment_object.assignment_uuid, session.data?.tokens?.access_token);
|
const res = await putFinalGrade(user_id, assignments?.assignment_object.assignment_uuid, session.data?.tokens?.access_token);
|
||||||
|
|
@ -92,17 +91,17 @@ function EvaluateAssignment({ user_id }: any) {
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
<div className='flex space-x-4 font-semibold items-center justify-between'>
|
<div className='flex space-x-4 font-semibold items-center justify-between'>
|
||||||
<button onClick={rejectAssignment} className='flex space-x-2 px-4 py-2 text-sm bg-rose-600/80 text-white rounded-lg nice-shadow items-center'>
|
<button onClick={rejectAssignment} className='flex space-x-2 px-4 py-2 text-sm bg-rose-600/80 text-white rounded-lg nice-shadow items-center cursor-pointer'>
|
||||||
<X size={18} />
|
<X size={18} />
|
||||||
<span>Reject Assignment</span>
|
<span>Reject Assignment</span>
|
||||||
</button>
|
</button>
|
||||||
<div className='flex space-x-3 items-center'>
|
<div className='flex space-x-3 items-center'>
|
||||||
<button onClick={gradeAssignment} className='flex space-x-2 px-4 py-2 text-sm bg-violet-600/80 text-white rounded-lg nice-shadow items-center'>
|
<button onClick={gradeAssignment} className='flex space-x-2 px-4 py-2 text-sm bg-violet-600/80 text-white rounded-lg nice-shadow items-center cursor-pointer'>
|
||||||
<BookOpenCheck size={18} />
|
<BookOpenCheck size={18} />
|
||||||
<span>Set final grade</span>
|
<span>Set final grade</span>
|
||||||
</button>
|
</button>
|
||||||
<MoveRight className='text-gray-400' size={18} />
|
<MoveRight className='text-gray-400' size={18} />
|
||||||
<button onClick={markActivityAsDone} className='flex space-x-2 px-4 py-2 text-sm bg-teal-600/80 text-white rounded-lg nice-shadow items-center'>
|
<button onClick={markActivityAsDone} className='flex space-x-2 px-4 py-2 text-sm bg-teal-600/80 text-white rounded-lg nice-shadow items-center cursor-pointer'>
|
||||||
<Check size={18} />
|
<Check size={18} />
|
||||||
<span>Mark Activity as Done for User</span>
|
<span>Mark Activity as Done for User</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue