UI responsiveness on AI roadmap generator

ai/roadmap
Kamran Ahmed 8 months ago
parent 1a7bbdde18
commit 87a3de3302
  1. 15
      src/components/GenerateRoadmap/GenerateRoadmap.tsx
  2. 29
      src/components/GenerateRoadmap/RoadmapSearch.tsx

@ -1,4 +1,4 @@
import { useEffect, useRef, useState, type FormEvent } from 'react'; import { type FormEvent, useEffect, useRef, useState } from 'react';
import './GenerateRoadmap.css'; import './GenerateRoadmap.css';
import { useToast } from '../../hooks/use-toast'; import { useToast } from '../../hooks/use-toast';
import { generateAIRoadmapFromText } from '../../../editor/utils/roadmap-generator'; import { generateAIRoadmapFromText } from '../../../editor/utils/roadmap-generator';
@ -8,7 +8,7 @@ import { readAIRoadmapStream } from '../../helper/read-stream';
import { isLoggedIn, removeAuthToken } from '../../lib/jwt'; import { isLoggedIn, removeAuthToken } from '../../lib/jwt';
import { RoadmapSearch } from './RoadmapSearch.tsx'; import { RoadmapSearch } from './RoadmapSearch.tsx';
import { Spinner } from '../ReactIcons/Spinner.tsx'; import { Spinner } from '../ReactIcons/Spinner.tsx';
import { Ban, Download, PenSquare, StopCircleIcon, Wand } from 'lucide-react'; import { Ban, Download, PenSquare, Wand } from 'lucide-react';
import { ShareRoadmapButton } from '../ShareRoadmapButton.tsx'; import { ShareRoadmapButton } from '../ShareRoadmapButton.tsx';
import { httpGet, httpPost } from '../../lib/http.ts'; import { httpGet, httpPost } from '../../lib/http.ts';
import { pageProgressMessage } from '../../stores/page.ts'; import { pageProgressMessage } from '../../stores/page.ts';
@ -20,7 +20,6 @@ import {
import { downloadGeneratedRoadmapImage } from '../../helper/download-image.ts'; import { downloadGeneratedRoadmapImage } from '../../helper/download-image.ts';
import { showLoginPopup } from '../../lib/popup.ts'; import { showLoginPopup } from '../../lib/popup.ts';
import { cn } from '../../lib/classname.ts'; import { cn } from '../../lib/classname.ts';
import { StopIcon } from '../ReactIcons/StopIcon.tsx';
const ROADMAP_ID_REGEX = new RegExp('@ROADMAPID:(\\w+)@'); const ROADMAP_ID_REGEX = new RegExp('@ROADMAPID:(\\w+)@');
@ -245,7 +244,7 @@ export function GenerateRoadmap() {
return ( return (
<section className="flex flex-grow flex-col bg-gray-100"> <section className="flex flex-grow flex-col bg-gray-100">
<div className="flex items-center justify-center border-b bg-white py-6"> <div className="flex items-center justify-center border-b bg-white py-3 sm:py-6">
{isLoading && ( {isLoading && (
<span className="flex items-center gap-2 rounded-full bg-black px-3 py-1 text-white"> <span className="flex items-center gap-2 rounded-full bg-black px-3 py-1 text-white">
<Spinner isDualRing={false} innerFill={'white'} /> <Spinner isDualRing={false} innerFill={'white'} />
@ -253,7 +252,7 @@ export function GenerateRoadmap() {
</span> </span>
)} )}
{!isLoading && ( {!isLoading && (
<div className="flex max-w-[600px] flex-grow flex-col items-center"> <div className="flex max-w-[600px] flex-grow flex-col items-center px-5">
<div className="mt-2 flex w-full items-center justify-between text-sm"> <div className="mt-2 flex w-full items-center justify-between text-sm">
<span className="text-gray-800"> <span className="text-gray-800">
<span <span
@ -283,7 +282,7 @@ export function GenerateRoadmap() {
</div> </div>
<form <form
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="my-3 flex w-full flex-row items-center justify-center gap-2" className="my-3 flex w-full flex-col sm:flex-row sm:items-center sm:justify-center gap-2"
> >
<input <input
type="text" type="text"
@ -298,7 +297,7 @@ export function GenerateRoadmap() {
<button <button
type={'submit'} type={'submit'}
className={cn( className={cn(
'ml-2 flex min-w-[127px] flex-shrink-0 items-center gap-2 rounded-md bg-black px-4 py-2 text-white', 'flex min-w-[127px] flex-shrink-0 items-center gap-2 rounded-md bg-black px-4 py-2 text-white justify-center',
{ {
'cursor-not-allowed opacity-50': 'cursor-not-allowed opacity-50':
!roadmapLimit || !roadmapLimit ||
@ -331,7 +330,7 @@ export function GenerateRoadmap() {
onClick={downloadGeneratedRoadmap} onClick={downloadGeneratedRoadmap}
> >
<Download size={15} /> <Download size={15} />
Download <span className="hidden sm:inline">Download</span>
</button> </button>
{roadmapId && ( {roadmapId && (
<ShareRoadmapButton <ShareRoadmapButton

@ -24,13 +24,19 @@ export function RoadmapSearch(props: RoadmapSearchProps) {
const canGenerateMore = limitUsed < limit; const canGenerateMore = limitUsed < limit;
return ( return (
<div className="flex flex-grow flex-col items-center justify-center py-6"> <div className="flex flex-grow flex-col items-center justify-center px-4 py-6 sm:px-6">
<div className="flex flex-col gap-2 text-center"> <div className="flex flex-col gap-0 text-center sm:gap-2">
<h1 className="relative text-3xl font-medium"> <h1 className="relative text-2xl font-medium sm:text-3xl">
Generate roadmaps with AI <span className="hidden sm:inline">Generate roadmaps with AI</span>
<span className="inline sm:hidden">AI Roadmap Generator</span>
</h1> </h1>
<p className="text-lg text-gray-500"> <p className="text-base text-gray-500 sm:text-lg">
<span className="hidden sm:inline">
Enter a topic and let the AI generate a roadmap for you Enter a topic and let the AI generate a roadmap for you
</span>
<span className="inline sm:hidden">
Enter a topic to generate a roadmap
</span>
</p> </p>
</div> </div>
<form <form
@ -41,7 +47,7 @@ export function RoadmapSearch(props: RoadmapSearchProps) {
e.preventDefault(); e.preventDefault();
} }
}} }}
className="my-6 flex w-full max-w-[600px] flex-row" className="my-3 flex w-full max-w-[600px] flex-col gap-2 sm:my-5 sm:flex-row"
> >
<input <input
autoFocus autoFocus
@ -53,7 +59,7 @@ export function RoadmapSearch(props: RoadmapSearchProps) {
/> />
<button <button
className={cn( className={cn(
'ml-2 flex min-w-[143px] flex-shrink-0 items-center justify-center gap-2 rounded-md bg-black px-4 py-2 text-white', 'flex min-w-[143px] flex-shrink-0 items-center justify-center gap-2 rounded-md bg-black px-4 py-2 text-white',
{ {
'cursor-not-allowed opacity-50': 'cursor-not-allowed opacity-50':
!limit || !roadmapTopic || limitUsed >= limit, !limit || !roadmapTopic || limitUsed >= limit,
@ -74,7 +80,7 @@ export function RoadmapSearch(props: RoadmapSearchProps) {
)} )}
{limit > 0 && !canGenerateMore && ( {limit > 0 && !canGenerateMore && (
<span className="flex items-center text-sm"> <span className="flex items-center text-base sm:text-sm">
<Ban size={15} className="mr-2" /> <Ban size={15} className="mr-2" />
Limit reached Limit reached
</span> </span>
@ -83,10 +89,11 @@ export function RoadmapSearch(props: RoadmapSearchProps) {
</form> </form>
<div className="mb-36"> <div className="mb-36">
<p className="text-gray-500"> <p className="text-gray-500">
You have generated{' '} <span className="inline sm:hidden">Generated </span>
<span className="hidden sm:inline">You have generated </span>
<span <span
className={cn( className={cn(
'inline-block w-[55px] rounded-md border px-0.5 text-center text-sm tabular-nums text-gray-800', 'inline-block w-[65px] rounded-md border px-0.5 text-center text-sm tabular-nums text-gray-800',
{ {
'animate-pulse border-zinc-300 bg-zinc-300 text-zinc-300': 'animate-pulse border-zinc-300 bg-zinc-300 text-zinc-300':
!limit, !limit,
@ -95,7 +102,7 @@ export function RoadmapSearch(props: RoadmapSearchProps) {
> >
{limitUsed} of {limit} {limitUsed} of {limit}
</span>{' '} </span>{' '}
roadmaps today. roadmaps.
{!isLoggedIn && ( {!isLoggedIn && (
<> <>
{' '} {' '}

Loading…
Cancel
Save