diff --git a/.eslintignore b/.eslintignore index 63b8059b2a..5d521d54e4 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,25 +1,36 @@ -*.png -*.svg -*.test.js -*.test.jsx -src/actions/** -src/App.css -src/config.json +# ======================================================================= +# ⚠️ DO NOT ADD NEW ENTRIES ⚠️ +# Only the files and folders listed below are allowed to be ignored. +# This .gitignore is locked down to maintain consistency across the team. +# To propose changes, please open a discussion or PR with justification. +# ======================================================================= + +# Ignore build folders +/node_modules/ +/public/ +# Ignore test files inside /src/components src/__tests__/** +/src/components/**/__test__/**/* +/src/components/**/__tests__/**/* +/src/reducers/**/__tests__/**/* -/public/ -/build/ -/node_modules/ +src/components/BMDashboard/_tests_/BMDashboard.test.jsx +src/components/Login/ForgotPassword.test.jsx +src/components/Login/LoginPage.test.js +src/components/PermissionsManagement/PermissionsManagement.test.js +src/components/Reports/PeopleReport/components/PeopleTasksPieChart.test.jsx + +# Ignore folders in /src +src/actions/** src/components/Badge/** src/components/common/** +src/components/Dashboard/** +src/components/EmailSubscribeForm/** src/components/Projects/** src/components/SummaryManagement/** src/components/TaskEditSuggestions/** src/components/TeamMemberTasks/** -src/components/Timelog/** +src/components/Teams/TeamMembersPopup.jsx src/components/UserManagement/** -src/components/UserProfile/** -src/components/EmailSubscribeForm/** -src/components/Dashboard/** -src/components/Teams/TeamMembersPopup.jsx \ No newline at end of file +src/components/UserProfile/** \ No newline at end of file diff --git a/src/components/Collaboration/JobFormBuilder.css b/src/components/Collaboration/JobFormBuilder.css index bb8aba30ad..7df95d1bf4 100644 --- a/src/components/Collaboration/JobFormBuilder.css +++ b/src/components/Collaboration/JobFormBuilder.css @@ -79,10 +79,10 @@ h2 { margin-top: 15px; } -input, +/* input, textarea, select { - /* width: 95%; */ + width: 95%; padding: 10px; margin-top: 5px; margin-bottom: 15px; @@ -94,7 +94,7 @@ select { textarea { height: 80px; resize: vertical; -} +} */ .new-field-section { background-color: #d9d9d9; @@ -142,10 +142,10 @@ textarea { /* gap: 10px; */ } -.field-options input, +/* .field-options input, textarea { width: 95%; -} +} */ /* Individual Option Item */ .option-item { diff --git a/src/components/CommunityPortal/CPDashboard.jsx b/src/components/CommunityPortal/CPDashboard.jsx index 82d848643e..2792aa494e 100644 --- a/src/components/CommunityPortal/CPDashboard.jsx +++ b/src/components/CommunityPortal/CPDashboard.jsx @@ -1,26 +1,11 @@ import { useState, useEffect } from 'react'; -import { - Container, - Row, - Col, - Card, - CardBody, - Button, - Input, - Dropdown, - DropdownToggle, - DropdownMenu, - DropdownItem, -} from 'reactstrap'; -import { useHistory } from 'react-router-dom'; // For React Router v5 +import { Container, Row, Col, Card, CardBody, Button, Input } from 'reactstrap'; import './CPDashboard.css'; import { FaCalendarAlt, FaMapMarkerAlt, FaUserAlt } from 'react-icons/fa'; export function CPDashboard() { const [events, setEvents] = useState([]); const [search, setSearch] = useState(''); - const [dropdownOpen, setDropdownOpen] = useState(false); - const history = useHistory(); // Use useHistory for navigation useEffect(() => { const mockEvents = [ @@ -52,12 +37,6 @@ export function CPDashboard() { setEvents(mockEvents); }, []); - const toggleDropdown = () => setDropdownOpen(!dropdownOpen); - - const handleNavigation = path => { - history.push(path); // Navigate to the selected path - }; - return (
@@ -72,7 +51,7 @@ export function CPDashboard() { className="dashboard-search" /> - + {/* Community Portal @@ -82,7 +61,7 @@ export function CPDashboard() { handleNavigation('/about')}>About Us handleNavigation('/contact')}>Contact - + */}
diff --git a/src/components/CommunityPortal/CPHeader/CPHeader.css b/src/components/CommunityPortal/CPHeader/CPHeader.css new file mode 100644 index 0000000000..e07cbe38c1 --- /dev/null +++ b/src/components/CommunityPortal/CPHeader/CPHeader.css @@ -0,0 +1,64 @@ +.header-wrapper { + height: fit-content; + width: clamp(100vw, 0.1rem + 1vw, 100%); +} + +.navbar { + z-index: 100; + white-space: nowrap; +} + +.timer-message-section { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + gap: 1rem; +} + +.nav-links { + align-items: center; +} + +.redBackGroupHeader { + z-index: 10; + bottom: 0; + right: 0; + position: relative; + height: 30px; + text-align: center; + vertical-align: middle; + background: #ff4d4f; + border-radius: 40px; + color: #fff; + padding: 3px 6px; + max-width: 39px; + font-size: 1rem; + min-width: 29px; +} + +.owner-message { + margin-right: 3rem; +} + +@media (max-width: 1500px) { + .dashboard-text-link { + font-size: 14px; + } + + .owner-message { + margin-right: 0; + } +} + +@media (max-width: 1400px) { + .timer-message-section { + display: flex; + width: 0; + margin-right: 0; + } + + .owner-message { + display: none; + } +} \ No newline at end of file diff --git a/src/components/CommunityPortal/CPHeader/CPHeader.jsx b/src/components/CommunityPortal/CPHeader/CPHeader.jsx new file mode 100644 index 0000000000..be365fd22b --- /dev/null +++ b/src/components/CommunityPortal/CPHeader/CPHeader.jsx @@ -0,0 +1,360 @@ +import { useState, useEffect, useMemo } from 'react'; +// import { getUserProfile } from '../../actions/userProfile' +import { Link } from 'react-router-dom'; +import { connect, useDispatch } from 'react-redux'; +import { + Collapse, + Navbar, + NavbarToggler, + Nav, + NavItem, + NavLink, + UncontrolledDropdown, + DropdownToggle, + DropdownMenu, + DropdownItem, +} from 'reactstrap'; +import DarkModeButton from 'components/Header/DarkModeButton'; +import { fetchTaskEditSuggestions } from 'components/TaskEditSuggestions/thunks'; +import BellNotification from 'components/Header/BellNotification'; +import { getHeaderData } from '../../../actions/authActions'; +import { getAllRoles } from '../../../actions/role'; +import Timer from '../../Timer/Timer'; +import OwnerMessage from '../../OwnerMessage/OwnerMessage'; +import { + // LOGO, + DASHBOARD, + // BM_DASHBOARD, + // CP_DASHBOARD, + REPORTS, + OTHER_LINKS, + USER_MANAGEMENT, + BADGE_MANAGEMENT, + PROJECTS, + TEAMS, + WELCOME, + VIEW_PROFILE, + UPDATE_PASSWORD, + LOGOUT, + PERMISSIONS_MANAGEMENT, + ACTIVITY, + PARTICIPATION, + RESOURCE_USAGE, + CALENDAR, + EVENT_PERSONALIZATION, + ACTIVITIES, + REGISTRATION, + SEND_EMAILS, +} from '../../../languages/en/ui'; +import Logout from '../../Logout/Logout'; +import './CPHeader.css'; +import hasPermission, { cantUpdateDevAdminDetails } from '../../../utils/permissions'; + +export function Header(props) { + const [isOpen, setIsOpen] = useState(false); + const [logoutPopup, setLogoutPopup] = useState(false); + const { isAuthenticated, user } = props.auth; + const [firstName, setFirstName] = useState(props.auth.firstName); + const [profilePic, setProfilePic] = useState(props.auth.profilePic); + const [isAuthUser, setIsAuthUser] = useState(true); + const [displayUserId, setDisplayUserId] = useState(user.userid); + + const ALLOWED_ROLES_TO_INTERACT = useMemo(() => ['Owner', 'Administrator'], []); + const canInteractWithViewingUser = useMemo( + () => ALLOWED_ROLES_TO_INTERACT.includes(props.auth.user.role), + [ALLOWED_ROLES_TO_INTERACT, props.auth.user.role], + ); + + // Users + const canAccessUserManagement = + props.hasPermission('postUserProfile', !isAuthUser && canInteractWithViewingUser) || + props.hasPermission('deleteUserProfile', !isAuthUser && canInteractWithViewingUser) || + props.hasPermission('changeUserStatus', !isAuthUser && canInteractWithViewingUser) || + props.hasPermission('getUserProfiles', !isAuthUser && canInteractWithViewingUser); + + // Badges + const canAccessBadgeManagement = + props.hasPermission('seeBadges', !isAuthUser && canInteractWithViewingUser) || + props.hasPermission('createBadges', !isAuthUser && canInteractWithViewingUser) || + props.hasPermission('updateBadges', !isAuthUser && canInteractWithViewingUser) || + props.hasPermission('deleteBadges', !isAuthUser && canInteractWithViewingUser); + + // Projects + const canAccessProjects = + props.hasPermission('postProject', !isAuthUser && canInteractWithViewingUser) || + props.hasPermission('deleteProject', !isAuthUser && canInteractWithViewingUser) || + props.hasPermission('putProject', !isAuthUser && canInteractWithViewingUser) || + props.hasPermission('getProjectMembers', !isAuthUser && canInteractWithViewingUser) || + props.hasPermission('assignProjectToUsers', !isAuthUser && canInteractWithViewingUser) || + props.hasPermission('postWbs', !isAuthUser && canInteractWithViewingUser) || + props.hasPermission('deleteWbs', !isAuthUser && canInteractWithViewingUser) || + props.hasPermission('postTask', !isAuthUser && canInteractWithViewingUser) || + props.hasPermission('updateTask', !isAuthUser && canInteractWithViewingUser) || + props.hasPermission('deleteTask', !isAuthUser && canInteractWithViewingUser); + // Tasks + const canUpdateTask = props.hasPermission( + 'updateTask', + !isAuthUser && canInteractWithViewingUser, + ); + // Teams + const canAccessTeams = + props.hasPermission('postTeam', !isAuthUser && canInteractWithViewingUser) || + props.hasPermission('putTeam', !isAuthUser && canInteractWithViewingUser) || + props.hasPermission('deleteTeam', !isAuthUser && canInteractWithViewingUser) || + props.hasPermission('assignTeamToUsers', !isAuthUser && canInteractWithViewingUser); + // Popups + const canAccessPopups = + props.hasPermission('createPopup', !isAuthUser && canInteractWithViewingUser) || + props.hasPermission('updatePopup', !isAuthUser && canInteractWithViewingUser); + // SendEmails + const canAccessSendEmails = props.hasPermission('sendEmails', !isAuthUser); + // Permissions + const canAccessPermissionsManagement = + props.hasPermission('postRole', !isAuthUser && canInteractWithViewingUser) || + props.hasPermission('putRole', !isAuthUser && canInteractWithViewingUser) || + props.hasPermission('deleteRole', !isAuthUser && canInteractWithViewingUser) || + props.hasPermission('putUserProfilePermissions', !isAuthUser && canInteractWithViewingUser); + + const dispatch = useDispatch(); + const { darkMode } = props; + + useEffect(() => { + const handleStorageEvent = () => { + const sessionStorageData = JSON.parse(window.sessionStorage.getItem('viewingUser')); + if (sessionStorageData) { + setDisplayUserId(sessionStorageData.userId); + setFirstName(sessionStorageData.firstName); + setProfilePic(sessionStorageData.profilePic); + setIsAuthUser(false); + } else { + setDisplayUserId(user.userid); + setFirstName(props.auth.firstName); + setProfilePic(props.auth.profilePic); + setIsAuthUser(true); + } + }; + + // Set the initial state when the component mounts + handleStorageEvent(); + + // Add the event listener + window.addEventListener('storage', handleStorageEvent); + + // Clean up the event listener when the component unmounts + return () => { + window.removeEventListener('storage', handleStorageEvent); + }; + }, [user.userid, props.auth.firstName]); + + useEffect(() => { + if (props.auth.isAuthenticated) { + props.getHeaderData(props.auth.user.userid); + if (props.auth.user.role === 'Administrator') { + dispatch(fetchTaskEditSuggestions()); + } + } + }, [props.auth.isAuthenticated]); + + const roles = props.role?.roles; + + useEffect(() => { + if (roles.length === 0) { + props.getAllRoles(); + } + }, []); + + const toggle = () => { + setIsOpen(prevIsOpen => !prevIsOpen); + }; + + const openModal = () => { + setLogoutPopup(true); + }; + + const fontColor = darkMode ? 'text-white dropdown-item-hover' : ''; + + return ( +
+ + {logoutPopup && } + +
+ {isAuthenticated && } + {isAuthenticated && ( +
+ +
+ )} +
+ + {isAuthenticated && ( + + + + )} +
+
+ ); +} + +const mapStateToProps = state => ({ + auth: state.auth, + userProfile: state.userProfile, + taskEditSuggestionCount: state.taskEditSuggestions.count, + role: state.role, + notification: state.notification, + darkMode: state.theme.darkMode, +}); + +export default connect(mapStateToProps, { + getHeaderData, + getAllRoles, + hasPermission, +})(Header); diff --git a/src/components/CommunityPortal/index.js b/src/components/CommunityPortal/index.js index 697aa47523..243c7de856 100644 --- a/src/components/CommunityPortal/index.js +++ b/src/components/CommunityPortal/index.js @@ -1,3 +1,5 @@ import CPDashboard from './CPDashboard'; +import CPHeader from './CPHeader/CPHeader'; export default CPDashboard; +export { CPHeader }; diff --git a/src/components/Header/HeaderRenderer.jsx b/src/components/Header/HeaderRenderer.jsx new file mode 100644 index 0000000000..2dc1f342e9 --- /dev/null +++ b/src/components/Header/HeaderRenderer.jsx @@ -0,0 +1,33 @@ +import { CPHeader } from 'components/CommunityPortal'; +import { useLocation } from 'react-router-dom'; +import { connect } from 'react-redux'; +import { getWeeklySummaries } from 'actions/weeklySummaries'; +import { Header } from './Header'; +import { getHeaderData } from '../../actions/authActions'; +import { getAllRoles } from '../../actions/role'; +import hasPermission from '../../utils/permissions'; + +export function HeaderRenderer(props) { + const location = useLocation(); + const isCommunityPortal = location.pathname.startsWith('/communityportal'); + +// eslint-disable-next-line react/jsx-props-no-spreading + return isCommunityPortal ? :
; +} + + +const mapStateToProps = state => ({ + auth: state.auth, + userProfile: state.userProfile, + taskEditSuggestionCount: state.taskEditSuggestions.count, + role: state.role, + notification: state.notification, + darkMode: state.theme.darkMode, +}); + +export default connect(mapStateToProps, { + getHeaderData, + getAllRoles, + hasPermission, + getWeeklySummaries, +})(HeaderRenderer); \ No newline at end of file diff --git a/src/components/LBDashboard/BiddingOverview/BiddingOverview.module.css b/src/components/LBDashboard/BiddingOverview/BiddingOverview.module.css index a96655d64a..5bdec5c58b 100644 --- a/src/components/LBDashboard/BiddingOverview/BiddingOverview.module.css +++ b/src/components/LBDashboard/BiddingOverview/BiddingOverview.module.css @@ -330,7 +330,7 @@ /* Style for date inputs */ input[type="date"] { - width: 77%; + /* width: 77%; */ padding: 0.5rem; border: 1px solid #ccc; border-radius: 2px; diff --git a/src/components/LeaderBoard/Leaderboard.jsx b/src/components/LeaderBoard/Leaderboard.jsx index 61033bff3e..7a321b27b7 100644 --- a/src/components/LeaderBoard/Leaderboard.jsx +++ b/src/components/LeaderBoard/Leaderboard.jsx @@ -150,7 +150,7 @@ function LeaderBoard({ useEffect(() => { const checkAbbreviatedView = () => { - const isAbbrev = window.innerWidth < 1024; + const isAbbrev = window.innerWidth < window.screen.width * 0.75; setIsAbbreviatedView(isAbbrev); }; diff --git a/src/components/Projects/WBS/WBSDetail/EditTask/EditTaskModal.jsx b/src/components/Projects/WBS/WBSDetail/EditTask/EditTaskModal.jsx index d3207e49a3..ad16ec09af 100644 --- a/src/components/Projects/WBS/WBSDetail/EditTask/EditTaskModal.jsx +++ b/src/components/Projects/WBS/WBSDetail/EditTask/EditTaskModal.jsx @@ -19,6 +19,7 @@ import { toast } from 'react-toastify'; import TagsSearch from '../components/TagsSearch'; import ReadOnlySectionWrapper from './ReadOnlySectionWrapper'; import '../../../../Header/DarkMode.css' +import '../wbs.css' function EditTaskModal(props) { /* diff --git a/src/components/Projects/WBS/WBSDetail/wbs.css b/src/components/Projects/WBS/WBSDetail/wbs.css index 69486350d9..989df37e74 100644 --- a/src/components/Projects/WBS/WBSDetail/wbs.css +++ b/src/components/Projects/WBS/WBSDetail/wbs.css @@ -143,6 +143,7 @@ .taskName { height: auto; + text-align: left !important; } .taskDrop { @@ -229,9 +230,6 @@ } @media (min-width: 200px) and (max-width: 1496px) { - table.responsive { - table-layout: fixed; - } .flex-responsive { display: flex; diff --git a/src/components/Projects/WBS/WBSItem/WBSItem.jsx b/src/components/Projects/WBS/WBSItem/WBSItem.jsx index 2b4dd48796..f7cfded839 100644 --- a/src/components/Projects/WBS/WBSItem/WBSItem.jsx +++ b/src/components/Projects/WBS/WBSItem/WBSItem.jsx @@ -37,7 +37,7 @@ const WBSItem = ({ darkMode, index, name, wbsId, projectId, getPopupById, delete
{index}
- + {name} diff --git a/src/components/TSAForm/pages/TSAFormPage1.js b/src/components/TSAForm/pages/TSAFormPage1.js new file mode 100644 index 0000000000..4a37ea8ae8 --- /dev/null +++ b/src/components/TSAForm/pages/TSAFormPage1.js @@ -0,0 +1,549 @@ +import { useHistory } from 'react-router-dom'; +import { useState } from 'react'; + +function TSAFormPage1() { + const history = useHistory(); + const [errors, setErrors] = useState({ + email: false, + fullname: false, + professionaltitle: false, + professionalExperience: false, + areaofExpertise: false, + }); + + const clearError = field => { + setErrors(prev => ({ ...prev, [field]: false })); + }; + const isValidEmail = email => { + // Simple email regex (enough for most use cases) + return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); + }; + + const isValidFullName = name => { + const words = name.trim().split(/\s+/); + if (words.length < 2) return false; + + return words.every(word => /^[A-Z][a-zA-Z'-]*$/.test(word)); + }; + + const handleNextClick = () => { + const newErrors = { + email: false, + fullname: false, + professionaltitle: false, + professionalExperience: false, + areaofExpertise: false, + }; + + const email = document.querySelector('input[name="email"]'); + const fullname = document.querySelector('input[name="fullname"]'); + const title = document.querySelector('input[name="professionaltitle"]'); + const experience = document.querySelector('input[name="professionalExperience"]:checked'); + const expertise = document.querySelectorAll('input[name="areaofExpertise"]:checked'); + + if (!email || !email.value.trim() || !isValidEmail(email.value.trim())) { + newErrors.email = true; + } + + if (!fullname || !fullname.value.trim() || !isValidFullName(fullname.value.trim())) { + newErrors.fullname = true; + } + + if (!title || !title.value.trim()) newErrors.professionaltitle = true; + if (!experience) newErrors.professionalExperience = true; + if (expertise.length === 0) newErrors.areaofExpertise = true; + + setErrors(newErrors); + + const hasErrors = Object.values(newErrors).some(Boolean); + if (hasErrors) { + const firstErrorField = Object.entries(newErrors).find(([, val]) => val)?.[0]; + const el = document.querySelector(`[name="${firstErrorField}"]`); + if (el && el.scrollIntoView) el.scrollIntoView({ behavior: 'smooth', block: 'center' }); + return; + } + + history.push('/tsaformpage2'); + }; + return ( +
+ {/* Banner Box */} +
+
+
+ + {/* Introduction Content Box */} +
+ {/* Title Bar inside content box */} +
+ Technical Support and Advisory Volunteer Agreement +
+ + {/* Content */} +
+

+ This questionnaire is for those interested in helping support One Community as a member + of the all-volunteer Sustainable Infrastructure Technical Support and Advisory Team. It + includes an assessment of your strengths, interests, and availability, followed by our + standard volunteer agreement. +

+

It contains these 6 parts:

+
    +
  • General Questions
  • +
  • Interests and Involvement
  • +
  • Your Participation Preferences
  • +
  • Your Design Preferences
  • +
  • Scope and Rights of Termination
  • +
  • Open Source Agreements
  • +
  • Dispute Resolution and Agreement of Terms
  • +
+

+ We are only interested in working with people who are serious about helping and making a + difference, so please take your time to complete this accurately. All answers are + required and it could take as much as an hour. +

+

+ Your answers here will help our interns and volunteers identify if you'd be a good + person to ask for input on their specific projects and/or designs. +

+

+ * Indicates required question +

+
+
+ {/* Title + Intro */} +
+
+ General Questions +
+
+

These are the basics we need to understand your experience and areas of expertise.

+
+
+ + {/* Email */} +
+ + clearError('email')} + style={{ + width: '100%', + padding: '10px 0', + fontSize: '16px', + border: 'none', + borderBottom: '1px solid #ccc', + outline: 'none', + backgroundColor: 'transparent', + }} + /> + {errors.email && ( +
+ Please enter a valid email address +
+ )} +
+ + {/* Full Name */} +
+ + clearError('fullname')} + style={{ + width: '100%', + padding: '10px 0', + fontSize: '16px', + border: 'none', + borderBottom: '1px solid #ccc', + outline: 'none', + backgroundColor: 'transparent', + }} + /> + {errors.fullname && ( +
+ This field is required
+ Please enter your Full Name (first and last name required) +
+ )} +
+ + {/* Professional Tile */} +
+ + clearError('professionaltitle')} + style={{ + width: '100%', + padding: '10px 0', + fontSize: '16px', + border: 'none', + borderBottom: '1px solid #ccc', + outline: 'none', + backgroundColor: 'transparent', + }} + /> + {errors.professionaltitle && ( +
+ This field is required +
+ )} +
+ + {/* Years of Professional Experience */} +
+ + + {['1-5', '5-7', '7-9', '10-14', '15-24', '25+'].map(option => ( + + ))} + {errors.professionalExperience && ( +
Please select one
+ )} +
+ + {/* Areas of Expertise */} +
+
+ + Areas of Expertise: (Check all that apply) + + + * +
+ + {[ + '3D CAD modeling, drawings and assembly design', + 'Architecture', + 'Civil Engineering', + 'Construction', + 'Electrical Engineering', + 'Design and Planning Infrastructure Projects', + 'Fabrication Methods and Estimating', + 'HVAC', + 'Licensed Electrician', + 'Master Electrician', + 'Licensed Plumber', + 'Plumbing Design', + 'Master Plumber', + 'Master Carpenter', + 'Mechanical Analysis', + 'Mechanical Design', + 'Solar/Renewable/Clean Energy', + 'Structural and Design Calculations', + 'Structural Analysis', + 'Structural Design', + 'Other:', + ].map(option => ( +
+ + {option === 'Other:' && ( + + )} +
+ ))} + {errors.areaofExpertise && ( +
Please select at least one
+ )} +
+ + {/* Next Button */} +
+ +
+
+ ); +} + +export default TSAFormPage1; diff --git a/src/components/TSAForm/pages/TSAFormPage2.js b/src/components/TSAForm/pages/TSAFormPage2.js new file mode 100644 index 0000000000..9686d44ab2 --- /dev/null +++ b/src/components/TSAForm/pages/TSAFormPage2.js @@ -0,0 +1,740 @@ +import { useHistory } from 'react-router-dom'; +import { useState } from 'react'; + +function TSAFormPage2() { + const history = useHistory(); + + const [errors, setErrors] = useState({ + interested: false, + availability: false, + BuildingInfrastructure: false, + FoodInfrastructure: false, + EnergyInfrastructure: false, + StewardshipInfrastructure: false, + }); + + const clearError = field => { + setErrors(prev => ({ ...prev, [field]: false })); + }; + + const handleNextClick = () => { + const requiredGroups = [ + 'interested', + 'availability', + 'BuildingInfrastructure', + 'FoodInfrastructure', + 'EnergyInfrastructure', + 'StewardshipInfrastructure', + ]; + + const newErrors = {}; + let firstInvalid = null; + + requiredGroups.forEach(group => { + const isChecked = document.querySelector(`input[name="${group}"]:checked`); + newErrors[group] = !isChecked; + if (!isChecked && !firstInvalid) { + firstInvalid = group; + } + }); + + setErrors(newErrors); + + if (firstInvalid) { + const el = document.querySelector(`[name="${firstInvalid}"]`); + if (el?.scrollIntoView) { + el.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } + return; + } + + history.push('/tsaformpage3'); + }; + + return ( +
+ {/* Banner */} +
+
+
+ + {/* Title + Intro */} +
+
+ Interests and Involvement +
+
+

+ These questions help us understand your general availability and preferred areas to + help. +

+

+ * Indicates required question +

+
+
+ + {/* Overall Interest */} +
+ + + {['Somewhat Interested', 'Moderately Interested', 'Very Interested'].map(option => ( + + ))} + + {errors.interested && ( +
+ This field is required +
+ )} +
+ + {/* Availability */} +
+ + {[ + 'Limited Availability (My availability is unpredictable and/or likely to change)', + 'Somewhat Available (My availability is limited, but I am happy to help a few hours when I can)', + 'Moderately Available (I am busy but can provide a couple of hours a week for areas I am interested)', + 'Very Available (My schedule is consistent, I have more than a few hours a week to help, and this is not expected to change)', + 'Other:', + ].map(option => ( + + ))} + {errors.availability && ( +
This field is required
+ )} +
+ + {/* Area of Interest Section */} +
+ {/* Title Bar */} +
+ Areas of Interest +
+ + {/* Content */} +
+

+ How interested are you in the following open source development areas of our project? +

+
+
+ + {/* Building Infrastructure Interest Rating */} +
+ + +
+ Not Interested At All + Extremely Interested +
+ +
+ {Array.from({ length: 10 }, (_, i) => ( + + ))} +
+ + {errors.BuildingInfrastructure && ( +
+ Please select a rating +
+ )} +
+ + {/* Food Infrastructure Interest Rating */} +
+ + +
+ Not Interested At All + Extremely Interested +
+ +
+ {Array.from({ length: 10 }, (_, i) => ( + + ))} +
+ {errors.FoodInfrastructure && ( +
+ Please select a rating +
+ )} +
+ + {/* Energy infrastructure Interest Rating */} +
+ + +
+ Not Interested At All + Extremely Interested +
+ +
+ {Array.from({ length: 10 }, (_, i) => ( + + ))} +
+ {errors.EnergyInfrastructure && ( +
+ Please select a rating +
+ )} +
+ + {/* Stewardship infrastructure Interest Rating */} +
+ + +
+ Not Interested At All + Extremely Interested +
+ +
+ {Array.from({ length: 10 }, (_, i) => ( + + ))} +
+ {errors.StewardshipInfrastructure && ( +
+ Please select a rating +
+ )} +
+ + {/* Navigation Buttons */} +
+ {/* Back Button */} + + + {/* Next Button */} + +
+
+ ); +} + +export default TSAFormPage2; diff --git a/src/components/TSAForm/pages/TSAFormPage3.js b/src/components/TSAForm/pages/TSAFormPage3.js new file mode 100644 index 0000000000..3f2a912231 --- /dev/null +++ b/src/components/TSAForm/pages/TSAFormPage3.js @@ -0,0 +1,1151 @@ +import { useHistory } from 'react-router-dom'; +import { useState } from 'react'; + +function TSAFormPage3() { + const history = useHistory(); + + const [errors, setErrors] = useState({ + meetingAvailability: false, + creativeProcessParticipation: false, + collabCallsParticipation: false, + designReviewInterest: false, + techConsultationInterest: false, + VirtualReviewInterest: false, + DetailedVirtualReviewInterest: false, + DesignAndCalc: false, + ConceptualDesign: false, + VerifyingAnalyses: false, + timeCommitment: false, + }); + + const clearError = field => { + setErrors(prev => ({ ...prev, [field]: false })); + }; + const handleNextClick = () => { + const requiredGroups = [ + 'meetingAvailability', + 'creativeProcessParticipation', + 'collabCallsParticipation', + 'designReviewInterest', + 'techConsultationInterest', + 'VirtualReviewInterest', + 'DetailedVirtualReviewInterest', + 'DesignAndCalc', + 'ConceptualDesign', + 'VerifyingAnalyses', + 'timeCommitment', + ]; + + const newErrors = {}; + let firstInvalidField = null; + + requiredGroups.forEach(group => { + const checkedInputs = document.querySelectorAll(`input[name="${group}"]:checked`); + const isValid = checkedInputs.length > 0; + + newErrors[group] = !isValid; + + if (!isValid && !firstInvalidField) { + firstInvalidField = document.querySelector(`input[name="${group}"]`); + } + }); + + setErrors(newErrors); + + if (firstInvalidField) { + firstInvalidField.scrollIntoView({ behavior: 'smooth', block: 'center' }); + firstInvalidField.focus(); + return; + } + + history.push('/tsaformpage4'); + }; + + return ( +
+ {/* Banner */} +
+
+
+ + {/* Title + Intro */} +
+
+ Your Participation Preferences +
+
+

+ This section covers how interested you are in helping with the specific processes + related to your areas of interest from the previous page. If you aren't sure about + an area, just rate it a 1. +

+

+ * Indicates required question +

+
+
+ + {[ + { + name: 'creativeProcessParticipation', + question: 'Participation in the complete creative/development process?', + }, + { + name: 'collabCallsParticipation', + question: 'Participation in virtual collaborative calls and meetings?', + }, + ].map(item => ( +
+ + +
+ Not Interested At All + Extremely Interested +
+ +
+ {Array.from({ length: 10 }, (_, i) => ( + + ))} +
+ {errors[item.name] && ( +
+ Please select a rating +
+ )} +
+ ))} + + {/* Checkbox Question Section */} +
+ + + {[ + 'N/A (My interest is below a 7)', + "Can meet if needed (Send me your questions and I'll schedule a meeting if I can help)", + 'Happy to meet but not very available (Would love to help with 1-2 meetings a month)', + 'I think meetings are important and enjoy them, contact me with your questions and we can set up a schedule. (Several meetings a month, scheduled as needed)', + "I think meetings are essential, fun and I love to collaborate. Let's coordinate a regular weekly call! (Weekly scheduled meetings with set times and required attendance of all team members)", + ].map((option, index) => ( +
+ +
+ ))} + + {/* Other Option */} +
+ + +
+ {errors.meetingAvailability && ( +
+ Please select an option +
+ )} +
+ +
+ + +
+ Not Interested At All + Extremely Interested +
+ +
+ {Array.from({ length: 10 }, (_, i) => ( + + ))} +
+ {errors.designReviewInterest && ( +
+ Please select a rating +
+ )} +
+ +
+ + +
+ Not Interested At All + Extremely Interested +
+ +
+ {Array.from({ length: 10 }, (_, i) => ( + + ))} +
+ {errors.techConsultationInterest && ( +
+ Please select a rating +
+ )} +
+ +
+ + +
+ Not Interested At All + Extremely Interested +
+ +
+ {Array.from({ length: 10 }, (_, i) => ( + + ))} +
+ {errors.VirtualReviewInterest && ( +
+ Please select a rating +
+ )} +
+ +
+ + +
+ Not Interested At All + Extremely Interested +
+ +
+ {Array.from({ length: 10 }, (_, i) => ( + + ))} +
+ {errors.DetailedVirtualReviewInterest && ( +
+ Please select a rating +
+ )} +
+ +
+ + +
+ Not Interested At All + Extremely Interested +
+ +
+ {Array.from({ length: 10 }, (_, i) => ( + + ))} +
+ {errors.DesignAndCalc && ( +
+ Please select a rating +
+ )} +
+ +
+ + +
+ Not Interested At All + Extremely Interested +
+ +
+ {Array.from({ length: 10 }, (_, i) => ( + + ))} +
+ {errors.ConceptualDesign && ( +
+ Please select a rating +
+ )} +
+ +
+ + +
+ Not Interested At All + Extremely Interested +
+ +
+ {Array.from({ length: 10 }, (_, i) => ( + + ))} +
+ {errors.VerifyingAnalyses && ( +
+ Please select a rating +
+ )} +
+ + {/* Anything Else Box */} +
+ + +
+ + {/* Availability Time Commitment Box */} +
+ + + {[ + '1-5 hours a month', + '5-10 hours a month', + '2-3 hours a week', + '3-5 hours a week', + '5+ hours a week', + '10+ hours a week', + ].map(option => ( +
+ +
+ ))} + + {/* Other Option */} +
+ + +
+ {errors.timeCommitment && ( +
+ Please select an option +
+ )} +
+ + {/* Navigation Buttons */} +
+ {/* Back Button */} + + + {/* Next Button */} + +
+
+ ); +} + +export default TSAFormPage3; diff --git a/src/components/TSAForm/pages/TSAFormPage4.js b/src/components/TSAForm/pages/TSAFormPage4.js new file mode 100644 index 0000000000..2dfadc8ece --- /dev/null +++ b/src/components/TSAForm/pages/TSAFormPage4.js @@ -0,0 +1,749 @@ +import { useHistory } from 'react-router-dom'; +import { useState } from 'react'; + +function TSAFormPage4() { + const history = useHistory(); + const [errors, setErrors] = useState({ + interested: false, + availability: false, + BuildingInfrastructure: false, + FoodInfrastructure: false, + EnergyInfrastructure: false, + StewardshipInfrastructure: false, + }); + + const clearError = field => { + setErrors(prev => ({ ...prev, [field]: false })); + }; + const handleNextClick = () => { + const requiredGroups = [ + 'EstablishingRequirements', + 'ConceptualDesigns', + 'PreliminaryDesignReview', + 'DesignVerification', + 'FinalDesignReview', + 'Detaileddrawings', + ]; + const newErrors = {}; + let firstInvalid = null; + + requiredGroups.forEach(group => { + const isChecked = document.querySelector(`input[name="${group}"]:checked`); + newErrors[group] = !isChecked; + if (!isChecked && !firstInvalid) { + firstInvalid = group; + } + }); + + setErrors(newErrors); + + if (firstInvalid) { + const el = document.querySelector(`[name="${firstInvalid}"]`); + if (el?.scrollIntoView) { + el.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } + return; + } + + history.push('/tsaformpage5'); + }; + + return ( +
+ {/* Banner */} +
+
+
+ + {/* Title + Intro */} +
+
+ Your Design Preferences +
+
+

+ This section covers which areas of the design, construction & implementation process you + can and most want to contribute to. If you are not sure about an area, just rate it a 1. +

+

+ * Indicates required question +

+
+
+ + {/* Establishing requirements */} +
+ + +
+ + Can't or don't want +
to help here +
+ + Very much can and +
want to help here +
+
+ +
+ {Array.from({ length: 10 }, (_, i) => ( + + ))} +
+ {errors.EstablishingRequirements && ( +
This field is required
+ )} +
+ + {/* Conceptual Designs */} +
+ + +
+ + Can't or don't want +
to help here +
+ + Very much can and +
want to help here +
+
+ +
+ {Array.from({ length: 10 }, (_, i) => ( + + ))} +
+ {errors.ConceptualDesigns && ( +
This field is required
+ )} +
+ + {/* Preliminary Design Review */} +
+ + +
+ + Can't or don't want +
to help here +
+ + Very much can and +
want to help here +
+
+ +
+ {Array.from({ length: 10 }, (_, i) => ( + + ))} +
+ {errors.PreliminaryDesignReview && ( +
This field is required
+ )} +
+ + {/* Design Verification/Analysis */} +
+ + +
+ + Can't or don't want +
to help here +
+ + Very much can and +
want to help here +
+
+ +
+ {Array.from({ length: 10 }, (_, i) => ( + + ))} +
+ {errors.DesignVerification && ( +
This field is required
+ )} +
+ + {/* Final Design Review */} +
+ + +
+ + Can't or don't want +
to help here +
+ + Very much can and +
want to help here +
+
+ +
+ {Array.from({ length: 10 }, (_, i) => ( + + ))} +
+ {errors.FinalDesignReview && ( +
This field is required
+ )} +
+ + {/* Detailed drawings and/or statement of work (SOW) */} +
+ + +
+ + Can't or don't want +
to help here +
+ + Very much can and +
want to help here +
+
+ +
+ {Array.from({ length: 10 }, (_, i) => ( + + ))} +
+ {errors.Detaileddrawings && ( +
This field is required
+ )} +
+ + {/* Anything else */} +
+ + { + e.target.style.backgroundColor = '#3b6f87'; + }} + onBlur={e => { + e.target.style.borderBottom = '1px solid #ccc'; + }} + /> +
+ + {/* Navigation Buttons */} +
+ {/* Back Button */} + + + {/* Next Button */} + +
+
+ ); +} + +export default TSAFormPage4; diff --git a/src/components/TSAForm/pages/TSAFormPage5.js b/src/components/TSAForm/pages/TSAFormPage5.js new file mode 100644 index 0000000000..1ad4167212 --- /dev/null +++ b/src/components/TSAForm/pages/TSAFormPage5.js @@ -0,0 +1,579 @@ +import { useHistory } from 'react-router-dom'; +import { useState } from 'react'; + +function TSAFormPage5() { + const history = useHistory(); + + const [errors, setErrors] = useState({ + agreementone: false, + agreementtwo: false, + agreementthree: false, + agreementfour: false, + }); + + const clearError = field => { + setErrors(prev => ({ ...prev, [field]: false })); + }; + const handleNextClick = () => { + const requiredGroups = ['agreementone', 'agreementtwo', 'agreementthree', 'agreementfour']; + + const newErrors = {}; + let firstInvalid = null; + + requiredGroups.forEach(group => { + const isChecked = document.querySelector(`input[name="${group}"]:checked`); + newErrors[group] = !isChecked; + if (!isChecked && !firstInvalid) { + firstInvalid = group; + } + }); + + setErrors(newErrors); + + if (firstInvalid) { + const el = document.querySelector(`[name="${firstInvalid}"]`); + if (el?.scrollIntoView) { + el.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } + return; + } + + history.push('/tsaformpage6'); + }; + + return ( +
+ {/* Banner */} +
+
+
+ + {/* Title + Intro */} +
+
+ One Community Consultant / Volunteer Agreement +
+
+

+ On behalf of the Board of Directors of One Community, we are happy to extend this letter + confirming the terms of your engagement as a consultant/volunteer of ONE COMMUNITY for + good and valuable consideration and clarify the nature of the services you are to + provide. +
+
Please complete all of the following sections and add your digital signature at + the end before completing any work with us. +

+

+ * Indicates required question +

+
+
+ + {/* Scope */} +
+ + +
+ + {/* Agreement 1 */} +
+ + + + {errors.agreementone && ( +
+ Please agree to move forward +
+ )} +
+ + {/* Task */} +
+ + +
+ + {/* Agreement 2 */} +
+ + + + {errors.agreementtwo && ( +
+ Please agree to move forward +
+ )} +
+ + {/* Period of Engagement */} +
+ + +
+ + {/* Agreement 3 */} +
+ + + + {errors.agreementthree && ( +
+ Please agree to move forward +
+ )} +
+ + {/* Termination */} +
+ + +
+ + {/* Agreement 4 */} +
+ + + + {errors.agreementfour && ( +
+ Please agree to move forward +
+ )} +
+ + {/* Navigation Buttons */} +
+ {/* Back Button */} + + + {/* Next Button */} + +
+
+ ); +} + +export default TSAFormPage5; diff --git a/src/components/TSAForm/pages/TSAFormPage6.js b/src/components/TSAForm/pages/TSAFormPage6.js new file mode 100644 index 0000000000..46f3dda43a --- /dev/null +++ b/src/components/TSAForm/pages/TSAFormPage6.js @@ -0,0 +1,394 @@ +import { useHistory } from 'react-router-dom'; +import { useState } from 'react'; + +function TSAFormPage6() { + const history = useHistory(); + const [errors, setErrors] = useState({ + agreementfive: false, + agreementsix: false, + }); + + const clearError = field => { + setErrors(prev => ({ ...prev, [field]: false })); + }; + const handleNextClick = () => { + const requiredGroups = ['agreementfive', 'agreementsix']; + + const newErrors = {}; + let firstInvalid = null; + + requiredGroups.forEach(group => { + const isChecked = document.querySelector(`input[name="${group}"]:checked`); + newErrors[group] = !isChecked; + if (!isChecked && !firstInvalid) { + firstInvalid = group; + } + }); + + setErrors(newErrors); + + if (firstInvalid) { + const el = document.querySelector(`[name="${firstInvalid}"]`); + if (el?.scrollIntoView) { + el.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } + return; + } + + history.push('/tsaformpage7'); + }; + + return ( +
+ {/* Banner */} +
+
+
+ + {/* Title + Intro */} +
+
+ Open Source Agreements +
+
+ + +
+
+ + {/* Agreement 5 */} +
+ + + + {errors.agreementfour && ( +
+ Please agree to move forward +
+ )} +
+ + {/* Access to the Website */} +
+ + +
+ + {/* Agreement 6 */} +
+ + + + {errors.agreementfour && ( +
+ Please agree to move forward +
+ )} +
+ + {/* Navigation Buttons */} +
+ {/* Back Button */} + + + {/* Next Button */} + +
+
+ ); +} + +export default TSAFormPage6; diff --git a/src/components/TSAForm/pages/TSAFormPage7.js b/src/components/TSAForm/pages/TSAFormPage7.js new file mode 100644 index 0000000000..b0b72e874d --- /dev/null +++ b/src/components/TSAForm/pages/TSAFormPage7.js @@ -0,0 +1,485 @@ +import { useHistory } from 'react-router-dom'; +import { useState } from 'react'; + +function TSAFormPage7() { + const history = useHistory(); + + const [errors, setErrors] = useState({ + agreementseven: false, + agreementeight: false, + sign: false, + }); + + const clearError = field => { + setErrors(prev => ({ ...prev, [field]: false })); + }; + + const isValidFullName = name => { + const words = name.trim().split(/\s+/); + if (words.length < 2) return false; + return words.every(word => /^[A-Z][a-zA-Z'-]*$/.test(word)); + }; + + const handleNextClick = () => { + const newErrors = {}; + let firstInvalid = null; + + // Validate radio agreements + ['agreementseven', 'agreementeight'].forEach(group => { + const isChecked = document.querySelector(`input[name="${group}"]:checked`); + newErrors[group] = !isChecked; + if (!isChecked && !firstInvalid) { + firstInvalid = document.querySelector(`input[name="${group}"]`); + } + }); + + // Validate signature input + const signatureInput = document.querySelector('input[name="sign"]'); + const signatureValue = signatureInput?.value.trim(); + const isSignValid = isValidFullName(signatureValue); + + newErrors.sign = !isSignValid; + + if (!isSignValid && !firstInvalid) { + firstInvalid = signatureInput; + } + + setErrors(newErrors); + + if (firstInvalid) { + firstInvalid.scrollIntoView({ behavior: 'smooth', block: 'center' }); + firstInvalid.focus(); + return; + } + + history.push('/tsaformpage8'); + }; + + return ( +
+ {/* Banner */} +
+
+
+ + {/* Title + Intro */} +
+
+ Dispute Resolution & Agreement Of Terms +
+
+ + +
+
+ + {/* Agreement 7 */} +
+ + + + {errors.agreementseven && ( +
+ Please agree to move forward +
+ )} +
+ + {/* Agreement of Terms */} +
+ + +
+ + {/* Agreement 8 */} +
+ + + + {errors.agreementeight && ( +
+ Please agree to move forward +
+ )} +
+ + {/* Digital Signature */} +
+ + +
+ + {/* Sign your name */} +
+
+ + * +
+ + clearError('sign')} + required + style={{ + width: '100%', + padding: '10px 0', + fontSize: '16px', + border: 'none', + borderBottom: '1px solid #ccc', + outline: 'none', + backgroundColor: 'transparent', + }} + onFocus={e => { + e.target.style.borderBottom = '2px solid #4d87a1'; + }} + onBlur={e => { + e.target.style.borderBottom = '1px solid #ccc'; + }} + /> + {errors.sign && ( +
+ Please sign your full name to move forward. +
+ )} +
+ + {/* Navigation Buttons */} +
+ {/* Back Button */} + + + {/* Submit Button */} + +
+
+ ); +} + +export default TSAFormPage7; diff --git a/src/components/TSAForm/pages/TSAFormPage8.js b/src/components/TSAForm/pages/TSAFormPage8.js new file mode 100644 index 0000000000..a541680181 --- /dev/null +++ b/src/components/TSAForm/pages/TSAFormPage8.js @@ -0,0 +1,86 @@ +function TSAFormPage8() { + return ( +
+ {/* Banner */} +
+
+
+ + {/* Title + Intro */} +
+
+ Successfully Submitted! +
+
+ +
+
+
+ ); +} + +export default TSAFormPage8; diff --git a/src/components/TeamMemberTasks/TeamMemberTask.jsx b/src/components/TeamMemberTasks/TeamMemberTask.jsx index b5b8c150ef..d9dfec4a10 100644 --- a/src/components/TeamMemberTasks/TeamMemberTask.jsx +++ b/src/components/TeamMemberTasks/TeamMemberTask.jsx @@ -11,7 +11,7 @@ import { import CopyToClipboard from 'components/common/Clipboard/CopyToClipboard'; import { Table, Progress } from 'reactstrap'; -import { Link } from 'react-router-dom'; +import { Link, useHistory } from 'react-router-dom'; import hasPermission from 'utils/permissions'; import './style.css'; @@ -52,6 +52,7 @@ const TeamMemberTask = React.memo( const currentDate = moment.tz('America/Los_Angeles').startOf('day'); const dispatch = useDispatch(); const canSeeFollowUpCheckButton = userRole !== 'Volunteer'; + const history = useHistory(); const totalHoursRemaining = user.tasks.reduce((total, task) => { task.hoursLogged = task.hoursLogged || 0; @@ -123,6 +124,15 @@ const TeamMemberTask = React.memo( } }; + const handleReportClick = (event,to) => { + if (event.metaKey || event.ctrlKey || event.button === 1) { + return; + } + + event.preventDefault(); // prevent full reload + history.push(`/peoplereport/${to}`); + } + const openDetailModal = request => { dispatch(showTimeOffRequestModal(request)); }; @@ -279,6 +289,7 @@ const TeamMemberTask = React.memo( handleReportClick(event,user?.personId)} > handleReportClick(event,user?.personId)} > {completedTasks.length} diff --git a/src/components/Teams/Team.css b/src/components/Teams/Team.css index f41fca9a21..1631b7f951 100644 --- a/src/components/Teams/Team.css +++ b/src/components/Teams/Team.css @@ -11,7 +11,8 @@ thead { } #teams__active { - width: 10px; + width: 50px; /* Increase to provide space */ + text-align: center; } .centered-cell { @@ -35,6 +36,23 @@ thead { text-align: start; } +/* Targets 2nd column in team rows for better alignment and readability */ +.teams__tr td:nth-child(2) { + /* text-align: center; */ + vertical-align: middle; + font-size: 16px; + font-weight: 400; +} + +.teams__order--input div { + margin-top: 0; + display: flex; + align-items: center; + justify-content: center; + height: 100%; +} + + .teams__overview--top { background: transparent; @@ -126,8 +144,10 @@ tr.dark-mode:hover { } .usermanagement-actions-cell { - display: table-cell; - padding-left: 10px; + height: 35px; /* Set a proper height */ + padding: 5px 10px; + font-size: 14px; + line-height: normal; } @media (max-width: 768px) { @@ -139,5 +159,5 @@ tr.dark-mode:hover { } .overflow-container table { - min-width: 600px; -} + min-width: 600px; +} \ No newline at end of file diff --git a/src/components/Teams/Team.jsx b/src/components/Teams/Team.jsx index cb1411d9cb..242d37b94e 100644 --- a/src/components/Teams/Team.jsx +++ b/src/components/Teams/Team.jsx @@ -1,7 +1,8 @@ import './Team.css'; import hasPermission from 'utils/permissions'; -import { boxStyle } from 'styles'; +import { boxStyle, boxStyleDark } from 'styles'; import { connect, useSelector } from 'react-redux'; +import { Button } from 'reactstrap'; import { DELETE } from '../../languages/en/ui'; export function Team(props) { @@ -12,7 +13,7 @@ export function Team(props) { return ( -
{props.index + 1}
+
{(props.index ?? 0) + 1}
{props.name} @@ -25,10 +26,7 @@ export function Team(props) { } }} style={{ - all: 'unset', // Reset default button styles - cursor: 'pointer', - width: '100%', - height: '100%', + boxStyle, }} aria-label={`Change status for team ${props.name}`} > @@ -52,28 +50,28 @@ export function Team(props) { {(canDeleteTeam || canPutTeam) && ( - + - + )} diff --git a/src/components/Teams/TeamMembersPopup.jsx b/src/components/Teams/TeamMembersPopup.jsx index a4a5d5a0f6..1f042da284 100644 --- a/src/components/Teams/TeamMembersPopup.jsx +++ b/src/components/Teams/TeamMembersPopup.jsx @@ -24,6 +24,7 @@ import styles from './ToggleSwitch/ToggleSwitch.module.scss'; export const TeamMembersPopup = React.memo(props => { const darkMode = useSelector(state => state.theme.darkMode); + const hasVisibilityIconPermission = hasPermission('seeVisibilityIcon'); const [isChecked, setIsChecked] = useState(1); // 0 = false, 1 = true, 2 = all const [checkedStatus, setCheckedStatus] = useState('Active'); // 0 = false, 1 = true, 2 = all const [selectedUser, setSelectedUser] = useState(undefined); @@ -39,12 +40,13 @@ export const TeamMembersPopup = React.memo(props => { setDeletedPopup(!deletedPopup); }; + const handleDelete = id => { props.onDeleteClick(`${id}`); setDeletedPopup(true); }; - const handleToggle = () => { + const handleToggle = (event) => { setIsChecked(parseInt(event.target.value)); setCheckedStatus( parseInt(event.target.value) == 0 @@ -65,7 +67,7 @@ export const TeamMembersPopup = React.memo(props => { setMemberList([]); props.onClose(); setSortOrder(0); - setIsChecked(true); + setIsChecked(1); setCheckedStatus('Active'); }; const onAddUser = () => { @@ -271,27 +273,35 @@ export const TeamMembersPopup = React.memo(props => { > - -
-
- - {checkedStatus} -
-
- - # - User Name + + + + # + User Name Date Added{' '} { - {props.fetching && ( - - - - - - )} - - {!props.fetching && memberList.length === 0 && emptyState} - - {!props.fetching && - memberList.length > 0 && - Array.isArray(props.members.teamMembers) && - props.members.teamMembers.length > 0 && - memberList.toSorted().map(user => ( - - -
-