Skip to content
Closed
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
12 changes: 12 additions & 0 deletions public/data/quotes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[
{ "text": "Programs must be written for people to read, and only incidentally for machines to execute.", "author": "Harold Abelson" },
{ "text": "Talk is cheap. Show me the code.", "author": "Linus Torvalds" },
{ "text": "First, solve the problem. Then, write the code.", "author": "John Johnson" },
{ "text": "Experience is the name everyone gives to their mistakes.", "author": "Oscar Wilde" },
{ "text": "In order to be irreplaceable, one must always be different.", "author": "Coco Chanel" },
{ "text": "Java is to JavaScript what car is to Carpet.", "author": "Chris Heilmann" },
{ "text": "Code is like humor. When you have to explain it, it’s bad.", "author": "Cory House" },
{ "text": "Fix the cause, not the symptom.", "author": "Steve Maguire" },
{ "text": "Optimism is an occupational hazard of programming: feedback is the treatment.", "author": "Kent Beck" },
{ "text": "Simplicity is the soul of efficiency.", "author": "Austin Freeman" }
]
58 changes: 33 additions & 25 deletions src/Homepage.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useState, useEffect, useRef } from 'react';
import Profile from './components/Profile/Profile';
import RandomQuote from './components/RandomQuote';
import ProfileSkeleton from './components/ProfileSkeleton/ProfileSkeleton';
import Search from './components/Search/Search';
import Sidebar from './components/Sidebar/Sidebar';
Expand Down Expand Up @@ -31,10 +32,9 @@ function App() {
} catch (error) {
console.error('Error fetching data:', error);
return [];
}
};
}

const combineData = async () => {
export default App;
setLoadingProfiles(true);
try {
const promises = filenames.map((file, index) =>
Expand Down Expand Up @@ -146,26 +146,34 @@ function App() {
return paginatedData.map((currentRecord, index) => <Profile data={currentRecord} key={index} />);
};

return currentUrl === '/' ? (
<div className="App flex flex-col bg-primaryColor dark:bg-secondaryColor md:flex-row">
<Sidebar />
<div className="w-full pl-5 pr-4 md:h-screen md:w-[77%] md:overflow-y-scroll md:py-7" ref={profilesRef}>
<Search onSearch={handleSearch} />
{profiles.length === 0 && searching ? <NoResultFound /> : renderProfiles()}
{combinedData.length > 0 && (
<Pagination
currentPage={currentPage}
totalPages={Math.ceil((searching ? profiles.length : shuffledProfiles.length) / recordsPerPage)}
onNextPage={handleNextPage}
onPrevPage={handlePrevPage}
/>
)}
if (currentUrl === '/') {
return (
<div className="App flex flex-col bg-primaryColor dark:bg-secondaryColor md:flex-row">
<Sidebar />
<div className="main-content">
<RandomQuote />
<Search onSearch={handleSearch} searching={searching} setSearching={setSearching} />
{searching ? (
<ProfileSkeleton />
) : (
<Profile
profiles={profiles}
loadingProfiles={loadingProfiles}
profilesRef={profilesRef}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
recordsPerPage={recordsPerPage}
shuffledProfiles={shuffledProfiles}
setShuffledProfiles={setShuffledProfiles}
NoResultFound={NoResultFound}
Pagination={Pagination}
/>
)}
</div>
{/* <GTranslateLoader /> */}
</div>
{/* <GTranslateLoader /> */}
</div>
) : (
<ErrorPage />
);
}

export default App;
);
} else {
return <ErrorPage />;
}
}
136 changes: 136 additions & 0 deletions src/components/RandomQuote.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import React from 'react';



const RandomQuote = () => {
const [quotes, setQuotes] = React.useState([]);
const [quoteIdx, setQuoteIdx] = React.useState(0);
const [fade, setFade] = React.useState(true);

React.useEffect(() => {
fetch('/data/quotes.json')
.then(res => res.json())
.then(data => {
setQuotes(data);
setQuoteIdx(Math.floor(Math.random() * data.length));
});
}, []);

function getRandomQuoteIdx(excludeIdx) {
if (!quotes.length) return 0;
let idx;
do {
idx = Math.floor(Math.random() * quotes.length);
} while (idx === excludeIdx && quotes.length > 1);
return idx;
}

const handleNewQuote = () => {
setFade(false);
setTimeout(() => {
setQuoteIdx(getRandomQuoteIdx(quoteIdx));
setFade(true);
}, 250);
};

if (!quotes.length) return null;
const quote = quotes[quoteIdx];

// Share/copy handlers
const [copied, setCopied] = React.useState(false);
const quoteText = `"${quote.text}" — ${quote.author}`;

const handleCopy = async () => {
try {
await navigator.clipboard.writeText(quoteText);
setCopied(true);
setTimeout(() => setCopied(false), 1200);
} catch (e) {
setCopied(false);
}
};

const handleShareTwitter = () => {
const url = `https://twitter.com/intent/tweet?text=${encodeURIComponent(quoteText)}`;
window.open(url, '_blank');
};

return (
<div style={{
background: 'linear-gradient(90deg, #e0e7ff 0%, #f3f4f6 100%)',
borderRadius: '12px',
padding: '1.5rem',
margin: '1.5rem 0',
textAlign: 'center',
boxShadow: '0 4px 16px rgba(0,0,0,0.07)',
maxWidth: 500,
marginLeft: 'auto',
marginRight: 'auto',
transition: 'box-shadow 0.2s',
position: 'relative',
}}>
<div style={{
fontSize: '2rem',
color: '#6366f1',
marginBottom: '0.5rem',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '0.5rem',
opacity: fade ? 1 : 0,
transition: 'opacity 0.25s',
}}>
<span role="img" aria-label="lightbulb">💡</span>
<span style={{ fontWeight: 600, fontSize: '1.1rem', color: '#22223b' }}>{quote.text}</span>
</div>
<div style={{
fontWeight: 500,
color: '#374151',
marginBottom: '1rem',
fontStyle: 'italic',
opacity: fade ? 1 : 0,
transition: 'opacity 0.25s',
}}>
— <span style={{ color: '#2563eb', fontWeight: 700 }}>{quote.author}</span>
</div>
<div style={{ display: 'flex', justifyContent: 'center', gap: '0.5rem', marginBottom: '1rem' }}>
<button onClick={handleCopy} style={{
background: copied ? '#22c55e' : '#6366f1',
color: 'white',
border: 'none',
borderRadius: '6px',
padding: '0.3rem 1rem',
cursor: 'pointer',
fontWeight: 500,
fontSize: '0.95rem',
transition: 'background 0.2s',
}}>{copied ? 'Copied!' : 'Copy Quote'}</button>
<button onClick={handleShareTwitter} style={{
background: '#1da1f2',
color: 'white',
border: 'none',
borderRadius: '6px',
padding: '0.3rem 1rem',
cursor: 'pointer',
fontWeight: 500,
fontSize: '0.95rem',
transition: 'background 0.2s',
}}>Share on Twitter</button>
</div>
<button onClick={handleNewQuote} style={{
background: 'linear-gradient(90deg, #6366f1 0%, #2563eb 100%)',
color: 'white',
border: 'none',
borderRadius: '6px',
padding: '0.4rem 1.2rem',
cursor: 'pointer',
fontWeight: 600,
fontSize: '1rem',
boxShadow: '0 2px 8px rgba(99,102,241,0.08)',
transition: 'background 0.2s',
}}>New Quote</button>
</div>
);
};

export default RandomQuote;