Build a Motorcycle Shop - Build a Motorcycle Shop

Tell us what’s happening:

hello, test #18 to #24 are failing, even elements don’t render as i return them. i need help

Your code so far

<!-- file: index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>MotoShop - Find Your Perfect Ride</title>
    <link rel="stylesheet" href="./styles.css">
</head>
<body>
    <div id="app">
        <header class="bg-gradient-to-r from-gray-900 via-gray-800 to-gray-900 text-white">
            <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
                <div class="flex flex-col md:flex-row items-start md:items-center gap-4 md:gap-6">
                    <div class="flex items-center gap-3">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-bike-icon lucide-bike w-8 h-8 text-orange-500"><circle cx="18.5" cy="17.5" r="3.5"/><circle cx="5.5" cy="17.5" r="3.5"/><circle cx="15" cy="5" r="1"/><path d="M12 17.5V14l-3-3 4-3 2 3h2"/></svg>
                        <h1 class="text-3xl font-bold">MotoShop</h1>
                    </div>
                    <div class="flex-1 w-full md:w-auto">
                        <div class="relative">
                            <input
                                type="text"
                                id="name-filter-input"
                                placeholder="Search motorcycles by name..."
                                class="w-full md:w-96 px-4 py-2 pl-10 rounded-lg bg-gray-700 text-white placeholder-gray-400 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-orange-500 focus:border-transparent"
                            />
                            <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400">
                                <circle cx="11" cy="11" r="8"/>
                                <path d="m21 21-4.35-4.35"/>
                            </svg>
                        </div>
                    </div>
                </div>
            </div>
        </header>

        <section class="bg-gradient-to-r from-orange-600 via-orange-500 to-red-500 text-white py-20">
            <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
                <h2 class="text-5xl font-bold mb-4">Find Your Perfect Ride</h2>
                <p class="text-xl opacity-90 max-w-2xl mx-auto">
                    Explore our curated collection of premium motorcycles from the world's leading manufacturers
                </p>
            </div>
        </section>

        <main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">

            <div id="results-count" class="results-count">
                <p class="results-count-text">
                    Showing <span id="results-number" class="results-count-number">0</span> motorcycles
                </p>
            </div>

            <div id="loading-container" class="loading-container" style="display: none;">
                <div class="loading-spinner">
                   <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-loader-circle-icon lucide-loader-circle w-24 h-24 text-orange-500 animate-spin"><path d="M21 12a9 9 0 1 1-6.219-8.56"></path></svg>
                </div>
            </div>

            <div id="no-results" class="no-results" style="display: none;">
                <p class="no-results-text">
                    No motorcycles found matching your filters.
                </p>
            </div>

            <div id="motorcycle-grid" class="motorcycle-grid">
            </div>
        </main>

        <footer class="bg-gray-900 text-white py-8 mt-20">
            <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
                <p class="text-gray-400">
                    © 2024 MotoGallery. Ride with passion, choose with confidence.
                </p>
            </div>
        </footer>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/lucide/0.344.0/lucide.min.js"></script>
    <script src="./index.ts"></script>
</body>
</html>
/* file: styles.css */
/* Reset and base styles */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Layout utilities */
.max-w-7xl {
  max-width: 80rem;
}

.mx-auto {
  margin-left: auto;
  margin-right: auto;
}

.px-4 {
  padding-left: 1rem;
  padding-right: 1rem;
}

.py-6 {
  padding-top: 1.5rem;
  padding-bottom: 1.5rem;
}

.py-8 {
  padding-top: 2rem;
  padding-bottom: 2rem;
}

.py-20 {
  padding-top: 5rem;
  padding-bottom: 5rem;
}

.mt-20 {
  margin-top: 5rem;
}

.mb-4 {
  margin-bottom: 1rem;
}

/* Responsive padding */
@media (min-width: 640px) {
  .sm\:px-6 {
    padding-left: 1.5rem;
    padding-right: 1.5rem;
  }
}

@media (min-width: 1024px) {
  .lg\:px-8 {
    padding-left: 2rem;
    padding-right: 2rem;
  }
}

