Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

E2465. UI for Institutions and Notification #67

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
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
56 changes: 40 additions & 16 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import Login from "./pages/Authentication/Login";
import Logout from "./pages/Authentication/Logout";
import InstitutionEditor, { loadInstitution } from "./pages/Institutions/InstitutionEditor";
import Institutions, { loadInstitutions } from "./pages/Institutions/Institutions";
import NotificationEditor from "./pages/Notifications/NotificationEditor"
import Notifications from "./pages/Notifications/Notifications"
import ViewNotifications from "./pages/Notifications/ViewNotifications";
import RoleEditor, { loadAvailableRole } from "./pages/Roles/RoleEditor";
import Roles, { loadRoles } from "./pages/Roles/Roles";
import Assignment from "./pages/Assignments/Assignment";
Expand Down Expand Up @@ -142,6 +145,10 @@ function App() {
path: "profile",
element: <ProtectedRoute element={<EditProfile />} />,
},
{
path: "view-notifications",
element: <ProtectedRoute element={<ViewNotifications />} />,
},
{
path: "assignments/edit/:assignmentId/participants",
element: <Participants type="student_tasks" id={1} />,
Expand Down Expand Up @@ -226,6 +233,38 @@ function App() {
},
],
},
{
path: "notifications",
element: <Notifications />,
//loader: loadNotifications,
children: [
{
path: "new",
element: <NotificationEditor mode="create" />,
},
{
path: "edit/:id",
element: <NotificationEditor mode="update" />,
//loader: loadNotification,
},
],
},
{
path: "institutions",
element: <Institutions />,
loader: loadInstitutions,
children: [
{
path: "new",
element: <InstitutionEditor mode="create" />,
},
{
path: "edit/:id",
element: <InstitutionEditor mode="update" />,
loader: loadInstitution,
},
],
},
{
path: "administrator",
element: (
Expand All @@ -250,22 +289,7 @@ function App() {
},
],
},
{
path: "institutions",
element: <Institutions />,
loader: loadInstitutions,
children: [
{
path: "new",
element: <InstitutionEditor mode="create" />,
},
{
path: "edit/:id",
element: <InstitutionEditor mode="update" />,
loader: loadInstitution,
},
],
},

