Skip to content
Merged
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
5 changes: 4 additions & 1 deletion src/pages/researcher/ApplicationHub.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import { Link, useSearchParams } from 'react-router-dom';
import { api } from '../../api.js';
import { useAuth } from '../../auth.jsx';
import { Card, Badge, Button, Field, EmptyState } from '../../components/ui.jsx';
Expand Down Expand Up @@ -117,6 +117,9 @@ export default function ApplicationHub() {

return (
<div>
<p className="muted" style={{ margin: '0 0 0.5rem', fontSize: '0.82rem' }}>
Also in Grow: <Link to="/researcher/programs">Programs</Link> · <Link to="/researcher/competitions">Competitions</Link>
</p>
<h1 className="page-title">Apply for a role</h1>
<p className="page-sub">
Explore advanced roles and apply. A Moderator reviews each application — once approved, your role
Expand Down
3 changes: 1 addition & 2 deletions src/pages/researcher/Community.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,10 @@ function Composer({ onPosted }) {
<img src={imageSrc(imageUrl)} alt="" style={{ width: '100%', borderRadius: 12, maxHeight: 300, objectFit: 'cover' }} />
<button
type="button"
className="btn btn-ghost btn-sm"
className="btn btn-ghost btn-sm img-dismiss"
onClick={() => setImageUrl('')}
title="Remove image"
aria-label="Remove image"
style={{ position: 'absolute', top: 8, right: 8, background: 'rgba(0,0,0,0.55)', color: '#fff', minWidth: 42, minHeight: 36 }}
>
<Icon name="x" size={14} />
</button>
Expand Down
1 change: 0 additions & 1 deletion src/pages/researcher/Competitions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ function FeaturedComp({ c }) {
const href = safeHref(c.url);
return (
<div className="comp-hero pop-in">
<div className="comp-hero-glow" aria-hidden="true" />
<div className="comp-hero-body">
<span className="comp-hero-tag"><span className="icon-label"><Icon name="star" size={14} /> Featured opportunity</span></span>
<h2 className="comp-hero-title">{c.title}</h2>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/researcher/GroupDetail.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export default function GroupDetail() {

{g.bannerUrl
? <img className="group-banner pop-in" src={imageSrc(g.bannerUrl)} alt="" onError={(e) => { e.currentTarget.style.display = 'none'; }} />
: <div className="group-banner pop-in" style={{ background: 'linear-gradient(135deg, var(--brand), var(--brand-deep))', border: 'none' }} aria-hidden="true" />}
: <div className="group-banner pop-in banner-ph" aria-hidden="true" />}

<div className="card-row" style={{ margin: '0.6rem 0 0.2rem', alignItems: 'center' }}>
<div className="row" style={{ gap: '0.7rem', alignItems: 'center', minWidth: 0 }}>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/researcher/Groups.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ function GroupCard({ g }) {
<Link to={`/researcher/groups/${g.id}`} className="card group-card" style={{ textDecoration: 'none', color: 'inherit' }}>
{g.bannerUrl
? <img className="group-card-banner" src={imageSrc(g.bannerUrl)} alt="" loading="lazy" onError={(e) => { e.currentTarget.style.display = 'none'; }} />
: <div className="group-card-banner" style={{ background: 'linear-gradient(135deg, var(--brand), var(--brand-deep))' }} aria-hidden="true" />}
: <div className="group-card-banner banner-ph" aria-hidden="true" />}
<div className="row" style={{ gap: '0.6rem', alignItems: 'center', marginBottom: '0.35rem' }}>
{g.logoUrl && <img className="group-logo" src={imageSrc(g.logoUrl)} alt="" loading="lazy" onError={(e) => { e.currentTarget.style.display = 'none'; }} />}
<h3 style={{ margin: 0 }}>{g.name}</h3>
Expand Down
1 change: 0 additions & 1 deletion src/pages/researcher/Mentors.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ export default function Mentors() {
return (
<div>
<div className="comp-hero">
<span className="comp-hero-glow" />
<div className="comp-hero-body">
<span className="comp-hero-tag icon-label"><Icon name="graduation-cap" size={13} /> Expertise Mentors</span>
<h1 className="comp-hero-title">Book a 1:1 with a subject-matter mentor</h1>
Expand Down
5 changes: 4 additions & 1 deletion src/pages/researcher/People.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ export default function People() {

return (
<div>
<h1 className="page-title">People</h1>
<div className="card-row" style={{ alignItems: 'flex-start' }}>
<h1 className="page-title" style={{ margin: 0 }}>People</h1>
<Link className="btn btn-ghost btn-sm" to="/researcher/mentors" style={{ flex: 'none' }}>Find a mentor</Link>
</div>
<p className="page-sub">Follow researchers and editors to get their updates — and message anyone directly.</p>

<div className="seg" style={{ marginBottom: '1.25rem' }}>
Expand Down
54 changes: 44 additions & 10 deletions src/pages/researcher/ProjectDetail.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,33 @@ import { useParams, Link } from 'react-router-dom';
import { api } from '../../api.js';
import { useAuth } from '../../auth.jsx';
import { Card, Badge, Button, Field, Pfp } from '../../components/ui.jsx';
import Icon from '../../components/Icon.jsx';
import { useToast } from '../../components/toast.jsx';
import { Embed, embedSrc } from '../../components/embed.jsx';

// Quiet "⋯" overflow for secondary per-row controls (remove/rename/settings-ish),
// so each view keeps one visible primary action. Same <details> pattern as
// SafetyMenu; reuses its popover styles for one consistent menu look.
function OverflowMenu({ label = 'More options', align = 'right', children }) {
return (
<details className="safety-menu">
<summary className="safety-menu-trigger" title={label} aria-label={label}>
<Icon name="more-horizontal" size={16} />
</summary>
<div className={`safety-menu-pop ${align === 'left' ? 'left' : ''}`}>{children}</div>
</details>
);
}

function MenuItem({ onClick, disabled, children }) {
const click = (e) => {
const d = e.currentTarget.closest('details');
if (d) d.open = false;
onClick?.(e);
};
return <button type="button" disabled={disabled} onClick={click}>{children}</button>;
}

const today = () => new Date().toISOString().slice(0, 10);
const isOverdue = (d, done) => d && !done && String(d).slice(0, 10) < today();
const fmtDate = (d) => (d ? new Date(`${String(d).slice(0, 10)}T12:00:00`).toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' }) : '');
Expand Down Expand Up @@ -149,7 +173,7 @@ function Overview({ project, pct, done, total, onJump }) {
<Card>
<h3 style={{ marginTop: 0 }}>Chosen direction</h3>
{chosen ? (
<div className="info-block" style={{ borderLeft: '3px solid var(--success)' }}>{chosen.text}</div>
<div className="info-block">{chosen.text}</div>
) : (
<p className="muted" style={{ margin: 0 }}>The team hasn't picked a research direction yet.</p>
)}
Expand Down Expand Up @@ -248,7 +272,7 @@ function TasksCard({ project, onChange }) {
) : (
<div className="stack" style={{ marginTop: '0.6rem' }}>
{ordered.map((t) => (
<div key={t.id} className="info-block" style={t.type === 'question' ? { borderLeft: '3px solid var(--brand)' } : undefined}>
<div key={t.id} className="info-block">
<div className="card-row">
<div className="row" style={{ gap: '0.4rem' }}>
<Badge tone={t.type === 'question' ? 'blue' : 'gray'}>{t.type}</Badge>
Expand Down Expand Up @@ -428,7 +452,11 @@ function CalRow({ it, onRsvp, onRemove, past }) {
</button>
)}
<Badge tone={overdue ? 'red' : CAL_TONE[it.kind] || 'gray'}>{overdue ? 'overdue' : it.kind}</Badge>
{it.canDelete && <button className="btn btn-ghost btn-sm" onClick={() => onRemove(it)}>Remove</button>}
{it.canDelete && (
<OverflowMenu label="Event options">
<MenuItem onClick={() => onRemove(it)}>Remove from calendar</MenuItem>
</OverflowMenu>
)}
</div>
</div>
</div>
Expand Down Expand Up @@ -493,7 +521,7 @@ function IdeasCard({ project, onChange }) {
<div className="stack">
{ideas.length === 0 && <p className="muted">No ideas yet — add the first one.</p>}
{ideas.map((i) => (
<div key={i.id} className="info-block" style={i.chosen ? { borderLeft: '3px solid var(--success)' } : undefined}>
<div key={i.id} className="info-block">
<div className="card-row">
<div>{i.text} {i.chosen && <Badge tone="green">chosen</Badge>}</div>
<div className="row" style={{ gap: '0.3rem' }}>
Expand Down Expand Up @@ -554,9 +582,11 @@ function LinksCard({ project, onChange }) {
<div key={l.id} className="info-block">
<div className="card-row">
<a href={l.url} target="_blank" rel="noreferrer">{l.label || l.url}</a>
<button className="btn btn-ghost btn-sm" onClick={() => remove(l.id)} disabled={deleting === l.id}>
{deleting === l.id ? '…' : 'Remove'}
</button>
<OverflowMenu label="Link options">
<MenuItem disabled={deleting === l.id} onClick={() => remove(l.id)}>
{deleting === l.id ? 'Removing…' : 'Remove link'}
</MenuItem>
</OverflowMenu>
</div>
{embedSrc(l.url) && <Embed url={l.url} height={300} title={l.label || 'Embedded preview'} />}
</div>
Expand Down Expand Up @@ -635,9 +665,13 @@ function RoleEditor({ projectId, member, onChange }) {
};
if (!editing) {
return (
<button className="btn btn-ghost btn-sm" style={{ marginTop: '0.25rem' }} onClick={() => { setTitle(member.role || ''); setEditing(true); }}>
{member.role ? 'Edit role' : '+ Set role'}
</button>
<div style={{ marginTop: '0.15rem' }}>
<OverflowMenu label={`Options for ${member.name}`} align="left">
<MenuItem onClick={() => { setTitle(member.role || ''); setEditing(true); }}>
{member.role ? 'Edit role' : 'Set role'}
</MenuItem>
</OverflowMenu>
</div>
);
}
return (
Expand Down
Loading
Loading