/* Background gradients */
.bg-gradient-to-r {
  background: linear-gradient(to right, var(--tw-gradient-stops));
}

.from-gray-900 {
  --tw-gradient-from: #111827;
  --tw-gradient-to: rgb(17 24 39 / 0);
  --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}

.via-gray-800 {
  --tw-gradient-to: rgb(31 41 55 / 0);
  --tw-gradient-stops: var(--tw-gradient-from), #1f2937, var(--tw-gradient-to);
}

.to-gray-900 {
  --tw-gradient-to: #111827;
}

.from-orange-600 {
  --tw-gradient-from: #ea580c;
  --tw-gradient-to: rgb(234 88 12 / 0);
  --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}

.via-orange-500 {
  --tw-gradient-to: rgb(249 115 22 / 0);
  --tw-gradient-stops: var(--tw-gradient-from), #f97316, var(--tw-gradient-to);
}

.to-red-500 {
  --tw-gradient-to: #ef4444;
}

/* Colors */
.bg-gray-900 {
  background-color: #111827;
}

.text-white {
  color: #ffffff;
}

.text-gray-400 {
  color: #9ca3af;
}

.text-orange-500 {
  color: #f97316;
  stroke: #f97316;
}

/* Flexbox utilities */
.flex {
  display: flex;
}

.items-center {
  align-items: center;
}

.gap-3 {
  gap: 0.75rem;
}

/* Grid utilities */
.grid {
  display: grid;
}

.gap-6 {
  gap: 1.5rem;
}

/* Typography */
.text-xl {
  font-size: 1.25rem;
  line-height: 1.75rem;
}

.text-3xl {
  font-size: 1.875rem;
  line-height: 2.25rem;
}

.text-5xl {
  font-size: 3rem;
  line-height: 1;
}

.font-bold {
  font-weight: 700;
}

.text-center {
  text-align: center;
}

.opacity-90 {
  opacity: 0.9;
}

.max-w-2xl {
  max-width: 42rem;
}

/* Spacing utilities */
.w-8 {
  width: 2rem;
}

.h-8 {
  height: 2rem;
}

.h-2 {
  height: 0.5rem;
}

.w-full {
  width: 100%;
}

/* Border utilities */
.border {
  border-width: 1px;
}

.border-t {
  border-top-width: 1px;
}

.rounded-lg {
  border-radius: 0.5rem;
}

/* Position utilities */
.relative {
  position: relative;
}

.absolute {
  position: absolute;
}

/* Padding utilities */
.px-4 {
  padding-left: 1rem;
  padding-right: 1rem;
}

.px-6 {
  padding-left: 1.5rem;
  padding-right: 1.5rem;
}

.py-1 {
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
}

.py-2 {
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
}

/* Margin utilities */
.mb-4 {
  margin-bottom: 1rem;
}

/* Focus utilities */
.focus\:ring-2:focus {
  --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
  --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
  box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
}

.focus\:ring-orange-500:focus {
  --tw-ring-color: #f97316;
}

.focus\:border-transparent:focus {
  border-color: transparent;
}

/* Hover utilities */
.hover\:-translate-y-1:hover {
  transform: translateY(-0.25rem);
}

/* Animation utilities */
.animate-spin {
  animation: spin 1s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* Custom component styles */
.loading-container {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 400px;
}

.loading-spinner {
  display: flex;
  align-items: center;
  justify-content: center;
}

.motorcycle-grid {
  display: grid;
  grid-template-columns: repeat(1, minmax(0, 1fr));
  gap: 2rem;
}

@media (min-width: 768px) {
  .motorcycle-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 1024px) {
  .motorcycle-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

.motorcycle-card {
  background-color: #ffffff;
  border-radius: 0.75rem;
  overflow: hidden;
  box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
  transition: all 300ms cubic-bezier(0.4, 0, 0.2, 1);
  transform: translateY(0);
}

.motorcycle-card:hover {
  box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);
  transform: translateY(-0.25rem);
}

.motorcycle-card-image-container {
  position: relative;
  height: 16rem;
  overflow: hidden;
  background-color: #111827;
}

.motorcycle-card-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 500ms cubic-bezier(0.4, 0, 0.2, 1);
}

