@openedx/frontend-app-instructor-dashboard 1.0.0-alpha.23 → 1.0.0-alpha.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState, useImperativeHandle, forwardRef } from 'react';
3
+ import { isAxiosError } from 'axios';
3
4
  import { useParams } from 'react-router-dom';
4
5
  import { Button, Form, Icon, OverlayTrigger, Tooltip, useToggle } from '@openedx/paragon';
5
6
  import { InfoOutline, SpinnerIcon } from '@openedx/paragon/icons';
@@ -8,6 +9,7 @@ import messages from './messages';
8
9
  import { useDebouncedFilter } from '../hooks/useDebouncedFilter';
9
10
  import { useProblemDetails } from '../data/apiHook';
10
11
  const SpecifyProblemField = forwardRef(({ buttonLabel, disabled, fieldLabel, problemResponsesError, usernameOrEmail = '', onClickSelect, }, ref) => {
12
+ var _a;
11
13
  const intl = useIntl();
12
14
  const { courseId = '' } = useParams();
13
15
  const [problemLocation, setProblemLocation] = useState('');
@@ -16,7 +18,7 @@ const SpecifyProblemField = forwardRef(({ buttonLabel, disabled, fieldLabel, pro
16
18
  filterValue: problemLocation,
17
19
  setFilter: setProblemLocation,
18
20
  });
19
- const { data = { breadcrumbs: [], name: '', id: '' }, refetch } = useProblemDetails(courseId, inputValue, usernameOrEmail);
21
+ const { data = { breadcrumbs: [], name: '', id: '' }, refetch, error } = useProblemDetails(courseId, inputValue, usernameOrEmail);
20
22
  useImperativeHandle(ref, () => ({
21
23
  reset: () => {
22
24
  resetFilter();
@@ -30,16 +32,20 @@ const SpecifyProblemField = forwardRef(({ buttonLabel, disabled, fieldLabel, pro
30
32
  }
31
33
  };
32
34
  const handleClick = (event) => {
33
- refetch().then(() => {
34
- onClickSelect(inputValue, event);
35
- enableShowSelectedLocation();
36
- });
35
+ if (inputValue) {
36
+ refetch().then(() => {
37
+ onClickSelect(inputValue, event);
38
+ enableShowSelectedLocation();
39
+ });
40
+ }
37
41
  };
38
42
  return (_jsxs(Form.Group, { className: "mb-0", isInvalid: !!problemResponsesError, size: "sm", children: [_jsx(Form.Label, { className: "d-flex align-content-end align-items-center gap-2 text-primary-500", children: showSelectedLocation ? intl.formatMessage(messages.selectedProblem)
39
- : (_jsxs(_Fragment, { children: [fieldLabel, _jsx(OverlayTrigger, { placement: "top", overlay: (_jsx(Tooltip, { id: "problem-location-tooltip", className: "info-tooltip", children: intl.formatMessage(messages.problemLocationTooltip) })), children: _jsx(Icon, { src: InfoOutline, size: "sm", "aria-label": intl.formatMessage(messages.problemLocationInfoIconLabel) }) })] })) }), _jsx("div", { className: "d-flex align-items-center", children: showSelectedLocation && data ? (_jsxs("div", { className: "d-flex gap-3 align-items-center col-8 p-0", children: [_jsxs("div", { className: "d-block w-100", children: [_jsx("p", { className: "x-small mb-0 text-primary-500 text-truncate", children: data.breadcrumbs
43
+ : (_jsxs(_Fragment, { children: [fieldLabel, _jsx(OverlayTrigger, { placement: "top", overlay: (_jsx(Tooltip, { id: "problem-location-tooltip", className: "info-tooltip", children: intl.formatMessage(messages.problemLocationTooltip) })), children: _jsx(Icon, { src: InfoOutline, size: "sm", "aria-label": intl.formatMessage(messages.problemLocationInfoIconLabel) }) })] })) }), _jsx("div", { className: "d-flex align-items-center", children: showSelectedLocation && data && !error ? (_jsxs("div", { className: "d-flex gap-3 align-items-center col-8 p-0", children: [_jsxs("div", { className: "d-block w-100", children: [_jsx("p", { className: "x-small mb-0 text-primary-500 text-truncate", children: data.breadcrumbs
40
44
  .slice(1, -1)
41
45
  .map(breadcrumb => breadcrumb.displayName)
42
- .join(' > ') }), _jsx("p", { className: "text-primary-500 mb-0", children: data.name }), _jsx("p", { className: "x-small text-gray-700 text-truncate mb-0", children: data.id })] }), _jsx(Button, { iconBefore: SpinnerIcon, onClick: disableShowSelectedLocation, children: intl.formatMessage(messages.change) })] })) : (_jsxs(_Fragment, { children: [_jsx(Form.Control, { type: "text", placeholder: intl.formatMessage(messages.problemLocationPlaceholder), value: inputValue, onChange: handleInputChange, className: "flex-grow-1", size: "md" }), problemResponsesError && (_jsx(Form.Control.Feedback, { type: "invalid", children: problemResponsesError })), _jsx(Button, { variant: "primary", onClick: handleClick, disabled: disabled || !inputValue, className: "text-nowrap", children: buttonLabel })] })) })] }));
46
+ .join(' > ') }), _jsx("p", { className: "text-primary-500 mb-0", children: data.name }), _jsx("p", { className: "x-small text-gray-700 text-truncate mb-0", children: data.id })] }), _jsx(Button, { iconBefore: SpinnerIcon, onClick: disableShowSelectedLocation, children: intl.formatMessage(messages.change) })] })) : (_jsxs(_Fragment, { children: [_jsx(Form.Control, { type: "text", placeholder: intl.formatMessage(messages.problemLocationPlaceholder), value: inputValue, onChange: handleInputChange, className: "flex-grow-1", size: "md" }), problemResponsesError && (_jsx(Form.Control.Feedback, { type: "invalid", children: problemResponsesError })), _jsx(Button, { variant: "primary", onClick: handleClick, disabled: disabled || !inputValue, className: "text-nowrap", children: buttonLabel })] })) }), showSelectedLocation && error
47
+ && isAxiosError(error)
48
+ && (((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 400) && (_jsx("p", { className: "text-danger-500 mb-0 x-small mt-2", children: intl.formatMessage(messages.problemNotFound, { identifier: inputValue }) }))] }));
43
49
  });
44
50
  SpecifyProblemField.displayName = 'SpecifyProblemField';
45
51
  export default SpecifyProblemField;
@@ -1 +1 @@
1
- {"version":3,"file":"SpecifyProblemField.js","sourceRoot":"","sources":["../../src/components/SpecifyProblemField.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useState, useImperativeHandle, forwardRef } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { Button, Form, Icon, OverlayTrigger, Tooltip, useToggle } from '@openedx/paragon';\nimport { InfoOutline, SpinnerIcon } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport messages from './messages';\nimport { useDebouncedFilter } from '@src/hooks/useDebouncedFilter';\nimport { useProblemDetails } from '@src/data/apiHook';\n\ninterface SpecifyProblemFieldProps {\n buttonLabel: string,\n disabled?: boolean,\n fieldLabel: string,\n problemResponsesError?: string,\n usernameOrEmail?: string,\n onClickSelect: (problemLocation: string, event: React.MouseEvent<HTMLButtonElement>) => void,\n}\n\ninterface SpecifyProblemFieldRef {\n reset: () => void,\n}\n\nconst SpecifyProblemField = forwardRef<SpecifyProblemFieldRef, SpecifyProblemFieldProps>(({\n buttonLabel,\n disabled,\n fieldLabel,\n problemResponsesError,\n usernameOrEmail = '',\n onClickSelect,\n}, ref) => {\n const intl = useIntl();\n const { courseId = '' } = useParams<{ courseId: string }>();\n const [problemLocation, setProblemLocation] = useState('');\n const [showSelectedLocation, enableShowSelectedLocation, disableShowSelectedLocation] = useToggle(false);\n\n const { inputValue, handleChange, resetFilter } = useDebouncedFilter({\n filterValue: problemLocation,\n setFilter: setProblemLocation,\n });\n const { data = { breadcrumbs: [], name: '', id: '' }, refetch } = useProblemDetails(courseId, inputValue, usernameOrEmail);\n\n useImperativeHandle(ref, () => ({\n reset: () => {\n resetFilter();\n disableShowSelectedLocation();\n }\n }));\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n handleChange(e.target.value);\n\n if (showSelectedLocation) {\n disableShowSelectedLocation();\n }\n };\n\n const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n refetch().then(() => {\n onClickSelect(inputValue, event);\n enableShowSelectedLocation();\n });\n };\n\n return (\n <Form.Group className=\"mb-0\" isInvalid={!!problemResponsesError} size=\"sm\">\n <Form.Label className=\"d-flex align-content-end align-items-center gap-2 text-primary-500\">\n {showSelectedLocation ? intl.formatMessage(messages.selectedProblem)\n : (\n <>\n {fieldLabel}\n <OverlayTrigger\n placement=\"top\"\n overlay={(\n <Tooltip id=\"problem-location-tooltip\" className=\"info-tooltip\">\n {intl.formatMessage(messages.problemLocationTooltip)}\n </Tooltip>\n )}\n >\n <Icon src={InfoOutline} size=\"sm\" aria-label={intl.formatMessage(messages.problemLocationInfoIconLabel)} />\n </OverlayTrigger>\n </>\n )}\n </Form.Label>\n <div className=\"d-flex align-items-center\">\n {showSelectedLocation && data ? (\n <div className=\"d-flex gap-3 align-items-center col-8 p-0\">\n <div className=\"d-block w-100\">\n <p className=\"x-small mb-0 text-primary-500 text-truncate\">\n {data.breadcrumbs\n .slice(1, -1)\n .map(breadcrumb => breadcrumb.displayName)\n .join(' > ')}\n </p>\n <p className=\"text-primary-500 mb-0\">{data.name}</p>\n <p className=\"x-small text-gray-700 text-truncate mb-0\">{data.id}</p>\n </div>\n <Button iconBefore={SpinnerIcon} onClick={disableShowSelectedLocation}>{intl.formatMessage(messages.change)}</Button>\n </div>\n ) : (\n <>\n <Form.Control\n type=\"text\"\n placeholder={intl.formatMessage(messages.problemLocationPlaceholder)}\n value={inputValue}\n onChange={handleInputChange}\n className=\"flex-grow-1\"\n size=\"md\"\n />\n {problemResponsesError && (\n <Form.Control.Feedback type=\"invalid\">\n {problemResponsesError}\n </Form.Control.Feedback>\n )}\n <Button\n variant=\"primary\"\n onClick={handleClick}\n disabled={disabled || !inputValue}\n className=\"text-nowrap\"\n >\n {buttonLabel}\n </Button>\n </>\n )}\n </div>\n </Form.Group>\n );\n});\n\nSpecifyProblemField.displayName = 'SpecifyProblemField';\n\nexport default SpecifyProblemField;\n"]}
1
+ {"version":3,"file":"SpecifyProblemField.js","sourceRoot":"","sources":["../../src/components/SpecifyProblemField.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useState, useImperativeHandle, forwardRef } from 'react';\nimport { isAxiosError } from 'axios';\nimport { useParams } from 'react-router-dom';\nimport { Button, Form, Icon, OverlayTrigger, Tooltip, useToggle } from '@openedx/paragon';\nimport { InfoOutline, SpinnerIcon } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport messages from './messages';\nimport { useDebouncedFilter } from '@src/hooks/useDebouncedFilter';\nimport { useProblemDetails } from '@src/data/apiHook';\n\ninterface SpecifyProblemFieldProps {\n buttonLabel: string,\n disabled?: boolean,\n fieldLabel: string,\n problemResponsesError?: string,\n usernameOrEmail?: string,\n onClickSelect: (problemLocation: string, event: React.MouseEvent<HTMLButtonElement>) => void,\n}\n\ninterface SpecifyProblemFieldRef {\n reset: () => void,\n}\n\nconst SpecifyProblemField = forwardRef<SpecifyProblemFieldRef, SpecifyProblemFieldProps>(({\n buttonLabel,\n disabled,\n fieldLabel,\n problemResponsesError,\n usernameOrEmail = '',\n onClickSelect,\n}, ref) => {\n const intl = useIntl();\n const { courseId = '' } = useParams<{ courseId: string }>();\n const [problemLocation, setProblemLocation] = useState('');\n const [showSelectedLocation, enableShowSelectedLocation, disableShowSelectedLocation] = useToggle(false);\n\n const { inputValue, handleChange, resetFilter } = useDebouncedFilter({\n filterValue: problemLocation,\n setFilter: setProblemLocation,\n });\n const { data = { breadcrumbs: [], name: '', id: '' }, refetch, error } = useProblemDetails(courseId, inputValue, usernameOrEmail);\n\n useImperativeHandle(ref, () => ({\n reset: () => {\n resetFilter();\n disableShowSelectedLocation();\n }\n }));\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n handleChange(e.target.value);\n\n if (showSelectedLocation) {\n disableShowSelectedLocation();\n }\n };\n\n const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n if (inputValue) {\n refetch().then(() => {\n onClickSelect(inputValue, event);\n enableShowSelectedLocation();\n });\n }\n };\n\n return (\n <Form.Group className=\"mb-0\" isInvalid={!!problemResponsesError} size=\"sm\">\n <Form.Label className=\"d-flex align-content-end align-items-center gap-2 text-primary-500\">\n {showSelectedLocation ? intl.formatMessage(messages.selectedProblem)\n : (\n <>\n {fieldLabel}\n <OverlayTrigger\n placement=\"top\"\n overlay={(\n <Tooltip id=\"problem-location-tooltip\" className=\"info-tooltip\">\n {intl.formatMessage(messages.problemLocationTooltip)}\n </Tooltip>\n )}\n >\n <Icon src={InfoOutline} size=\"sm\" aria-label={intl.formatMessage(messages.problemLocationInfoIconLabel)} />\n </OverlayTrigger>\n </>\n )}\n </Form.Label>\n <div className=\"d-flex align-items-center\">\n {showSelectedLocation && data && !error ? (\n <div className=\"d-flex gap-3 align-items-center col-8 p-0\">\n <div className=\"d-block w-100\">\n <p className=\"x-small mb-0 text-primary-500 text-truncate\">\n {data.breadcrumbs\n .slice(1, -1)\n .map(breadcrumb => breadcrumb.displayName)\n .join(' > ')}\n </p>\n <p className=\"text-primary-500 mb-0\">{data.name}</p>\n <p className=\"x-small text-gray-700 text-truncate mb-0\">{data.id}</p>\n </div>\n <Button iconBefore={SpinnerIcon} onClick={disableShowSelectedLocation}>{intl.formatMessage(messages.change)}</Button>\n </div>\n ) : (\n <>\n <Form.Control\n type=\"text\"\n placeholder={intl.formatMessage(messages.problemLocationPlaceholder)}\n value={inputValue}\n onChange={handleInputChange}\n className=\"flex-grow-1\"\n size=\"md\"\n />\n {problemResponsesError && (\n <Form.Control.Feedback type=\"invalid\">\n {problemResponsesError}\n </Form.Control.Feedback>\n )}\n <Button\n variant=\"primary\"\n onClick={handleClick}\n disabled={disabled || !inputValue}\n className=\"text-nowrap\"\n >\n {buttonLabel}\n </Button>\n </>\n )}\n </div>\n {showSelectedLocation && error\n && isAxiosError(error)\n && (error.response?.status === 400) && (\n <p className=\"text-danger-500 mb-0 x-small mt-2\">\n {intl.formatMessage(messages.problemNotFound, { identifier: inputValue })}\n </p>\n )}\n </Form.Group>\n );\n});\n\nSpecifyProblemField.displayName = 'SpecifyProblemField';\n\nexport default SpecifyProblemField;\n"]}
@@ -114,6 +114,11 @@ declare const messages: {
114
114
  defaultMessage: string;
115
115
  description: string;
116
116
  };
117
+ problemNotFound: {
118
+ id: string;
119
+ defaultMessage: string;
120
+ description: string;
121
+ };
117
122
  searchPlaceholder: {
118
123
  id: string;
119
124
  defaultMessage: string;
@@ -115,6 +115,11 @@ const messages = defineMessages({
115
115
  defaultMessage: 'Could not find student matching identifier: {identifier}',
116
116
  description: 'Error message displayed when a learner cannot be found based on the provided identifier (email or username)',
117
117
  },
118
+ problemNotFound: {
119
+ id: 'instruct.specifyProblemField.problemNotFound',
120
+ defaultMessage: 'Could not find problem matching identifier: {identifier}',
121
+ description: 'Error message displayed when a problem cannot be found based on the provided identifier',
122
+ },
118
123
  searchPlaceholder: {
119
124
  id: 'instruct.usernameFilter.searchPlaceholder',
120
125
  defaultMessage: 'Search By Username or Email',
@@ -1 +1 @@
1
- {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/components/messages.ts"],"names":[],"mappings":"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","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n select: {\n id: 'instruct.specifyLearner.select',\n defaultMessage: 'Select',\n description: 'Label for select dropdown in specify learner field',\n },\n specifyLearner: {\n id: 'instruct.specifyLearner.label',\n defaultMessage: 'Specify Learner:',\n description: 'Label for specify learner field',\n },\n specifyLearnerPlaceholder: {\n id: 'instruct.specifyLearner.placeholder',\n defaultMessage: 'Learner email address or username',\n description: 'Placeholder text for specify learner input field',\n },\n pageNotFoundHeader: {\n id: 'instruct.pageNotFound.header',\n defaultMessage: 'Page not found',\n description: 'Header for page not found error',\n },\n pageNotFoundBody: {\n id: 'instruct.pageNotFound.body',\n defaultMessage: \"The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again.\",\n description: 'Body text for page not found error',\n },\n pendingTasksTitle: {\n id: 'instruct.pendingTasks.section.title',\n defaultMessage: 'Pending Tasks',\n description: 'Title for the pending tasks section',\n },\n noTasksMessage: {\n id: 'instruct.pendingTasks.section.noTasks',\n defaultMessage: 'No tasks currently running.',\n description: 'Message displayed when there are no pending tasks',\n },\n taskTypeColumnName: {\n id: 'instruct.pendingTasks.table.column.taskType',\n defaultMessage: 'Task Type',\n description: 'Column name for task type in pending tasks table',\n },\n taskInputColumnName: {\n id: 'instruct.pendingTasks.table.column.taskInput',\n defaultMessage: 'Task Input',\n description: 'Column name for task input in pending tasks table',\n },\n taskIdColumnName: {\n id: 'instruct.pendingTasks.table.column.taskId',\n defaultMessage: 'Task ID',\n description: 'Column name for task ID in pending tasks table',\n },\n requesterColumnName: {\n id: 'instruct.pendingTasks.table.column.requester',\n defaultMessage: 'Requester',\n description: 'Column name for requester in pending tasks table',\n },\n taskStateColumnName: {\n id: 'instruct.pendingTasks.table.column.taskState',\n defaultMessage: 'Task State',\n description: 'Column name for task state in pending tasks table',\n },\n createdColumnName: {\n id: 'instruct.pendingTasks.table.column.created',\n defaultMessage: 'Created',\n description: 'Column name for created date in pending tasks table',\n },\n taskOutputColumnName: {\n id: 'instruct.pendingTasks.table.column.taskOutput',\n defaultMessage: 'Task Output',\n description: 'Column name for task output in pending tasks table',\n },\n durationColumnName: {\n id: 'instruct.pendingTasks.table.column.duration',\n defaultMessage: 'Duration (sec)',\n description: 'Column name for duration in pending tasks table',\n },\n statusColumnName: {\n id: 'instruct.pendingTasks.table.column.status',\n defaultMessage: 'Status',\n description: 'Column name for status in pending tasks table',\n },\n taskMessageColumnName: {\n id: 'instruct.pendingTasks.table.column.taskMessage',\n defaultMessage: 'Task Message',\n description: 'Column name for task message in pending tasks table',\n },\n downloadCSVTitle: {\n id: 'instruct.csvComponent.downloadCSVTitle',\n defaultMessage: 'Upload CSV File',\n description: 'Title for the upload CSV file section'\n },\n downloadCSVDescription: {\n id: 'instruct.csvComponent.downloadCSVDescription',\n defaultMessage: 'Only properly formatted CSV files will be accepted',\n description: 'Description for the upload CSV file section'\n },\n viewCSVTemplate: {\n id: 'instruct.csvComponent.viewCSVTemplate',\n defaultMessage: 'View Template',\n description: 'Label for the view CSV template link'\n },\n uploadingFileMessage: {\n id: 'instruct.csvComponent.uploadingFileMessage',\n defaultMessage: 'File chosen: {fileName}',\n description: 'Message displayed when a file is being uploaded, with the file name included'\n },\n change: {\n id: 'instruct.specifyLearner.change',\n defaultMessage: 'Change',\n description: 'Label for change button in specify learner field',\n },\n learnerNotFound: {\n id: 'instruct.specifyLearner.learnerNotFound',\n defaultMessage: 'Could not find student matching identifier: {identifier}',\n description: 'Error message displayed when a learner cannot be found based on the provided identifier (email or username)',\n },\n searchPlaceholder: {\n id: 'instruct.usernameFilter.searchPlaceholder',\n defaultMessage: 'Search By Username or Email',\n description: 'Placeholder text for the username filter input',\n },\n problemLocationPlaceholder: {\n id: 'instruct.specifyProblemField.locationPlaceholder',\n defaultMessage: 'Problem location',\n description: 'Placeholder text for problem location input',\n },\n problemLocationInfoIconLabel: {\n id: 'instruct.specifyProblemField.infoIconLabel',\n defaultMessage: 'Example format for problem location',\n description: 'Aria label for the info icon next to the problem location input',\n },\n problemLocationTooltip: {\n id: 'instruct.specifyProblemField.locationTooltip',\n defaultMessage: 'Example: block-v1:edX+DemoX+2015+type@problem+block@618c5933b8b544e4a4cc103d3e508378',\n description: 'Tooltip text showing an example problem location format',\n },\n selectedProblem: {\n id: 'instruct.specifyProblemField.selectedProblem',\n defaultMessage: 'Selected Problem:',\n description: 'Label for specify problem field when a problem has been selected',\n },\n learnerNotEnrolled: {\n id: 'instruct.specifyLearner.learnerNotEnrolled',\n defaultMessage: '{identifier} is not enrolled in this course',\n description: 'Error message displayed when a learner is found based on the provided identifier (email or username) but is not enrolled in the course',\n }\n});\n\nexport default messages;\n"]}
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/components/messages.ts"],"names":[],"mappings":"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","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n select: {\n id: 'instruct.specifyLearner.select',\n defaultMessage: 'Select',\n description: 'Label for select dropdown in specify learner field',\n },\n specifyLearner: {\n id: 'instruct.specifyLearner.label',\n defaultMessage: 'Specify Learner:',\n description: 'Label for specify learner field',\n },\n specifyLearnerPlaceholder: {\n id: 'instruct.specifyLearner.placeholder',\n defaultMessage: 'Learner email address or username',\n description: 'Placeholder text for specify learner input field',\n },\n pageNotFoundHeader: {\n id: 'instruct.pageNotFound.header',\n defaultMessage: 'Page not found',\n description: 'Header for page not found error',\n },\n pageNotFoundBody: {\n id: 'instruct.pageNotFound.body',\n defaultMessage: \"The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again.\",\n description: 'Body text for page not found error',\n },\n pendingTasksTitle: {\n id: 'instruct.pendingTasks.section.title',\n defaultMessage: 'Pending Tasks',\n description: 'Title for the pending tasks section',\n },\n noTasksMessage: {\n id: 'instruct.pendingTasks.section.noTasks',\n defaultMessage: 'No tasks currently running.',\n description: 'Message displayed when there are no pending tasks',\n },\n taskTypeColumnName: {\n id: 'instruct.pendingTasks.table.column.taskType',\n defaultMessage: 'Task Type',\n description: 'Column name for task type in pending tasks table',\n },\n taskInputColumnName: {\n id: 'instruct.pendingTasks.table.column.taskInput',\n defaultMessage: 'Task Input',\n description: 'Column name for task input in pending tasks table',\n },\n taskIdColumnName: {\n id: 'instruct.pendingTasks.table.column.taskId',\n defaultMessage: 'Task ID',\n description: 'Column name for task ID in pending tasks table',\n },\n requesterColumnName: {\n id: 'instruct.pendingTasks.table.column.requester',\n defaultMessage: 'Requester',\n description: 'Column name for requester in pending tasks table',\n },\n taskStateColumnName: {\n id: 'instruct.pendingTasks.table.column.taskState',\n defaultMessage: 'Task State',\n description: 'Column name for task state in pending tasks table',\n },\n createdColumnName: {\n id: 'instruct.pendingTasks.table.column.created',\n defaultMessage: 'Created',\n description: 'Column name for created date in pending tasks table',\n },\n taskOutputColumnName: {\n id: 'instruct.pendingTasks.table.column.taskOutput',\n defaultMessage: 'Task Output',\n description: 'Column name for task output in pending tasks table',\n },\n durationColumnName: {\n id: 'instruct.pendingTasks.table.column.duration',\n defaultMessage: 'Duration (sec)',\n description: 'Column name for duration in pending tasks table',\n },\n statusColumnName: {\n id: 'instruct.pendingTasks.table.column.status',\n defaultMessage: 'Status',\n description: 'Column name for status in pending tasks table',\n },\n taskMessageColumnName: {\n id: 'instruct.pendingTasks.table.column.taskMessage',\n defaultMessage: 'Task Message',\n description: 'Column name for task message in pending tasks table',\n },\n downloadCSVTitle: {\n id: 'instruct.csvComponent.downloadCSVTitle',\n defaultMessage: 'Upload CSV File',\n description: 'Title for the upload CSV file section'\n },\n downloadCSVDescription: {\n id: 'instruct.csvComponent.downloadCSVDescription',\n defaultMessage: 'Only properly formatted CSV files will be accepted',\n description: 'Description for the upload CSV file section'\n },\n viewCSVTemplate: {\n id: 'instruct.csvComponent.viewCSVTemplate',\n defaultMessage: 'View Template',\n description: 'Label for the view CSV template link'\n },\n uploadingFileMessage: {\n id: 'instruct.csvComponent.uploadingFileMessage',\n defaultMessage: 'File chosen: {fileName}',\n description: 'Message displayed when a file is being uploaded, with the file name included'\n },\n change: {\n id: 'instruct.specifyLearner.change',\n defaultMessage: 'Change',\n description: 'Label for change button in specify learner field',\n },\n learnerNotFound: {\n id: 'instruct.specifyLearner.learnerNotFound',\n defaultMessage: 'Could not find student matching identifier: {identifier}',\n description: 'Error message displayed when a learner cannot be found based on the provided identifier (email or username)',\n },\n problemNotFound: {\n id: 'instruct.specifyProblemField.problemNotFound',\n defaultMessage: 'Could not find problem matching identifier: {identifier}',\n description: 'Error message displayed when a problem cannot be found based on the provided identifier',\n },\n searchPlaceholder: {\n id: 'instruct.usernameFilter.searchPlaceholder',\n defaultMessage: 'Search By Username or Email',\n description: 'Placeholder text for the username filter input',\n },\n problemLocationPlaceholder: {\n id: 'instruct.specifyProblemField.locationPlaceholder',\n defaultMessage: 'Problem location',\n description: 'Placeholder text for problem location input',\n },\n problemLocationInfoIconLabel: {\n id: 'instruct.specifyProblemField.infoIconLabel',\n defaultMessage: 'Example format for problem location',\n description: 'Aria label for the info icon next to the problem location input',\n },\n problemLocationTooltip: {\n id: 'instruct.specifyProblemField.locationTooltip',\n defaultMessage: 'Example: block-v1:edX+DemoX+2015+type@problem+block@618c5933b8b544e4a4cc103d3e508378',\n description: 'Tooltip text showing an example problem location format',\n },\n selectedProblem: {\n id: 'instruct.specifyProblemField.selectedProblem',\n defaultMessage: 'Selected Problem:',\n description: 'Label for specify problem field when a problem has been selected',\n },\n learnerNotEnrolled: {\n id: 'instruct.specifyLearner.learnerNotEnrolled',\n defaultMessage: '{identifier} is not enrolled in this course',\n description: 'Error message displayed when a learner is found based on the provided identifier (email or username) but is not enrolled in the course',\n }\n});\n\nexport default messages;\n"]}
@@ -36,7 +36,12 @@ const InstructorNav = () => {
36
36
  }
37
37
  if (sortedTabs.length === 0)
38
38
  return null;
39
- return (_jsx(Navbar, { expand: "md", className: "py-0", children: _jsxs(Nav, { variant: "tabs", activeKey: tabId, children: [_jsx(Navbar.Toggle, { "aria-controls": "instructor-nav" }), _jsx(Navbar.Collapse, { id: "instructor-nav", children: sortedTabs.map((tab) => (_jsx(Nav.Item, { children: _jsx(Nav.Link, { as: Link, to: tab.url, active: tab.tabId === tabId, onClick: () => clearAlerts(), children: tab.title }) }, tab.tabId))) })] }) }));
39
+ return (_jsx(Navbar, { expand: "md", className: "py-0", children: _jsxs(Nav, { variant: "tabs", activeKey: tabId, children: [_jsx(Navbar.Toggle, { "aria-controls": "instructor-nav" }), _jsx(Navbar.Collapse, { id: "instructor-nav", children: sortedTabs.map((tab) => {
40
+ const isInternal = tab.url.startsWith('/');
41
+ return (_jsx(Nav.Item, { children: _jsx(Nav.Link, Object.assign({}, (isInternal
42
+ ? { to: tab.url, as: Link }
43
+ : { href: tab.url }), { active: tab.tabId === tabId, onClick: () => clearAlerts(), children: tab.title })) }, tab.tabId));
44
+ }) })] }) }));
40
45
  };
41
46
  export default InstructorNav;
42
47
  //# sourceMappingURL=InstructorNav.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"InstructorNav.js","sourceRoot":"","sources":["../../src/instructorNav/InstructorNav.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo } from 'react';\nimport { useParams, Link } from 'react-router-dom';\nimport { Nav, Navbar, Skeleton } from '@openedx/paragon';\nimport { useCourseInfo } from '@src/data/apiHook';\nimport { useAlert } from '@src/providers/AlertProvider';\nimport { useWidgetProps } from '../slots/SlotUtils';\n\nexport interface TabProps {\n tabId: string,\n url: string,\n title: string,\n sortOrder: number,\n}\n\nconst InstructorNav = () => {\n const { courseId = '', tabId = '' } = useParams<{ courseId: string, tabId?: string }>();\n const { data: courseInfo, isLoading } = useCourseInfo(courseId);\n const widgetPropsArray = useWidgetProps('org.openedx.frontend.slot.instructorDashboard.tabs.v1') as TabProps[];\n const { clearAlerts } = useAlert();\n\n const sortedTabs = useMemo(() => {\n if (isLoading) return [];\n const apiTabs: TabProps[] = courseInfo?.tabs ?? [];\n const tabMap = new Map<string, TabProps>();\n\n // Adding tabs from API and from slot into a map to avoid duplicates\n apiTabs.forEach(tab => {\n tabMap.set(tab.tabId, tab);\n });\n\n widgetPropsArray.forEach(slotTab => {\n // If the slotTab doesn't have a tabId or title, we can't render it properly, so we skip it.\n if (!slotTab.tabId || !slotTab.title) {\n return;\n }\n\n tabMap.set(slotTab.tabId, slotTab);\n });\n\n const allTabs = Array.from(tabMap.values());\n\n // Tabs are sorted by sortOrder, with a fallback to 1000 to be placed at the end for tabs that don't have sortOrder defined (to avoid NaN issues)\n return allTabs.sort((a, b) => (a.sortOrder ?? 1000) - (b.sortOrder ?? 1000));\n }, [courseInfo?.tabs, isLoading, widgetPropsArray]);\n\n if (isLoading) {\n return <Skeleton className=\"lead\" />;\n }\n\n if (sortedTabs.length === 0) return null;\n\n return (\n <Navbar expand=\"md\" className=\"py-0\">\n <Nav\n variant=\"tabs\"\n activeKey={tabId}\n >\n <Navbar.Toggle aria-controls=\"instructor-nav\" />\n <Navbar.Collapse id=\"instructor-nav\">\n {\n sortedTabs.map((tab) => (\n <Nav.Item key={tab.tabId}>\n <Nav.Link\n as={Link}\n to={tab.url}\n active={tab.tabId === tabId}\n onClick={() => clearAlerts()}\n >\n {tab.title}\n </Nav.Link>\n </Nav.Item>\n ))\n }\n </Navbar.Collapse>\n </Nav>\n </Navbar>\n );\n};\n\nexport default InstructorNav;\n"]}
1
+ {"version":3,"file":"InstructorNav.js","sourceRoot":"","sources":["../../src/instructorNav/InstructorNav.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo } from 'react';\nimport { useParams, Link } from 'react-router-dom';\nimport { Nav, Navbar, Skeleton } from '@openedx/paragon';\nimport { useCourseInfo } from '@src/data/apiHook';\nimport { useAlert } from '@src/providers/AlertProvider';\nimport { useWidgetProps } from '@src/slots/SlotUtils';\n\nexport interface TabProps {\n tabId: string,\n url: string,\n title: string,\n sortOrder: number,\n}\n\nconst InstructorNav = () => {\n const { courseId = '', tabId = '' } = useParams<{ courseId: string, tabId?: string }>();\n const { data: courseInfo, isLoading } = useCourseInfo(courseId);\n const widgetPropsArray = useWidgetProps('org.openedx.frontend.slot.instructorDashboard.tabs.v1') as TabProps[];\n const { clearAlerts } = useAlert();\n\n const sortedTabs = useMemo(() => {\n if (isLoading) return [];\n const apiTabs: TabProps[] = courseInfo?.tabs ?? [];\n const tabMap = new Map<string, TabProps>();\n\n // Adding tabs from API and from slot into a map to avoid duplicates\n apiTabs.forEach(tab => {\n tabMap.set(tab.tabId, tab);\n });\n\n widgetPropsArray.forEach(slotTab => {\n // If the slotTab doesn't have a tabId or title, we can't render it properly, so we skip it.\n if (!slotTab.tabId || !slotTab.title) {\n return;\n }\n\n tabMap.set(slotTab.tabId, slotTab);\n });\n\n const allTabs = Array.from(tabMap.values());\n\n // Tabs are sorted by sortOrder, with a fallback to 1000 to be placed at the end for tabs that don't have sortOrder defined (to avoid NaN issues)\n return allTabs.sort((a, b) => (a.sortOrder ?? 1000) - (b.sortOrder ?? 1000));\n }, [courseInfo?.tabs, isLoading, widgetPropsArray]);\n\n if (isLoading) {\n return <Skeleton className=\"lead\" />;\n }\n\n if (sortedTabs.length === 0) return null;\n\n return (\n <Navbar expand=\"md\" className=\"py-0\">\n <Nav\n variant=\"tabs\"\n activeKey={tabId}\n >\n <Navbar.Toggle aria-controls=\"instructor-nav\" />\n <Navbar.Collapse id=\"instructor-nav\">\n {\n sortedTabs.map((tab) => {\n const isInternal = tab.url.startsWith('/');\n return (\n <Nav.Item key={tab.tabId}>\n <Nav.Link\n {...(isInternal\n ? { to: tab.url, as: Link }\n : { href: tab.url }\n )}\n active={tab.tabId === tabId}\n onClick={() => clearAlerts()}\n >\n {tab.title}\n </Nav.Link>\n </Nav.Item>\n );\n })\n }\n </Navbar.Collapse>\n </Nav>\n </Navbar>\n );\n};\n\nexport default InstructorNav;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openedx/frontend-app-instructor-dashboard",
3
- "version": "1.0.0-alpha.23",
3
+ "version": "1.0.0-alpha.25",
4
4
  "description": "The Open edX Instructor Dashboard",
5
5
  "repository": {
6
6
  "type": "git",