Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 122 additions & 9 deletions src/components/HGNHelpSkillsDashboard/TeamCard/TeamCard.jsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,84 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useState } from 'react';
import { useSelector } from 'react-redux';
import { toast } from 'react-toastify';
import styles from './TeamCard.module.css';
import { TeamMemberRow } from './TeamMemberRow';

function ConfirmModal({ action, count, onConfirm, onCancel }) {
return (
<div className={styles.modalOverlay}>
<div className={styles.modal}>
<h3 className={styles.modalTitle}>Confirm Action</h3>
<p className={styles.modalText}>
Are you sure you want to <strong>{action}</strong> to <strong>{count}</strong> member
{count > 1 ? 's' : ''}?
</p>
<div className={styles.modalActions}>
<button type="button" className={styles.confirmBtn} onClick={onConfirm}>
Yes, Proceed
</button>
<button type="button" className={styles.cancelBtn} onClick={onCancel}>
Cancel
</button>
</div>
</div>
</div>
);
}

ConfirmModal.propTypes = {
action: PropTypes.string.isRequired,
count: PropTypes.number.isRequired,
onConfirm: PropTypes.func.isRequired,
onCancel: PropTypes.func.isRequired,
};

export default function TeamCard() {
const teamMembers = [
{ name: 'Shreya Laheri', score: '9/10' },
{ name: 'Shreya Vithala', score: '7/10' },
{ name: 'Jae Sabol', score: '5/10' },
{ name: 'Sara Sabol', score: '2/10' },
{ id: 1, name: 'Shreya Laheri', score: '9/10' },
{ id: 2, name: 'Shreya Vithala', score: '7/10' },
{ id: 3, name: 'Jae Sabol', score: '5/10' },
{ id: 4, name: 'Sara Sabol', score: '2/10' },
];

const darkMode = useSelector(state => state.theme.darkMode);
const [selectedMembers, setSelectedMembers] = useState([]);
const [confirmAction, setConfirmAction] = useState(null);

const toggleSelect = id => {
setSelectedMembers(prev => (prev.includes(id) ? prev.filter(m => m !== id) : [...prev, id]));
};

const handleBulkAction = action => {
setConfirmAction(action);
};

const handleConfirm = () => {
// TODO: wire up real API calls when backend is ready
if (confirmAction === 'Send Message') {
toast.success(
`Message sent to ${selectedMembers.length} member${selectedMembers.length > 1 ? 's' : ''}!`,
);
} else if (confirmAction === 'Request Skill Update') {
toast.success(
`Skill update requested for ${selectedMembers.length} member${
selectedMembers.length > 1 ? 's' : ''
}!`,
);
}
setSelectedMembers([]);
setConfirmAction(null);
};

const handleCancel = () => {
setConfirmAction(null);
};

const noneSelected = selectedMembers.length === 0;

return (
<>
<div className={styles.teamCardWrapper}>
<div className={styles.teamCardContainer}>
<div className={styles.teamCardHeader}>
<h2 className={styles.teamCardTitle}>
Expand All @@ -33,12 +100,47 @@ export default function TeamCard() {
Your Team_______ rating
</h2>
</div>

{/* Bulk Action Bar */}
<div className={styles.bulkActionBar}>
<span className={styles.selectedCount}>
{noneSelected
? 'No members selected'
: `${selectedMembers.length} member${selectedMembers.length > 1 ? 's' : ''} selected`}
</span>
<div className={styles.bulkButtons}>
<button
type="button"
className={styles.bulkBtn}
disabled={noneSelected}
onClick={() => handleBulkAction('Send Message')}
>
Send Message
</button>
<button
type="button"
className={styles.bulkBtn}
disabled={noneSelected}
onClick={() => handleBulkAction('Request Skill Update')}
>
Request Skill Update
</button>
</div>
</div>

{/* Member Rows */}
<div>
{teamMembers.map((member, index) => (
<TeamMemberRow key={index} member={member} />
{teamMembers.map(member => (
<TeamMemberRow
key={member.id}
member={member}
isSelected={selectedMembers.includes(member.id)}
onToggleSelect={toggleSelect}
/>
))}
</div>
</div>

<div className={styles.showMoreSpan}>
<span>
<span className={styles.showMoreText}>Show your team member </span>
Expand All @@ -47,6 +149,17 @@ export default function TeamCard() {
</span>
</span>
</div>
</>

{/* Confirmation Modal */}
{confirmAction && (
<ConfirmModal
action={confirmAction}
count={selectedMembers.length}
onConfirm={handleConfirm}
onCancel={handleCancel}
darkMode={darkMode}
/>
)}
</div>
);
}
159 changes: 156 additions & 3 deletions src/components/HGNHelpSkillsDashboard/TeamCard/TeamCard.module.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
.teamCardWrapper {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 60vh;
}

.teamCardContainer {
width: 580px;
height: 449px;
display: flex;
flex-direction: column;
border-radius: 20px;
Expand All @@ -18,8 +25,8 @@

.teamCardTitle {
display: flex;
align-items: center; /* This will vertically align the icon and text */
gap: 8px; /* This adds some space between the icon and the text */
align-items: center;
gap: 8px;
color: #6e6e6e;
font-family: Inter, sans-serif;
font-size: 24px;
Expand All @@ -36,16 +43,77 @@
color: #6e6e6e;
}

/* ── Bulk Action Bar ── */
.bulkActionBar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 12px;
margin: 0 0 8px 0;
border: 1px solid #adb5bd;
border-radius: 8px;
background: transparent;
}

.selectedCount {
font-size: 14px;
font-weight: 500;
color: inherit;
}

.bulkButtons {
display: flex;
gap: 8px;
}

.bulkBtn {
padding: 7px 14px;
border: none;
border-radius: 4px;
background: #007bff;
color: #fff;
font-size: 13px;
cursor: pointer;
transition: background 0.2s;
}

.bulkBtn:hover:not(:disabled) {
background: #0056b3;
}

.bulkBtn:disabled {
background: #adb5bd;
cursor: not-allowed;
opacity: 0.7;
}

/* ── Member Row ── */
.teamMemberRow {
padding: 12px;
display: flex;
align-items: center;
justify-content: space-between;
border-radius: 8px;
transition: background 0.2s;
}

.teamMemberRowSelected {
background: rgba(0, 123, 255, 0.08);
}

.memberCheckbox {
width: 18px;
height: 18px;
margin-right: 12px;
cursor: pointer;
accent-color: #007bff;
flex-shrink: 0;
}

.teamMemberInfo {
display: flex;
align-items: center;
flex: 1;
}

.teamMemberName {
Expand Down Expand Up @@ -106,3 +174,88 @@
display: flex;
justify-content: space-evenly;
}

/* ── Confirmation Modal ── */
.modalOverlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}

.modal {
background: #fff;
border-radius: 8px;
padding: 28px 32px;
width: 380px;
max-width: 90%;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
text-align: center;
}

.modalTitle {
margin: 0 0 12px 0;
font-size: 1.2rem;
font-family: Inter, sans-serif;
color: #000;
}

.modalText {
font-size: 15px;
margin-bottom: 24px;
font-family: Inter, sans-serif;
color: #333;
}

.modalActions {
display: flex;
gap: 12px;
justify-content: center;
}

.confirmBtn {
padding: 9px 20px;
background: #007bff;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background 0.2s;
}

.confirmBtn:hover {
background: #0056b3;
}

.cancelBtn {
padding: 9px 20px;
background: #6c757d;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background 0.2s;
}

.cancelBtn:hover {
background: #5a6268;
}

@media (prefers-color-scheme: dark) {
.modal {
background: #2b2b2b;
}

.modalTitle {
color: #fff;
}

.modalText {
color: #ccc;
}
}
25 changes: 21 additions & 4 deletions src/components/HGNHelpSkillsDashboard/TeamCard/TeamMemberRow.jsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import React from 'react';
import { Mail } from 'lucide-react';
import PropTypes from 'prop-types';
import { SlackIcon } from './SlackIcon';
import styles from './TeamCard.module.css';

export const TeamMemberRow = ({ member }) => {
export const TeamMemberRow = ({ member, isSelected, onToggleSelect }) => {
const getScoreStyle = score => {
const scoreNum = parseInt(score);
const scoreNum = parseInt(score, 10);
const scoreColor = scoreNum >= 5 ? styles.scoreGreen : styles.scoreRed;
return `${styles.scoreBase} ${scoreColor}`;
};

return (
<div className={styles.teamMemberRow}>
<div className={`${styles.teamMemberRow} ${isSelected ? styles.teamMemberRowSelected : ''}`}>
<input
type="checkbox"
className={styles.memberCheckbox}
checked={isSelected}
onChange={() => onToggleSelect(member.id)}
aria-label={`Select ${member.name}`}
/>
<div className={styles.teamMemberInfo}>
<span className={styles.teamMemberName}>{member.name}</span>
<div className={styles.teamMemberIcons}>
Expand All @@ -23,3 +30,13 @@ export const TeamMemberRow = ({ member }) => {
</div>
);
};

TeamMemberRow.propTypes = {
member: PropTypes.shape({
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
score: PropTypes.string.isRequired,
}).isRequired,
isSelected: PropTypes.bool.isRequired,
onToggleSelect: PropTypes.func.isRequired,
};
Loading