Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ const config: Config = {
items: [
{
label: "📚 E-books",
to: "https://learn.recodehive.com/datascience",
to: "/ebooks",
},
{
label: "🗺️ Roadmap",
Expand Down
238 changes: 238 additions & 0 deletions src/pages/ebooks/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
/* ------------------------------
Root Container
------------------------------ */

.ebook-container {
padding: 4rem 1.5rem;
max-width: 1280px;
margin: 0 auto;
font-family: var(--ifm-font-family-base);

}



/* ------------------------------
Hero Section
------------------------------ */
.ebook-hero {
text-align: center;
margin-bottom: 3rem;
animation: fadeIn 0.6s ease-in;
}

.ebook-title {
font-size: 3rem;
font-weight: 700;
color: var(--ifm-heading-color);
}

.ebook-subtitle {
color: var(--ifm-color-gray-700);
margin-top: 0.75rem;
font-size: 1.2rem;
max-width: 700px;
margin-left: auto;
margin-right: auto;
}

.ebook-search {
margin-top: 1.5rem;
padding: 0.9rem 1.3rem;
width: 60%;
max-width: 480px;
border-radius: 12px;
border: 1px solid var(--ifm-toc-border-color);
font-size: 1rem;
background-color: var(--ifm-background-color);
box-shadow: var(--ifm-global-shadow-lw);
transition: all 0.25s ease;
}

.ebook-search:focus {
border-color: var(--ifm-color-primary);
box-shadow: 0 0 0 4px rgba(0, 122, 255, 0.15);
outline: none;
}

/* ------------------------------
Grid Section
------------------------------ */
.ebook-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 2rem;
justify-items: center;
animation: fadeInUp 0.6s ease-in;
}

/* ------------------------------
Ebook Card
------------------------------ */
.ebook-card {
width: 100%;
max-width: 280px;
background: var(--ifm-card-background-color);
border-radius: 16px;
box-shadow: var(--ifm-global-shadow-lw);
overflow: hidden;
cursor: pointer;
display: flex;
flex-direction: column;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.ebook-card:hover {
transform: translateY(-8px);
box-shadow: var(--ifm-global-shadow-md);
}

/* ------------------------------
Image Section
------------------------------ */
.ebook-image-wrapper {
height: 220px;
overflow: hidden;
position: relative;
border-bottom: 1px solid var(--ifm-toc-border-color);
}

.ebook-image {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.35s ease;
}

.ebook-card:hover .ebook-image {
transform: scale(1.05);
}

/* ------------------------------
Content Section
------------------------------ */
.ebook-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: flex-start;
padding: 1.5rem 1.25rem;
}

.ebook-card-title {
font-weight: 600;
font-size: 1.3rem;
color: var(--ifm-heading-color);
margin-bottom: 0.6rem;
}

.ebook-card-desc {
color: var(--ifm-color-gray-700);
font-size: 1rem;
line-height: 1.55;
margin-bottom: 0.8rem;
}

/* ------------------------------
Category Tag
------------------------------ */
.ebook-category {
display: inline-block;
margin-top: 1rem;
font-size: 0.85rem;
font-weight: 500;
color: var(--ifm-color-primary);
background-color: rgba(0, 122, 255, 0.08);
padding: 0.4rem 0.8rem;
border-radius: 6px;
text-transform: uppercase;
letter-spacing: 0.5px;
align-self: flex-start;
}

/* ------------------------------
Button
------------------------------ */
.ebook-read-btn {
color: #fff;
background-color: var(--ifm-color-primary);
border: none;
border-radius: 10px;
padding: 0.7rem 1.3rem;
font-size: 0.95rem;
font-weight: 500;
cursor: pointer;
transition: background-color 0.25s ease, transform 0.2s ease;
align-self: flex-start;
}

.ebook-read-btn:hover {
transform: translateY(-2px);
background-color: #0056d2;
}

/* ------------------------------
No Results
------------------------------ */
.no-results {
text-align: center;
padding: 50px 0;
font-size: 1.2rem;
grid-column: 1 / -1;
}

@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}

to {
opacity: 1;
transform: translateY(0);
}
}

@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}

to {
opacity: 1;
transform: translateY(0);
}
}

@media (max-width: 768px) {
.ebook-search {
width: 90%;
}

.ebook-title {
font-size: 2.4rem;
}

.ebook-card {
max-width: 95%;
}
}

@media (min-width: 1024px) {
.ebook-grid {
grid-template-columns: repeat(4, 1fr);
}
}

@media (min-width: 768px) and (max-width: 1023px) {
.ebook-grid {
grid-template-columns: repeat(3, 1fr);
}
}

@media (max-width: 767px) {
.ebook-grid {
grid-template-columns: repeat(1, 1fr);
}
}
95 changes: 95 additions & 0 deletions src/pages/ebooks/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React, { useState } from 'react';
import Layout from '@theme/Layout';
import { useHistory } from '@docusaurus/router';
import './index.css';

interface Ebook {
id: string;
title: string;
description: string;
contentLink: string;
category: string;
}

// Sample Ebook Data
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this not seems feasible to me as this is only a mock data part , what we are expecting is an end to end solution . lets see what @sanjay-kv thinks

Copy link
Author

@Khushalsarode Khushalsarode Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actual link that was requested to be redirected from ebook tab.
Why do you think it's not "feasible", this is not mock data but the actual data.

Just it's like we are hard putting data into code because it's redirection links.
Tested more cards with mock data. Need to remove that comment
Please let me know if you have any suggestions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Khushalsarode @Adez017 No worries, it's correctly implemented as @sanjay-kv can add more books in the future.

const ebooks: Ebook[] = [
{
id: '1',
title: 'Mastering Data Science with Python',
description: 'Learn Data Science with examples and real-world projects.',
contentLink: 'https://learn.recodehive.com/datascience',
category: 'Programming',
},

];

// --------------------------
// Ebook Card Component
// --------------------------
const EbookCard: React.FC<{ ebook: Ebook }> = ({ ebook }) => {
const history = useHistory();

const handleClick = () => {
if (ebook.contentLink.startsWith('http')) {
window.open(ebook.contentLink, '_blank');
} else {
history.push(ebook.contentLink);
}
};

return (
<div className="ebook-card" onClick={handleClick}>

<div className="ebook-content">
<h3 className="ebook-card-title">{ebook.title}</h3>
<p className="ebook-card-desc">{ebook.description}</p>
<div className="ebook-category">{ebook.category}</div>
<button className="ebook-read-btn">📖 Read Now</button>
</div>
</div>
);
};

// --------------------------
// Main Ebook Page
// --------------------------
export default function EbookPage(): JSX.Element {
const [searchTerm, setSearchTerm] = useState('');

const filteredEbooks = ebooks.filter((ebook) =>
ebook.title.toLowerCase().includes(searchTerm.toLowerCase())
);

return (
<Layout title="Ebooks" description="Explore ebooks and learning resources">
<div className="ebook-container">
{/* Hero Section */}
<div className="ebook-hero">
<h1 className="ebook-title">📚 Explore Ebooks</h1>
<p className="ebook-subtitle">
Read high-quality ebooks on programming, tools, and development.
</p>
<input
type="text"
className="ebook-search"
placeholder="Search ebooks..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</div>

{/* Grid Section */}
<div className="ebook-grid">
{filteredEbooks.length === 0 && (
<div className="no-results">
<p>No ebooks found. Try a different search.</p>
</div>
)}
{filteredEbooks.map((ebook) => (
<EbookCard key={ebook.id} ebook={ebook} />
))}
</div>
</div>
</Layout>
);
}
Loading