Skip to content

Commit 92456c5

Browse files
authored
Students tab fixes (#1614)
* Refactor Course Students Page and Floating Header Table styles for improved responsiveness and accessibility. Adjusted padding, widths, and overflow properties across components to enhance layout on various screen sizes. Removed unnecessary state management and optimized column definitions in the FloatingHeaderTable. Updated styles for better visual consistency and user experience. * Handle nones * Refactor CopyButton and Clipboard Utility - Updated CopyButton component to use new `useCopyHtmlContentToClipboard` hook for improved HTML content copying functionality. - Refactored utility functions in `utils.ts` to streamline the copying process, including handling HTML content and fallback methods. - Adjusted tests to reflect changes in the copying functionality and updated references to the new hook. - Enhanced clipboard functionality to ensure better handling of HTML content and improved user experience. * Implement user search functionality across course student tabs - Added search capabilities to the User, Completions, and Progress tabs, allowing users to filter results based on input. - Updated CourseUserInfo structure to replace the name field with first_name and last_name for better clarity. - Enhanced the backend query to support the new data structure and ensure compatibility with the frontend search features. - Refactored relevant components to utilize the new search query state and improve user experience. - Introduced a new JSON query file for fetching user details from the database. * Add student tab visibility based on global admin authorization - Integrated `useAuthorizeMultiple` hook to check for global admin permissions. - Conditionally rendered the students tab and associated page based on the user's admin status. - Enhanced user experience by ensuring only authorized users can access the students tab in the course management page. * Update package manager version to [email protected] and comment out minimumReleaseAge in workspace configuration files across multiple services for improved consistency and to reduce supply-chain risk. * Fixes * Update snapshots
1 parent e22ff59 commit 92456c5

File tree

79 files changed

+1207
-554
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+1207
-554
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "",
55
"homepage": "https://github.com/rage/secret-project-331#readme",
66
"main": "index.js",
7-
"packageManager": "pnpm@10.17.1",
7+
"packageManager": "pnpm@10.26.2",
88
"type": "module",
99
"bugs": {
1010
"url": "https://github.com/rage/secret-project-331/issues"

pnpm-workspace.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ updateNotifier: false
1818
sharedWorkspaceLockfile: false
1919

2020
# Delay brand-new releases to reduce supply-chain risk (minutes)
21-
minimumReleaseAge: 1440
21+
# minimumReleaseAge: 1440
2222
# If you want to exclude some packages from the minimumReleaseAge, you can do it here
2323
minimumReleaseAgeExclude: ["next", "react", "react-dom", "@next/*"]
2424

services/cms/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "material-editor",
33
"version": "0.1.0",
4-
"packageManager": "pnpm@10.17.1",
4+
"packageManager": "pnpm@10.26.2",
55
"private": true,
66
"scripts": {
77
"build": "NODE_ENV=production next build",

services/cms/pnpm-workspace.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ linkWorkspacePackages: true
44

55
managePackageManagerVersions: true
66

7-
minimumReleaseAge: 1440
7+
# minimumReleaseAge: 1440
88
minimumReleaseAgeExclude: ["next", "react", "@next/*"]
99

1010
nodeVersion: 22.20.0

services/course-material/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "course-material",
33
"version": "1.0.0",
4-
"packageManager": "pnpm@10.17.1",
4+
"packageManager": "pnpm@10.26.2",
55
"scripts": {
66
"build": "NODE_ENV=production next build",
77
"dev": "next dev --port 3003 --turbopack",

services/course-material/pnpm-workspace.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ updateNotifier: false
1818
sharedWorkspaceLockfile: false
1919

2020
# Delay brand-new releases to reduce supply-chain risk (minutes)
21-
minimumReleaseAge: 1440
21+
# minimumReleaseAge: 1440
2222
# If you want to exclude some packages from the minimumReleaseAge, you can do it here
2323
minimumReleaseAgeExclude: ["next", "react", "react-dom", "@next/*"]
2424

services/course-material/src/components/ContentRenderer/core/formatting/CodeBlock/CopyButton.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { useCallback, useEffect, useRef, useState } from "react"
55
import { useTranslation } from "react-i18next"
66
import { animated, SpringValue, useTransition } from "react-spring"
77

8-
import { useCopyToClipboard } from "./utils"
8+
import { useCopyHtmlContentToClipboard } from "./utils"
99

1010
import CopyIcon from "@/img/copy.svg"
1111
import { baseTheme } from "@/shared-module/common/styles"
@@ -112,7 +112,7 @@ export const CopyButton: React.FC<CopyButtonProps> = ({ content }) => {
112112
const { t } = useTranslation()
113113
const [copyStatus, setCopyStatus] = useState<CopyStatus>(COPY_STATUS.DEFAULT)
114114
const [showTooltip, setShowTooltip] = useState(false)
115-
const copyToClipboard = useCopyToClipboard(content)
115+
const copyToClipboard = useCopyHtmlContentToClipboard(content)
116116

117117
const buttonRef = useRef<HTMLButtonElement>(null)
118118
const tooltipRef = useRef<HTMLDivElement>(null)

services/course-material/src/components/ContentRenderer/core/formatting/CodeBlock/__tests__/utils.test.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { renderHook } from "@testing-library/react"
22

3-
import { decodeHtmlEntities, replaceBrTagsWithNewlines, useCopyToClipboard } from "../utils"
3+
import {
4+
decodeHtmlEntities,
5+
replaceBrTagsWithNewlines,
6+
useCopyHtmlContentToClipboard,
7+
} from "../utils"
48

59
describe("replaceBrTagsWithNewlines", () => {
610
it("should return null when input is null", () => {
@@ -81,7 +85,7 @@ describe("decodeHtmlEntities", () => {
8185
})
8286
})
8387

84-
describe("useCopyToClipboard", () => {
88+
describe("useCopyHtmlContentToClipboard", () => {
8589
const originalConsole = {
8690
log: console.log,
8791
warn: console.warn,
@@ -118,7 +122,7 @@ describe("useCopyToClipboard", () => {
118122
})
119123

120124
it("should handle basic text copying", async () => {
121-
const { result } = renderHook(() => useCopyToClipboard("Hello world"))
125+
const { result } = renderHook(() => useCopyHtmlContentToClipboard("Hello world"))
122126
const copyToClipboard = result.current
123127

124128
const success = await copyToClipboard()
@@ -129,7 +133,7 @@ describe("useCopyToClipboard", () => {
129133

130134
it("should decode HTML entities and convert BR tags", async () => {
131135
const { result } = renderHook(() =>
132-
useCopyToClipboard("if (x &lt; 10) {<br> return true;<br>}"),
136+
useCopyHtmlContentToClipboard("if (x &lt; 10) {<br> return true;<br>}"),
133137
)
134138
const copyToClipboard = result.current
135139

@@ -139,7 +143,9 @@ describe("useCopyToClipboard", () => {
139143
})
140144

141145
it("should preserve encoded <br> tags as text", async () => {
142-
const { result } = renderHook(() => useCopyToClipboard("Example: &lt;br&gt; tag<br>Next line"))
146+
const { result } = renderHook(() =>
147+
useCopyHtmlContentToClipboard("Example: &lt;br&gt; tag<br>Next line"),
148+
)
143149
const copyToClipboard = result.current
144150

145151
await copyToClipboard()
@@ -149,7 +155,7 @@ describe("useCopyToClipboard", () => {
149155

150156
it("should preserve literal \\n sequences", async () => {
151157
const { result } = renderHook(() =>
152-
useCopyToClipboard("console.log('\\n');<br>const x = '\\n';"),
158+
useCopyHtmlContentToClipboard("console.log('\\n');<br>const x = '\\n';"),
153159
)
154160
const copyToClipboard = result.current
155161

@@ -169,7 +175,7 @@ describe("useCopyToClipboard", () => {
169175
configurable: true,
170176
})
171177

172-
const { result } = renderHook(() => useCopyToClipboard("Test content"))
178+
const { result } = renderHook(() => useCopyHtmlContentToClipboard("Test content"))
173179
const copyToClipboard = result.current
174180

175181
const success = await copyToClipboard()
@@ -184,7 +190,7 @@ describe("useCopyToClipboard", () => {
184190
configurable: true,
185191
})
186192

187-
const { result } = renderHook(() => useCopyToClipboard("Test content"))
193+
const { result } = renderHook(() => useCopyHtmlContentToClipboard("Test content"))
188194
const copyToClipboard = result.current
189195

190196
const success = await copyToClipboard()
@@ -202,7 +208,7 @@ describe("useCopyToClipboard", () => {
202208
})
203209
document.execCommand = jest.fn(() => false)
204210

205-
const { result } = renderHook(() => useCopyToClipboard("Test content"))
211+
const { result } = renderHook(() => useCopyHtmlContentToClipboard("Test content"))
206212
const copyToClipboard = result.current
207213

208214
const success = await copyToClipboard()
Lines changed: 10 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback } from "react"
1+
import { useCopyToClipboard as useCopyToClipboardBase } from "@/shared-module/common/hooks/useCopyToClipboard"
22

33
/**
44
* Decodes HTML entities in a string
@@ -21,78 +21,15 @@ export function replaceBrTagsWithNewlines(html: string | null | undefined): type
2121
}
2222

2323
/**
24-
* Fallback copy method using execCommand.
25-
* @throws Error if copy fails
24+
* Returns a callback for copying HTML content to clipboard.
25+
* Processes HTML content by replacing BR tags with newlines and decoding HTML entities before copying.
26+
* @param htmlContent - The HTML content to copy (will be processed before copying)
27+
* @returns A function that when called attempts to copy the processed text and returns true if successful
2628
*/
27-
function copyWithFallback(text: string): void {
28-
const textArea = document.createElement("textarea")
29-
textArea.value = text
30-
document.body.appendChild(textArea)
31-
textArea.select()
29+
export function useCopyHtmlContentToClipboard(htmlContent: string): () => Promise<boolean> {
30+
const withoutNewLines = replaceBrTagsWithNewlines(htmlContent) ?? ""
31+
const processedText = decodeHtmlEntities(withoutNewLines)
32+
const baseCopyToClipboard = useCopyToClipboardBase(processedText)
3233

33-
const successful = document.execCommand("copy")
34-
document.body.removeChild(textArea)
35-
if (!successful) {
36-
throw new Error("Copy failed")
37-
}
38-
console.info(`[Copy] Success using legacy method:\n${text}`)
39-
}
40-
41-
/**
42-
* Attempts to copy text using the Clipboard API.
43-
* @throws Error if copy fails
44-
*/
45-
async function copyWithClipboardApi(text: string): Promise<void> {
46-
await navigator.clipboard.writeText(text)
47-
console.info(`[Copy] Success using Clipboard API:\n${text}`)
48-
}
49-
50-
/**
51-
* Returns a callback for copying code to clipboard.
52-
* @returns A function that when called attempts to copy text and returns true if successful
53-
*/
54-
export function useCopyToClipboard(content: string): () => Promise<boolean> {
55-
const copyToClipboard = useCallback(async (): Promise<boolean> => {
56-
const withoutNewLines = replaceBrTagsWithNewlines(content) ?? ""
57-
const textToCopy = decodeHtmlEntities(withoutNewLines)
58-
59-
try {
60-
if (navigator.clipboard) {
61-
try {
62-
await copyWithClipboardApi(textToCopy)
63-
return true
64-
} catch (error) {
65-
const isSecureContext = window.isSecureContext
66-
const isPermissionError = error instanceof Error && error.name === "NotAllowedError"
67-
68-
if (!isSecureContext) {
69-
console.warn(
70-
"[Copy] Unable to use Clipboard API - HTTPS required. Trying legacy method.",
71-
)
72-
} else if (isPermissionError) {
73-
console.warn(
74-
`[Copy] Unable to use Clipboard API - Permission denied: ${error instanceof Error ? error.message : String(error)}. Trying legacy method.`,
75-
)
76-
} else {
77-
console.warn(
78-
`[Copy] Unable to use Clipboard API - Unknown error: ${error instanceof Error ? error.message : String(error)}. Trying legacy method.`,
79-
)
80-
}
81-
copyWithFallback(textToCopy)
82-
return true
83-
}
84-
} else {
85-
console.warn("[Copy] Clipboard API not available. Trying legacy method.")
86-
copyWithFallback(textToCopy)
87-
return true
88-
}
89-
} catch (error) {
90-
console.error(
91-
`[Copy Failed] Copy operation failed. Please try selecting the text manually and using Ctrl+C/Cmd+C. Error: ${error}`,
92-
)
93-
return false
94-
}
95-
}, [content])
96-
97-
return copyToClipboard
34+
return baseCopyToClipboard
9835
}

services/example-exercise/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "example-exercise",
33
"version": "1.0.1",
4-
"packageManager": "pnpm@10.17.1",
4+
"packageManager": "pnpm@10.26.2",
55
"scripts": {
66
"build": "NODE_ENV=production next build",
77
"dev": "next dev --port 3002 --turbopack",

0 commit comments

Comments
 (0)