Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
import React from "react"
import { useTranslation } from "react-i18next"

import DeletedUserNotice from "@/components/DeletedUserNotice"
import KeyValueCard from "@/components/KeyValueCard"
import useCourseInstancesQuery from "@/hooks/useCourseInstancesQuery"
import { useCourseQuery } from "@/hooks/useCourseQuery"
import { useUserDetails } from "@/hooks/useUserDetails"
import { extractUserDetail, isUserDetailsNotFound, useUserDetails } from "@/hooks/useUserDetails"

interface CourseInstanceUserInfoBoxProps {
courseId: string
Expand All @@ -23,6 +24,8 @@ const CourseInstanceUserInfoBox: React.FC<CourseInstanceUserInfoBoxProps> = ({
const courseQuery = useCourseQuery(courseId)
const courseInstancesQuery = useCourseInstancesQuery(courseId)
const userDetailsQuery = useUserDetails([courseId], userId)
const userDetails = extractUserDetail(userDetailsQuery.data)
const userDetailsNotFound = isUserDetailsNotFound(userDetailsQuery.data)

if (courseQuery.isError || courseInstancesQuery.isError || userDetailsQuery.isError) {
return null
Expand Down Expand Up @@ -62,39 +65,46 @@ const CourseInstanceUserInfoBox: React.FC<CourseInstanceUserInfoBoxProps> = ({
},
]
: []),
{
// eslint-disable-next-line i18next/no-literal-string
key: "first-name",
label: t("first-name"),
value: userDetailsQuery.data.first_name,
colSpan: 2,
},
{
// eslint-disable-next-line i18next/no-literal-string
key: "last-name",
label: t("last-name"),
value: userDetailsQuery.data.last_name,
colSpan: 2,
},
{
// eslint-disable-next-line i18next/no-literal-string
key: "email",
label: t("label-email"),
value: userDetailsQuery.data.email,
colSpan: 4,
},
...(userDetails
? [
{
// eslint-disable-next-line i18next/no-literal-string
key: "first-name",
label: t("first-name"),
value: userDetails.first_name,
colSpan: 2,
},
{
// eslint-disable-next-line i18next/no-literal-string
key: "last-name",
label: t("last-name"),
value: userDetails.last_name,
colSpan: 2,
},
{
// eslint-disable-next-line i18next/no-literal-string
key: "email",
label: t("label-email"),
value: userDetails.email,
colSpan: 4,
},
]
: []),
]

return (
<KeyValueCard
sections={[
{
title: t("user-information"),
items,
gridColumns: 8,
},
]}
/>
<>
{userDetailsNotFound && <DeletedUserNotice userId={userId} />}
<KeyValueCard
sections={[
{
title: t("user-information"),
items,
gridColumns: 8,
},
]}
/>
</>
)
}

Expand Down
29 changes: 19 additions & 10 deletions services/main-frontend/src/app/manage/users/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import { useTranslation } from "react-i18next"
import CourseEnrollmentsList from "./CourseEnrollmentsList"
import ExerciseResetLogList from "./ExerciseResetLogList"

import { useUserDetails } from "@/hooks/useUserDetails"
import DeletedUserNotice from "@/components/DeletedUserNotice"
import { extractUserDetail, isUserDetailsNotFound, useUserDetails } from "@/hooks/useUserDetails"
import { getCourseEnrollmentsInfo } from "@/services/backend/users"
import ErrorBanner from "@/shared-module/common/components/ErrorBanner"
import OnlyRenderIfPermissions from "@/shared-module/common/components/OnlyRenderIfPermissions"
Expand All @@ -35,6 +36,8 @@ const UserPage: React.FC = () => {
const courseIds = courseEnrollmentsQuery.data?.course_enrollments.map((e) => e.course_id) ?? []

const userDetailsQuery = useUserDetails(courseIds, id)
const userDetails = extractUserDetail(userDetailsQuery.data)
const userDetailsNotFound = isUserDetailsNotFound(userDetailsQuery.data)

if (courseEnrollmentsQuery.isError) {
return <ErrorBanner error={courseEnrollmentsQuery.error} variant="readOnly" />
Expand All @@ -57,15 +60,21 @@ const UserPage: React.FC = () => {
<p>
{t("label-user-id")}: {id}
</p>
<p>
{t("label-email")}: {userDetailsQuery.data.email}
</p>
<p>
{t("first-name")}: {userDetailsQuery.data.first_name}
</p>
<p>
{t("last-name")}: {userDetailsQuery.data.last_name}
</p>
{userDetailsNotFound ? (
<DeletedUserNotice userId={id} />
) : (
<>
<p>
{t("label-email")}: {userDetails?.email}
</p>
<p>
{t("first-name")}: {userDetails?.first_name}
</p>
<p>
{t("last-name")}: {userDetails?.last_name}
</p>
</>
)}
</Area>
<Area>
<h2>{t("header-course-enrollments")}</h2>
Expand Down
89 changes: 53 additions & 36 deletions services/main-frontend/src/app/submissions/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ import React from "react"
import { useTranslation } from "react-i18next"

import AllSubmissionsList from "@/components/AllSubmissionsList"
import DeletedUserNotice from "@/components/DeletedUserNotice"
import ExerciseGradingCard from "@/components/ExerciseGradingCard"
import KeyValueCard from "@/components/KeyValueCard"
import MainFrontedViewSubmission from "@/components/MainFrontedViewSubmission"
import { useExerciseSubmissionsForUser } from "@/hooks/useExerciseSubmissionsForUser"
import { useUserCourseSettings } from "@/hooks/useUserCourseSettings"
import { useUserDetails } from "@/hooks/useUserDetails"
import { extractUserDetail, isUserDetailsNotFound, useUserDetails } from "@/hooks/useUserDetails"
import { fetchSubmissionInfo } from "@/services/backend/submissions"
import Breadcrumbs from "@/shared-module/common/components/Breadcrumbs"
import Button from "@/shared-module/common/components/Button"
Expand Down Expand Up @@ -42,6 +43,8 @@ const Submission: React.FC = () => {
getSubmissionInfo.data?.exercise.course_id ? [getSubmissionInfo.data.exercise.course_id] : null,
getSubmissionInfo.data?.exercise_slide_submission.user_id,
)
const user = extractUserDetail(userDetails.data)
const userDetailsNotFound = isUserDetailsNotFound(userDetails.data)

const exerciseSubmissions = useExerciseSubmissionsForUser(
getSubmissionInfo.data?.exercise.id,
Expand Down Expand Up @@ -175,41 +178,45 @@ const Submission: React.FC = () => {
},
],
},
{
title: t("user-information"),
items: [
{
// eslint-disable-next-line i18next/no-literal-string
key: "first-name",
label: t("first-name"),
value: userDetails.data.first_name ?? "",
},
{
// eslint-disable-next-line i18next/no-literal-string
key: "last-name",
label: t("last-name"),
value: userDetails.data.last_name ?? "",
},
{
// eslint-disable-next-line i18next/no-literal-string
key: "email",
label: t("email"),
value: userDetails.data.email ?? "",
},
{
// eslint-disable-next-line i18next/no-literal-string
key: "user-id",
label: t("user-id"),
colSpan: 3,
value: (
<HideTextInSystemTests
text={getSubmissionInfo.data.exercise_slide_submission.user_id}
testPlaceholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
/>
),
},
],
},
...(user
? [
{
title: t("user-information"),
items: [
{
// eslint-disable-next-line i18next/no-literal-string
key: "first-name",
label: t("first-name"),
value: user.first_name,
},
{
// eslint-disable-next-line i18next/no-literal-string
key: "last-name",
label: t("last-name"),
value: user.last_name,
},
{
// eslint-disable-next-line i18next/no-literal-string
key: "email",
label: t("email"),
value: user.email,
},
{
// eslint-disable-next-line i18next/no-literal-string
key: "user-id",
label: t("user-id"),
colSpan: 3,
value: (
<HideTextInSystemTests
text={getSubmissionInfo.data.exercise_slide_submission.user_id}
testPlaceholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
/>
),
},
],
},
]
: []),
]}
actionButtons={
getSubmissionInfo.data?.exercise.course_id
Expand All @@ -231,6 +238,16 @@ const Submission: React.FC = () => {
/>
)}

{userDetails.isSuccess && userDetailsNotFound && (
<DeletedUserNotice
userId={getSubmissionInfo.data.exercise_slide_submission.user_id}
className={css`
max-width: ${narrowContainerWidthRem}rem;
margin: 0 auto 2rem auto;
`}
/>
)}

{/* User Details Error Banner */}
{userDetails.isError && (
<ErrorBanner
Expand Down
41 changes: 41 additions & 0 deletions services/main-frontend/src/components/DeletedUserNotice.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"use client"

import { css } from "@emotion/css"
import React from "react"
import { useTranslation } from "react-i18next"

import GenericInfobox from "@/shared-module/common/components/GenericInfobox"

interface DeletedUserNoticeProps {
userId: string
className?: string
}

const textContainerStyle = css`
display: flex;
flex-direction: column;
gap: 0.25rem;
`

const userIdStyle = css`
font-size: 0.9rem;
`

const DeletedUserNotice: React.FC<DeletedUserNoticeProps> = ({ userId, className }) => {
const { t } = useTranslation()

return (
<div className={className}>
<GenericInfobox>
<div className={textContainerStyle}>
<span>{t("message-user-likely-deleted")}</span>
<span className={userIdStyle}>
{t("label-user-id")}: {userId}
</span>
</div>
</GenericInfobox>
</div>
)
}

export default DeletedUserNotice
13 changes: 7 additions & 6 deletions services/main-frontend/src/components/UserDisplay/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { CourseProgressSection } from "./CourseProgressSection"
import { UserDetailsContent } from "./UserDetailsContent"
import { UserDetailsPopover } from "./UserDetailsPopover"

import { useUserDetails } from "@/hooks/useUserDetails"
import { extractUserDetail, useUserDetails } from "@/hooks/useUserDetails"
import Button from "@/shared-module/common/components/Button"
import ErrorBanner from "@/shared-module/common/components/ErrorBanner"
import Spinner from "@/shared-module/common/components/Spinner"
Expand Down Expand Up @@ -90,6 +90,7 @@ const UserDisplay: React.FC<UserDisplayProps> = ({ userId, courseId }) => {
gcTime: 24 * 60 * 60 * 1000,
refetchOnWindowFocus: false,
})
const user = extractUserDetail(data)

const { buttonProps } = useButton(
mergeProps(triggerProps, {
Expand All @@ -106,7 +107,7 @@ const UserDisplay: React.FC<UserDisplayProps> = ({ userId, courseId }) => {
return <ErrorBanner error={error} />
}

if (!data) {
if (!user) {
return (
<span
className={css`
Expand All @@ -120,9 +121,9 @@ const UserDisplay: React.FC<UserDisplayProps> = ({ userId, courseId }) => {
)
}

const firstName = data.first_name?.trim() ?? ""
const lastName = data.last_name?.trim() ?? ""
const email = data.email?.trim() ?? ""
const firstName = user.first_name?.trim() ?? ""
const lastName = user.last_name?.trim() ?? ""
const email = user.email?.trim() ?? ""

const displayText =
hasName(firstName) || hasName(lastName)
Expand Down Expand Up @@ -179,7 +180,7 @@ const UserDisplay: React.FC<UserDisplayProps> = ({ userId, courseId }) => {
className={popoverStyle}
aria-label={t("header-user-details")}
>
<UserDetailsContent data={data} userId={userId} />
<UserDetailsContent data={user} userId={userId} />
{courseId && <CourseProgressSection courseId={courseId} userId={userId} />}
{courseId && (
<div
Expand Down
Loading
Loading