.motorcycle-card:hover .motorcycle-card-image {
  transform: scale(1.1);
}

.motorcycle-card-year-badge {
  position: absolute;
  top: 1rem;
  right: 1rem;
  background-color: #f97316;
  color: #ffffff;
  padding: 0.25rem 0.75rem;
  border-radius: 9999px;
  font-size: 0.875rem;
  font-weight: 700;
}

.motorcycle-card-content {
  padding: 1.5rem;
}

.motorcycle-card-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  margin-bottom: 0.75rem;
}

.motorcycle-card-title {
  font-size: 1.25rem;
  font-weight: 700;
  color: #111827;
  margin-bottom: 0.25rem;
}

.motorcycle-card-manufacturer {
  color: #4b5563;
  font-weight: 500;
}

.motorcycle-card-category {
  padding: 0.25rem 0.75rem;
  background-color: #f3f4f6;
  color: #374151;
  border-radius: 9999px;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.025em;
}

.motorcycle-card-description {
  color: #4b5563;
  font-size: 0.875rem;
  margin-bottom: 1rem;
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}

.motorcycle-card-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 1rem;
  border-top: 1px solid #e5e7eb;
}

.motorcycle-card-price {
  font-size: 1.5rem;
  font-weight: 700;
  color: #ea580c;
}

.motorcycle-card-engine {
  font-size: 0.75rem;
  color: #6b7280;
}

.motorcycle-card-button {
  background-color: #f97316;
  color: #ffffff;
  padding: 0.5rem 1.5rem;
  border-radius: 0.5rem;
  font-weight: 600;
  transition: background-color 200ms cubic-bezier(0.4, 0, 0.2, 1);
  border: none;
  cursor: pointer;
}

.motorcycle-card-button:hover {
  background-color: #ea580c;
}

.no-results {
  text-align: center;
  padding: 3rem 0;
}

.no-results-text {
  font-size: 1.25rem;
  color: #4b5563;
}

.results-count {
  margin-bottom: 1.5rem;
}

.results-count-text {
  color: #4b5563;
}

.results-count-number {
  font-weight: 700;
  color: #111827;
}

/* Additional utility classes for search input */
.bg-gray-700 {
  background-color: #374151;
}

.border-gray-600 {
  border-color: #4b5563;
}

.placeholder-gray-400::placeholder {
  color: #9ca3af;
}

.focus\:outline-none:focus {
  outline: none;
}

.focus\:ring-2:focus {
  box-shadow: 0 0 0 2px rgba(249, 115, 22, 0.5);
}

.focus\:ring-orange-500:focus {
  box-shadow: 0 0 0 2px rgba(249, 115, 22, 0.5);
}

.focus\:border-transparent:focus {
  border-color: transparent;
}

.pl-10 {
  padding-left: 2.5rem;
}

.w-96 {
  width: 24rem;
}

.left-3 {
  left: 0.75rem;
}

.top-1\/2 {
  top: 50%;
}

.transform {
  transform: translateY(-50%);
}

.flex-1 {
  flex: 1 1 0%;
}

.gap-4 {
  gap: 1rem;
}

.gap-6 {
  gap: 1.5rem;
}

.flex-col {
  flex-direction: column;
}

.items-start {
  align-items: flex-start;
}

.items-center {
  align-items: center;
}

@media (min-width: 768px) {
  .md\:flex-row {
    flex-direction: row;
  }
  
  .md\:items-center {
    align-items: center;
  }
  
  .md\:w-auto {
    width: auto;
  }
}
/* file: index.ts */
type Category = 'Sport' | 'Cruiser' | 'Touring' | 'Dirt' | 'Adventure' | 'Naked' | 'Electric';

interface Motorcycle {
  id: string;
  name: string;
  manufacturer: string;
  category: Category;
  price: number;
  image_url: string;
  created_at: Date;
  description: string;
  year: number;
}