{
path: ":user_type",
element: <ManageUserTypes />,
Expand Down
65 changes: 34 additions & 31 deletions src/components/Form/FormInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { IFormikFieldProps, IFormProps } from "./interfaces";
* @author Ankur Mundra on May, 2023
*/

const FormInput: React.FC<IFormProps> = (props) => {
const FormInput: React.FC<IFormProps> = (props) => { // Make sure it's being used here
const {
name,
label,
Expand All @@ -21,43 +21,46 @@ const FormInput: React.FC<IFormProps> = (props) => {
inputGroupPrepend,
inputGroupAppend,
tooltipPlacement,
rows, // rows should be passed as part of props now
} = props;

const displayLabel = tooltip ? (
<>
{label}&nbsp;
<ToolTip id={`${controlId}-tooltip`} info={tooltip} placement={tooltipPlacement} />
</>
<>
{label}&nbsp;
<ToolTip id={`${controlId}-tooltip`} info={tooltip} placement={tooltipPlacement} />
</>
) : (
label
label
);

return (
<Field name={name}>
{({ field, form }: IFormikFieldProps) => {
const isValid = !form.errors[field.name];
const isInvalid = form.touched[field.name] && !isValid;
return (
<Form.Group as={as} md={md} controlId={controlId} className="mb-md-2">
{label && <Form.Label>{displayLabel}</Form.Label>}
<InputGroup>
{inputGroupPrepend}
<Form.Control
{...field}
type={type}
disabled={disabled}
isInvalid={isInvalid}
feedback={form.errors[field.name]}
/>
{inputGroupAppend}
<Form.Control.Feedback type="invalid">
{form.errors[field.name]}
</Form.Control.Feedback>
</InputGroup>
</Form.Group>
);
}}
</Field>
<Field name={name}>
{({ field, form }: IFormikFieldProps) => {
const isValid = !form.errors[field.name];
const isInvalid = form.touched[field.name] && !isValid;
return (
<Form.Group as={as} md={md} controlId={controlId} className="mb-md-2">
{label && <Form.Label>{displayLabel}</Form.Label>}
<InputGroup>
{inputGroupPrepend}
<Form.Control
{...field}
type={type}
disabled={disabled}
isInvalid={isInvalid}
feedback={form.errors[field.name]}
as={as} // Add the "as" prop so it can handle "textarea"
rows={rows} // Pass the "rows" prop if it's a textarea
/>
{inputGroupAppend}
<Form.Control.Feedback type="invalid">
{form.errors[field.name]}
</Form.Control.Feedback>
</InputGroup>
</Form.Group>
);
}}
</Field>
);
};

Expand Down
3 changes: 2 additions & 1 deletion src/components/Form/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ElementType, ReactNode } from "react";
import React, { ElementType, ReactNode } from "react";

/**
* @author Ankur Mundra on May, 2023
Expand All @@ -16,6 +16,7 @@ export interface IFormProps {
tooltipPlacement?: "top" | "right" | "bottom" | "left";
inputGroupPrepend?: ReactNode;
inputGroupAppend?: ReactNode;
rows?: number; // rows would be passed as part of props now
}

export interface IFormOption {
Expand Down
150 changes: 69 additions & 81 deletions src/layout/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React, { Fragment, useState, useEffect } from "react";
import { Button, Container, Nav, Navbar, NavDropdown } from "react-bootstrap";
import { Button, Container, Nav, Navbar, NavDropdown, Badge } from "react-bootstrap";
import { useSelector } from "react-redux";
import { Link, useNavigate } from "react-router-dom";
import { RootState } from "../store/store";
import { ROLE } from "../utils/interfaces";
import { hasAllPrivilegesOf } from "../utils/util";
import detective from "../assets/detective.png";
import { mockNotifications } from "../pages/Notifications/mock_data"; // Import the mock notifications

/**
* @author Ankur Mundra on May, 2023
Expand All @@ -19,53 +20,56 @@ const Header: React.FC = () => {
const navigate = useNavigate();

const [visible, setVisible] = useState(true);
const [unreadCount, setUnreadCount] = useState(0);

const CustomBtn = () => {
return (
// Calculate unread notifications on component load
useEffect(() => {
const countUnread = mockNotifications.filter(
(notification) => notification.isUnread && notification.isActive
).length;
setUnreadCount(countUnread);
}, []);

const CustomBtn = () => (
<div
style={{
backgroundColor: "#fff",
color: "#333",
padding: "10px 4px",
borderRadius: 4,
marginRight: 8,
}}
>
<div
style={{
backgroundColor: "#fff",
color: "#333",
padding: "10px 4px",
borderRadius: 4,
marginRight: 8,
display: "flex",
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
}}
>
<div
<img src={detective} width={25} style={{ marginRight: 4 }} alt="Detective icon" />
<div>Anonymized View</div>
<button
style={{
display: "flex",
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
background: "none",
border: "none",
padding: 1,
marginLeft: 6,
backgroundColor: "red",
borderRadius: 50,
color: "white",
width: 18,
fontSize: 10,
fontWeight: 800,
}}
onClick={() => setVisible(!visible)}
>
<img src={detective} width={25} style={{ marginRight: 4 }} />
<div>Anonymized View</div>
<button
style={{
background: "none",
border: "none",
padding: 1,
marginLeft: 6,
backgroundColor: "red",
borderRadius: 50,
color: "white",
width: 18,
fontSize: 10,
fontWeight: 800,
}}
onClick={() => setVisible(!visible)}
>
x
</button>
</div>
x
</button>
</div>
);
};

// useEffect(() => {
// console.log(visible, 'Changed');
// }, [visible]);
</div>
);

return (
<Fragment>
Expand Down Expand Up @@ -100,9 +104,6 @@ const Header: React.FC = () => {
Roles
</NavDropdown.Item>
<NavDropdown.Divider />
<NavDropdown.Item as={Link} to="administrator/institutions">
Institutions
</NavDropdown.Item>
<NavDropdown.Item as={Link} to="administrator/instructors">
Instructors
</NavDropdown.Item>
Expand All @@ -119,45 +120,34 @@ const Header: React.FC = () => {
)}
{hasAllPrivilegesOf(auth.user.role, ROLE.TA) && (
<NavDropdown title="Manage" id="basic-nav-dropdown">
<NavDropdown.Item as={Link} to="/users">
Users
</NavDropdown.Item>
<NavDropdown.Item as={Link} to="/courses">
Courses
</NavDropdown.Item>
<NavDropdown.Item as={Link} to="/assignments">
Assignments
</NavDropdown.Item>
<NavDropdown.Item as={Link} to="/questionnaire">
Questionnaire
</NavDropdown.Item>
<NavDropdown.Item as={Link} to="/edit-questionnaire">
Edit Questionnaire
</NavDropdown.Item>
<NavDropdown.Item as={Link} to="/users">Users</NavDropdown.Item>
<NavDropdown.Item as={Link} to="/courses">Courses</NavDropdown.Item>
<NavDropdown.Item as={Link} to="/notifications">Notifications</NavDropdown.Item>
<NavDropdown.Item as={Link} to="/institutions">Institutions</NavDropdown.Item>
<NavDropdown.Item as={Link} to="/assignments">Assignments</NavDropdown.Item>
<NavDropdown.Item as={Link} to="/questionnaire">Questionnaire</NavDropdown.Item>
<NavDropdown.Item as={Link} to="/edit-questionnaire">Edit Questionnaire</NavDropdown.Item>
<NavDropdown.Divider />
<NavDropdown.Item as={Link} to="/impersonate">
Impersonate User
</NavDropdown.Item>
<NavDropdown.Item as={Link} to="#">
Anonymized View
</NavDropdown.Item>
<NavDropdown.Item as={Link} to="/impersonate">Impersonate User</NavDropdown.Item>
<NavDropdown.Item as={Link} to="#">Anonymized View</NavDropdown.Item>
</NavDropdown>
)}
<Nav.Link as={Link} to="/student_tasks">
Assignments
</Nav.Link>
<Nav.Link as={Link} to="/profile">
Profile
</Nav.Link>
<Nav.Link as={Link} to="/student_view">
Student View
</Nav.Link>
<Nav.Link as={Link} to="/view-team-grades">
Grades View
</Nav.Link>
<Nav.Link as={Link} to="#" onClick={() => setVisible(!visible)}>
Anonymized View
</Nav.Link>
<Nav.Link as={Link} to="/student_tasks">Assignments</Nav.Link>
<Nav.Link as={Link} to="/profile">Profile</Nav.Link>
<Nav.Link as={Link} to="/student_view">Student View</Nav.Link>
<Nav.Link as={Link} to="/view-team-grades">Grades View</Nav.Link>

{!hasAllPrivilegesOf(auth.user.role, ROLE.TA) && (
<Nav.Link as={Link} to="/view-notifications">
My Notifications{" "}
{unreadCount > 0 && (
<Badge bg="danger" pill className="align-text-top">
{unreadCount}
</Badge>
)}
</Nav.Link>
)}
<Nav.Link as={Link} to="#" onClick={() => setVisible(!visible)}>Anonymized View</Nav.Link>
</Nav>
{visible ? (
<Nav.Item className="text-light ps-md-3 pe-md-3">
Expand All @@ -177,9 +167,7 @@ const Header: React.FC = () => {
</div>
</Nav.Item>
)}
<Button variant="outline-light" onClick={() => navigate("/logout")}>
Logout
</Button>
<Button variant="outline-light" onClick={() => navigate("/logout")}>Logout</Button>
</Navbar.Collapse>
</Container>
)}
Expand Down
Loading