forked from UKSOURCE/hailearning.edu.vn
feat: complete publications and research sections and resolve conflicts
This commit is contained in:
79
app/components/research/search/ResearchSearchHeader.tsx
Normal file
79
app/components/research/search/ResearchSearchHeader.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
const FILTERS = ['Researchers', 'Labs', 'Projects', 'Institutes'];
|
||||
|
||||
const ResearchSearchHeader = () => {
|
||||
const [query, setQuery] = useState('');
|
||||
const [activeFilter, setActiveFilter] = useState('Researchers');
|
||||
const router = useRouter();
|
||||
|
||||
const handleSearch = () => {
|
||||
const params = new URLSearchParams({ q: query, type: activeFilter });
|
||||
router.push(`/research/search?${params.toString()}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<section id="repo-header">
|
||||
<div className="max-w-[1440px] mx-auto px-6 lg:px-8">
|
||||
|
||||
{/* Title row */}
|
||||
<div className="flex flex-col lg:flex-row justify-between items-start lg:items-end gap-6 mb-8 w-full">
|
||||
<div className="flex-1 max-w-3xl">
|
||||
<h1>Research Search</h1>
|
||||
<p>Search across researchers, labs, active projects, and institutes.</p>
|
||||
</div>
|
||||
<div className="flex gap-3 shrink-0">
|
||||
<button className="pub-btn-outline" onClick={() => router.back()}>
|
||||
<i className="fa-solid fa-arrow-left"></i>
|
||||
Back to Research
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Search bar */}
|
||||
<div className="pub-search-bar">
|
||||
<span className="pub-search-icon">
|
||||
<i className="fa-solid fa-magnifying-glass"></i>
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search researchers, labs, projects, and disciplines..."
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
|
||||
/>
|
||||
<div className="pub-search-btn-wrap">
|
||||
<button className="pub-btn-primary" onClick={handleSearch}>Search</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Filter tabs */}
|
||||
<div className="flex flex-wrap items-center gap-3 mt-5">
|
||||
<span className="text-xs font-bold uppercase tracking-widest" style={{ color: 'var(--pub-muted)' }}>
|
||||
Filter by:
|
||||
</span>
|
||||
{FILTERS.map((f) => (
|
||||
<button
|
||||
key={f}
|
||||
onClick={() => setActiveFilter(f)}
|
||||
className="pub-btn-outline"
|
||||
style={activeFilter === f ? {
|
||||
backgroundColor: 'var(--pub-blue-light)',
|
||||
color: 'var(--pub-blue)',
|
||||
borderColor: 'transparent',
|
||||
} : {}}
|
||||
>
|
||||
{f}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResearchSearchHeader;
|
||||
177
app/components/research/search/ResearchSearchResults.tsx
Normal file
177
app/components/research/search/ResearchSearchResults.tsx
Normal file
@@ -0,0 +1,177 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
const SORT_OPTIONS = ['Relevance', 'Newest First', 'Most Active', 'Alphabetical'];
|
||||
|
||||
const RESULTS = [
|
||||
{
|
||||
type: 'Project',
|
||||
badgeClass: 'pub-badge-open',
|
||||
badgeIcon: 'fa-flask',
|
||||
badgeLabel: 'Active Project',
|
||||
statusClass: 'pub-badge-peer',
|
||||
statusLabel: 'Environmental Studies',
|
||||
title: 'Urban Microclimate Modeling',
|
||||
lead: 'Dr. Alan Turing',
|
||||
updated: 'Updated 2 days ago',
|
||||
center: 'Institute for Climate Research',
|
||||
desc: 'Developing high-resolution predictive models for heat island effects in European metropolitan areas using machine learning and satellite data.',
|
||||
tags: ['Climate', 'Machine Learning', 'Urban Studies'],
|
||||
progress: 65,
|
||||
},
|
||||
{
|
||||
type: 'Lab',
|
||||
badgeClass: 'pub-badge-institutional',
|
||||
badgeIcon: 'fa-building-user',
|
||||
badgeLabel: 'Research Lab',
|
||||
statusClass: 'pub-badge-peer',
|
||||
statusLabel: 'Cognitive Science',
|
||||
title: 'Cognitive Sciences Lab',
|
||||
lead: 'Prof. Marie Curie',
|
||||
updated: 'Updated 1 week ago',
|
||||
center: 'Center for Digital Humanities',
|
||||
desc: 'Exploring the intersection of artificial intelligence and human psychology, with a focus on decision-making processes and behavioral modeling.',
|
||||
tags: ['AI', 'Psychology', 'Neuroscience'],
|
||||
progress: null,
|
||||
},
|
||||
{
|
||||
type: 'Project',
|
||||
badgeClass: 'pub-badge-request',
|
||||
badgeIcon: 'fa-gavel',
|
||||
badgeLabel: 'Data Collection',
|
||||
statusClass: 'pub-badge-peer',
|
||||
statusLabel: 'Law & Policy',
|
||||
title: 'Digital Rights in the EU Framework',
|
||||
lead: 'Dr. Elena Rostova',
|
||||
updated: 'Updated 1 week ago',
|
||||
center: 'Institute for Advanced European Studies',
|
||||
desc: 'A comparative analysis of member state implementation of digital privacy directives and their impact on civil liberties across the EU.',
|
||||
tags: ['Digital Rights', 'EU Law', 'Privacy'],
|
||||
progress: 30,
|
||||
},
|
||||
];
|
||||
|
||||
const ResearchSearchResults = () => {
|
||||
const [isSortOpen, setIsSortOpen] = useState(false);
|
||||
const [selectedSort, setSelectedSort] = useState('Relevance');
|
||||
|
||||
return (
|
||||
<div className="pub-results">
|
||||
|
||||
{/* Toolbar */}
|
||||
<div className="pub-results-toolbar">
|
||||
<p className="pub-results-count">
|
||||
Showing <strong>1–10</strong> of <strong>340</strong> results
|
||||
</p>
|
||||
<div className="pub-sort-wrap">
|
||||
<label>Sort by:</label>
|
||||
<div className="pub-sort-dropdown">
|
||||
<button
|
||||
className={`pub-sort-dropdown-btn ${isSortOpen ? 'open' : ''}`}
|
||||
onClick={() => setIsSortOpen(!isSortOpen)}
|
||||
>
|
||||
<span>{selectedSort}</span>
|
||||
<i className="fa-solid fa-chevron-down"></i>
|
||||
</button>
|
||||
{isSortOpen && (
|
||||
<div className="pub-sort-dropdown-menu">
|
||||
{SORT_OPTIONS.map((opt) => (
|
||||
<div
|
||||
key={opt}
|
||||
className={`pub-sort-dropdown-option ${selectedSort === opt ? 'selected' : ''}`}
|
||||
onClick={() => { setSelectedSort(opt); setIsSortOpen(false); }}
|
||||
>
|
||||
{opt}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Result cards */}
|
||||
<div className="pub-card-list">
|
||||
{RESULTS.map((item, idx) => (
|
||||
<div key={idx} className="pub-card">
|
||||
<div className="pub-card-top">
|
||||
<h3 className="pub-card-title">
|
||||
<a href="#">{item.title}</a>
|
||||
</h3>
|
||||
<div className="pub-badges">
|
||||
<span className={`pub-badge ${item.badgeClass}`}>
|
||||
<i className={`fa-solid ${item.badgeIcon}`}></i> {item.badgeLabel}
|
||||
</span>
|
||||
<span className="pub-badge pub-badge-peer">{item.statusLabel}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="pub-card-meta">
|
||||
<div className="pub-meta-item">
|
||||
<i className="fa-solid fa-user"></i>
|
||||
<strong>{item.lead}</strong>
|
||||
</div>
|
||||
<div className="pub-meta-item">
|
||||
<i className="fa-regular fa-clock"></i> {item.updated}
|
||||
</div>
|
||||
<div className="pub-meta-item">
|
||||
<i className="fa-solid fa-building-columns"></i> {item.center}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="pub-card-abstract">{item.desc}</p>
|
||||
|
||||
<div className="pub-keywords">
|
||||
<span className="pub-keywords-label">Tags:</span>
|
||||
{item.tags.map((tag) => (
|
||||
<span key={tag} className="pub-keyword-tag">{tag}</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{item.progress !== null && (
|
||||
<div style={{ marginBottom: '1rem' }}>
|
||||
<div className="flex justify-between text-xs mb-1" style={{ color: 'var(--pub-muted)' }}>
|
||||
<span>Progress</span>
|
||||
<span style={{ fontWeight: 600, color: 'var(--pub-text)' }}>{item.progress}%</span>
|
||||
</div>
|
||||
<div style={{ width: '100%', height: '6px', backgroundColor: 'var(--pub-bg)', borderRadius: '9999px' }}>
|
||||
<div style={{ width: `${item.progress}%`, height: '6px', backgroundColor: 'var(--pub-blue)', borderRadius: '9999px' }}></div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="pub-card-actions">
|
||||
<button className="pub-action-btn pub-action-btn-primary">
|
||||
<i className="fa-solid fa-arrow-right"></i> View Details
|
||||
</button>
|
||||
<button className="pub-action-btn pub-action-btn-secondary">
|
||||
<i className="fa-regular fa-bookmark"></i> Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Pagination */}
|
||||
<div className="pub-pagination">
|
||||
<button className="pub-page-btn" disabled>
|
||||
<i className="fa-solid fa-arrow-left"></i> Previous
|
||||
</button>
|
||||
<div className="pub-page-numbers">
|
||||
<button className="pub-page-num active">1</button>
|
||||
<button className="pub-page-num">2</button>
|
||||
<button className="pub-page-num">3</button>
|
||||
<span className="pub-page-ellipsis">...</span>
|
||||
<button className="pub-page-num">34</button>
|
||||
</div>
|
||||
<button className="pub-page-btn">
|
||||
Next <i className="fa-solid fa-arrow-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResearchSearchResults;
|
||||
94
app/components/research/search/ResearchSearchSidebar.tsx
Normal file
94
app/components/research/search/ResearchSearchSidebar.tsx
Normal file
@@ -0,0 +1,94 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
const DOMAINS = ['Law & Policy (42)', 'Cognitive Science (28)', 'Environmental Studies (35)', 'History & Humanities (50)', 'Economics (19)'];
|
||||
const STATUSES = ['Active', 'Data Collection', 'Review', 'Completed'];
|
||||
const TYPES = ['Researchers', 'Labs', 'Projects', 'Institutes'];
|
||||
|
||||
const AccordionSection = ({
|
||||
title,
|
||||
open,
|
||||
onToggle,
|
||||
children,
|
||||
}: {
|
||||
title: string;
|
||||
open: boolean;
|
||||
onToggle: () => void;
|
||||
children: React.ReactNode;
|
||||
}) => (
|
||||
<div className="pub-accordion-item">
|
||||
<button
|
||||
className={`pub-accordion-trigger ${open ? 'open' : ''}`}
|
||||
onClick={onToggle}
|
||||
>
|
||||
{title}
|
||||
<i className="fa-solid fa-chevron-down"></i>
|
||||
</button>
|
||||
<div className={`pub-accordion-body ${open ? 'open' : ''}`}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const ResearchSearchSidebar = () => {
|
||||
const [isDomainOpen, setIsDomainOpen] = useState(true);
|
||||
const [isStatusOpen, setIsStatusOpen] = useState(true);
|
||||
const [isTypeOpen, setIsTypeOpen] = useState(true);
|
||||
|
||||
return (
|
||||
<aside className="pub-sidebar">
|
||||
<div className="pub-sidebar-inner">
|
||||
|
||||
<div className="pub-sidebar-header">
|
||||
<h2>
|
||||
<i className="fa-solid fa-filter"></i> Filters
|
||||
</h2>
|
||||
<button className="pub-clear-btn">Clear All</button>
|
||||
</div>
|
||||
|
||||
<div className="pub-accordion">
|
||||
|
||||
{/* Research Domain */}
|
||||
<AccordionSection title="Research Domain" open={isDomainOpen} onToggle={() => setIsDomainOpen(!isDomainOpen)}>
|
||||
<div className="pub-filter-list">
|
||||
{DOMAINS.map((d, i) => (
|
||||
<label key={d} className="pub-filter-label">
|
||||
<input type="checkbox" className="pub-checkbox" defaultChecked={i === 0} />
|
||||
<span>{d}</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</AccordionSection>
|
||||
|
||||
{/* Project Status */}
|
||||
<AccordionSection title="Project Status" open={isStatusOpen} onToggle={() => setIsStatusOpen(!isStatusOpen)}>
|
||||
<div className="pub-filter-list">
|
||||
{STATUSES.map((s) => (
|
||||
<label key={s} className="pub-filter-label">
|
||||
<input type="checkbox" className="pub-checkbox" />
|
||||
<span>{s}</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</AccordionSection>
|
||||
|
||||
{/* Type */}
|
||||
<AccordionSection title="Type" open={isTypeOpen} onToggle={() => setIsTypeOpen(!isTypeOpen)}>
|
||||
<div className="pub-filter-list">
|
||||
{TYPES.map((t) => (
|
||||
<label key={t} className="pub-filter-label">
|
||||
<input type="checkbox" className="pub-checkbox" />
|
||||
<span>{t}</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</AccordionSection>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResearchSearchSidebar;
|
||||
Reference in New Issue
Block a user