Make project pages responsive

pull/6513/head
Kamran Ahmed 3 months ago
parent 377c86d8ad
commit 364c138fac
  1. 22
      src/components/Projects/ListProjectSolutions.tsx
  2. 73
      src/components/Projects/ProjectTabs.tsx
  3. 2
      src/components/Projects/VoteButton.tsx
  4. 6
      src/pages/projects/[projectId]/solutions.astro

@ -3,22 +3,14 @@ import { useToast } from '../../hooks/use-toast';
import { httpGet, httpPost } from '../../lib/http';
import { LoadingSolutions } from './LoadingSolutions';
import { EmptySolutions } from './EmptySolutions';
import {
ArrowDown,
ArrowUp,
CalendarCheck,
ThumbsDown,
ThumbsUp,
} from 'lucide-react';
import { ThumbsDown, ThumbsUp } from 'lucide-react';
import { getRelativeTimeString } from '../../lib/date';
import { Pagination } from '../Pagination/Pagination';
import { deleteUrlParam, getUrlParams, setUrlParams } from '../../lib/browser';
import { pageProgressMessage } from '../../stores/page';
import { cn } from '../../lib/classname';
import { LeavingRoadmapWarningModal } from './LeavingRoadmapWarningModal';
import { isLoggedIn } from '../../lib/jwt';
import { showLoginPopup } from '../../lib/popup';
import { CheckIcon } from '../ReactIcons/CheckIcon.tsx';
import { VoteButton } from './VoteButton.tsx';
import { GitHubIcon } from '../ReactIcons/GitHubIcon.tsx';
@ -239,7 +231,7 @@ export function ListProjectSolutions(props: ListProjectSolutionsProps) {
<section>
{leavingRoadmapModal}
<div className="flex flex-col divide-y divide-gray-100 min-h-[500px]">
<div className="flex min-h-[500px] flex-col divide-y divide-gray-100">
{solutions?.data.map((solution, counter) => {
const isVisited = alreadyVisitedSolutions[solution._id!];
const avatar = solution.user.avatar || '';
@ -247,7 +239,7 @@ export function ListProjectSolutions(props: ListProjectSolutionsProps) {
return (
<div
key={solution._id}
className="group flex items-center justify-between py-2 text-sm text-gray-500"
className="flex flex-col justify-between gap-2 py-2 text-sm text-gray-500 sm:flex-row sm:items-center sm:gap-0"
>
<div className="flex items-center gap-1.5">
<img
@ -262,15 +254,17 @@ export function ListProjectSolutions(props: ListProjectSolutionsProps) {
<span className="font-medium text-black">
{solution.user.name}
</span>
<span className="hidden sm:inline">
{submittedAlternatives[
counter % submittedAlternatives.length
] || 'submitted their solution'}{' '}
<span className="font-medium text-black">
] || 'submitted their solution'}
</span>{' '}
<span className="text-gray-400 text-right sm:text-left flex-grow sm:flex-grow-0 sm:font-medium sm:text-black">
{getRelativeTimeString(solution?.submittedAt!)}
</span>
</div>
<div className="5 flex items-center gap-1">
<div className="flex items-center justify-center sm:justify-end gap-1">
<span className="flex items-center overflow-hidden rounded-full border">
<VoteButton
icon={ThumbsUp}

@ -1,48 +1,69 @@
import { cn } from '../../lib/classname';
import { Blocks, BoxSelect, StickyNote, Text } from 'lucide-react';
import {
Blocks,
BoxSelect,
type LucideIcon,
StickyNote,
Text,
} from 'lucide-react';
export const allowedProjectTabs = ['details', 'solutions'] as const;
export type AllowedProjectTab = (typeof allowedProjectTabs)[number];
type ProjectTabsProps = {
activeTab: AllowedProjectTab;
projectId: string;
type TabButtonProps = {
text: string;
icon: LucideIcon;
smText?: string;
isActive?: boolean;
href: string;
};
export function ProjectTabs(props: ProjectTabsProps) {
const { activeTab, projectId } = props;
const tabs = [
{ name: 'Project Details', value: 'details', icon: Text },
{ name: 'Community Solutions', value: 'solutions', icon: Blocks },
];
return (
<div className="my-3 flex flex-row flex-wrap items-center gap-1.5 rounded-md border bg-white px-2.5 text-sm">
{tabs.map((tab) => {
const isActive = tab.value === activeTab;
function TabButton(props: TabButtonProps) {
const { text, icon: ButtonIcon, smText, isActive, href } = props;
return (
<a
key={tab.value}
href={
tab.value === 'details'
? `/projects/${projectId}`
: `/projects/${projectId}/${tab.value}`
}
href={href}
className={cn('relative flex items-center gap-1 p-2', {
'text-black': isActive,
'opacity-40 hover:opacity-90': !isActive,
})}
>
{tab.icon && <tab.icon className="mr-1 inline-block h-4 w-4" />}
{tab.name}
{ButtonIcon && <ButtonIcon className="mr-1 inline-block h-4 w-4" />}
<span className="hidden sm:inline">{text}</span>
{smText && <span className="sm:hidden">{smText}</span>}
{isActive && (
<span className="absolute bottom-0 left-0 right-0 h-0.5 translate-y-1/2 bg-black"></span>
<span className="absolute bottom-0 left-0 right-0 h-0.5 translate-y-1/2 bg-black rounded-t-md"></span>
)}
</a>
);
})}
}
type ProjectTabsProps = {
activeTab: AllowedProjectTab;
projectId: string;
};
export function ProjectTabs(props: ProjectTabsProps) {
const { activeTab, projectId } = props;
return (
<div className="my-3 flex flex-row flex-wrap items-center gap-1.5 rounded-md border bg-white px-2.5 text-sm">
<TabButton
text={'Project Detail'}
icon={Text}
smText={'Details'}
isActive={activeTab === 'details'}
href={`/projects/${projectId}`}
/>
<TabButton
text={'Community Solutions'}
icon={Blocks}
smText={'Solutions'}
isActive={activeTab === 'solutions'}
href={`/projects/${projectId}/solutions`}
/>
</div>
);
}

@ -12,7 +12,7 @@ export function VoteButton(props: VoteButtonProps) {
return (
<button
className={cn(
'flex items-center gap-1 px-2 py-1 text-sm text-gray-500 hover:bg-gray-100 hover:text-black',
'flex items-center gap-1 px-2 py-1 text-sm text-gray-500 hover:bg-gray-100 hover:text-black focus:outline-none',
{
'bg-gray-100 text-orange-600 hover:text-orange-700': isActive,
'bg-transparent text-gray-500 hover:text-black': !isActive,

@ -49,12 +49,14 @@ const githubUrl = `https://github.com/kamranahmedse/developer-roadmap/tree/maste
<div class='container'>
<ProjectTabs projectId={projectId} activeTab='solutions' />
<div class='mb-4 overflow-hidden rounded-lg border bg-white p-5'>
<div class='mb-4 overflow-hidden rounded-lg border bg-white p-3 sm:p-5'>
<div class='relative mb-5'>
<h1 class='mb-1 text-xl font-semibold'>
{projectData.title} Solutions
</h1>
<p class='text-sm text-gray-500'>{projectData.description}</p>
<p class='hidden text-sm text-gray-500 sm:block'>
{projectData.description}
</p>
</div>
<ListProjectSolutions projectId={projectId} client:load />

Loading…
Cancel
Save