Course content responsiveness

feat/ai-courses
Kamran Ahmed 1 month ago
parent a79e66742b
commit 6d4717127a
  1. 42
      src/components/GenerateCourse/AICourseContent.tsx
  2. 5
      src/components/GenerateCourse/AICourseFollowUpPopover.tsx
  3. 28
      src/components/GenerateCourse/AICourseLimit.tsx
  4. 10
      src/helper/number.ts

@ -122,27 +122,33 @@ export function AICourseContent(props: AICourseContentProps) {
aria-label="Back to generator"
>
<ChevronLeft className="size-4" strokeWidth={2.5} />
Back to Generator
Back<span className="hidden lg:inline"> to Generator</span>
</a>
<button
onClick={() => setSidebarOpen(!sidebarOpen)}
className="flex items-center justify-center text-gray-400 shadow-sm transition-colors hover:bg-gray-50 hover:text-gray-900 lg:hidden"
>
{sidebarOpen ? (
<X size={17} strokeWidth={3} />
) : (
<Menu size={17} strokeWidth={3} />
)}
</button>
<div className="flex items-center gap-2">
<div className="flex flex-row lg:hidden">
<AICourseLimit />
</div>
<button
onClick={() => setSidebarOpen(!sidebarOpen)}
className="flex items-center justify-center text-gray-400 shadow-sm transition-colors hover:bg-gray-50 hover:text-gray-900 lg:hidden"
>
{sidebarOpen ? (
<X size={17} strokeWidth={3} />
) : (
<Menu size={17} strokeWidth={3} />
)}
</button>
</div>
</div>
</div>
<header className="flex h-[80px] items-center justify-between border-b border-gray-200 bg-white px-6">
<header className="flex items-center justify-between border-b border-gray-200 bg-white px-6 max-lg:py-4 lg:h-[80px]">
<div className="flex items-center">
<div className="flex flex-col">
<h1 className="text-xl font-bold text-gray-900">
<h1 className="text-balance text-xl font-bold !leading-tight text-gray-900 max-lg:mb-0.5 max-lg:text-lg">
{course.title || 'Loading Course...'}
</h1>
<div className="mt-1 flex flex-row items-center gap-2 text-sm text-gray-600">
<div className="mt-1 flex flex-row items-center gap-2 text-sm text-gray-600 max-lg:text-xs">
<span className="font-medium">{totalModules} modules</span>
<span className="text-gray-400"></span>
<span className="font-medium">{totalCourseLessons} lessons</span>
@ -157,8 +163,10 @@ export function AICourseContent(props: AICourseContentProps) {
</div>
</div>
</div>
<div className="flex gap-3">
<AICourseLimit />
<div className="flex gap-2">
<div className="hidden gap-2 lg:flex">
<AICourseLimit />
</div>
{viewMode === 'module' && (
<button
@ -236,7 +244,7 @@ export function AICourseContent(props: AICourseContentProps) {
<main
className={cn(
'flex-1 overflow-y-auto p-6 transition-all duration-200 ease-in-out',
'flex-1 overflow-y-auto p-6 transition-all duration-200 ease-in-out max-lg:p-3',
sidebarOpen ? 'lg:ml-0' : '',
)}
>

@ -215,7 +215,10 @@ export function AICourseFollowUpPopover(props: AICourseFollowUpPopoverProps) {
<div className="mb-1 mt-0.5">
<div className="grid grid-cols-2 gap-2">
{capabilities.map((capability, index) => (
<CapabilityCard key={index} {...capability} />
<CapabilityCard
key={`capability-${index}`}
{...capability}
/>
))}
</div>
</div>

@ -1,19 +1,11 @@
import { useQuery } from '@tanstack/react-query';
import {
BookIcon,
BookOpenIcon,
MessageCircleQuestionIcon,
ChevronDownIcon,
ClockIcon,
BotIcon,
} from 'lucide-react';
import { useState, useRef } from 'react';
import { useState } from 'react';
import { getAiCourseLimitOptions } from '../../queries/ai-course';
import { queryClient } from '../../stores/query-client';
import { useOutsideClick } from '../../hooks/use-outside-click';
import { UpgradeAccountModal } from '../Billing/UpgradeAccountModal';
import { billingDetailsOptions } from '../../queries/billing';
import { getPercentage } from '../../helper/number';
import { Gift, Info } from 'lucide-react';
export function AICourseLimit() {
const [showUpgradeModal, setShowUpgradeModal] = useState(false);
@ -28,7 +20,7 @@ export function AICourseLimit() {
if (isLoading || !limits || isBillingDetailsLoading || !userBillingDetails) {
return (
<div className="h-[38px] w-[208.09px] animate-pulse rounded-lg border border-gray-200 bg-gray-200"></div>
<div className="hidden h-[38px] w-[208.09px] animate-pulse rounded-lg border border-gray-200 bg-gray-200 lg:block"></div>
);
}
@ -38,24 +30,30 @@ export function AICourseLimit() {
return (
<>
<div className="relative flex h-full min-h-[38px] cursor-pointer items-center overflow-hidden rounded-lg border border-gray-200 px-3 py-1.5 text-sm hover:bg-gray-50">
<button className="mr-1 flex items-center gap-1 text-sm font-medium lg:hidden underline underline-offset-2">
<Info className="size-4" />
{totalPercentage}% of limit used
</button>
<button className="relative hidden h-full min-h-[38px] cursor-pointer items-center overflow-hidden rounded-lg border border-gray-300 px-3 py-1.5 text-sm hover:bg-gray-50 lg:flex">
<span className="relative z-10">
{totalPercentage}% of the daily limit used
</span>
<div
className="absolute inset-0 h-full bg-gray-50"
className="absolute inset-0 h-full bg-gray-200/80"
style={{
width: `${totalPercentage}%`,
}}
></div>
</div>
</button>
{userBillingDetails.status === 'none' && (
<>
<button
className="ml-2 rounded-md border border-gray-200 px-2 py-1 text-sm hover:bg-gray-50"
className="hidden items-center justify-center gap-1 rounded-md bg-yellow-400 px-4 py-1 text-sm font-medium underline-offset-2 hover:bg-yellow-500 lg:flex"
onClick={() => setShowUpgradeModal(true)}
>
<Gift className="size-4" />
Upgrade
</button>

@ -1,12 +1,12 @@
export function getPercentage(portion: number, total: number): string {
export function getPercentage(portion: number, total: number): number {
if (portion <= 0 || total <= 0) {
return '0.00';
return 0;
}
if (portion >= total) {
return '100.00';
return 100;
}
const percentage = (portion / total) * 100;
return percentage.toFixed(2);
return Math.round(percentage);
}

Loading…
Cancel
Save