const resultsEl = document.getElementById('results-number');
const containerEl = document.getElementById('motorcycle-grid');

async function fetchMotorcycles(): Promise<any> {
  const answer = await fetch('https://cdn.freecodecamp.org/curriculum/labs/data/motorcycles.json');
  const data: string[] = await answer.json();
  ;
  //should return any type
  return data;
}

function renderMotorcycleCard(motorcycle: Motorcycle): string {

  return `
<img class='motorcycle-card-image-container src=${motorcycle.image_url} />
  <p class='motorcycle-card-year-badge'>${motorcycle.year}</p>
  <p class='motorcycle-card-title>${motorcycle.id}</p>
  <p class='motorcycle-card-manufacturer>${motorcycle.manufacturer}</p>
  <p class='motorcycle-card-category'>${motorcycle.category}</p>
  <p class='motorcycle-card-description>${motorcycle.description}</p>
  <p class='motorcycle-card-price>${motorcycle.price}</p>
  <span class='motorcycle-card-engine>${motorcycle.name}</span>
  `

}

class MotorcycleGalleryApp {
  private allMotorcycles: Motorcycle[] = [];//private array

  renderMotorcycles() {
    fetchMotorcycles().then(data => {
      for (let i = 0; i < data.length; i++) {
        containerEl ? containerEl.innerHTML += renderMotorcycleCard(data[i]) : ''
      }
      resultsEl ? resultsEl.textContent = data.length : ''
    })
  }
}

const instance = new MotorcycleGalleryApp();
instance.renderMotorcycles()

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36 OPR/131.0.0.0

Challenge Information:

Build a Motorcycle Shop - Build a Motorcycle Shop

GitHub Link: freeCodeCamp/curriculum/challenges/english/blocks/lab-motorcycle-shop/694175528a794a090ea0ba74.md at main · freeCodeCamp/freeCodeCamp · GitHub

Hey @ricot

I can see broken HTML strings so please look into that.

i fixed it but still fails #20 to #24

type Category = 'Sport' | 'Cruiser' | 'Touring' | 'Dirt' | 'Adventure' | 'Naked' | 'Electric';

interface Motorcycle {
  id: string;
  name: string;
  manufacturer: string;
  category: Category;
  price: number;
  image_url: string;
  created_at: Date;
  description: string;
  year: number;
}

const resultsEl = document.getElementById('results-number');
const containerEl = document.getElementById('motorcycle-grid');

async function fetchMotorcycles(): Promise<any> {
  const answer = await fetch('https://cdn.freecodecamp.org/curriculum/labs/data/motorcycles.json');

  if (!answer.ok) throw new Error('failed to fatch data')
  const data: string[] = await answer.json();
  ;
  //should return any type
  return data;
}

function renderMotorcycleCard(motorcycle: Motorcycle): string {

  return `
  <div class='motorcycle-card'>
<img class='motorcycle-card-image-container' src='${motorcycle.image_url}' />
  <p class='motorcycle-card-year-badge'>${motorcycle.year}</p>
  <p class='motorcycle-card-title'>${motorcycle.name}</p>
  <p class='motorcycle-card-manufacturer'>${motorcycle.manufacturer}</p>
  <p class='motorcycle-card-category'>${motorcycle.category}</p>
  <p class='motorcycle-card-description'>${motorcycle.description}</p>
  <p class='motorcycle-card-price'>$${motorcycle.price}</p>
  <span class="motorcycle-card-engine">no name</span>
  </div>
  `
}

class MotorcycleGalleryApp {
  private allMotorcycles: Motorcycle[] = [];//private array

  renderMotorcycles() {
    fetchMotorcycles().then(data => {
      if (!data || data.length == 0) {
        return
      } else {
        for (let i = 0; i < data.length; i++) {
          containerEl ? containerEl.innerHTML += renderMotorcycleCard(data[i]) : ''
        }
        resultsEl ? resultsEl.textContent = data.length : ''
      }
    })
  }
}

const instance = new MotorcycleGalleryApp();
instance.renderMotorcycles()

i made it guys, see you on the next one