Build a Motorcycle Shop - Lab Motorcycle Shop

Tell us what’s happening:

Lab Motorcycle Shop

Test No. 13, 17, 18, 19, 20, 21, 22, 23, and 24 are not passing even though it’s rendering and I already have a private array in the class.

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 */
const motorcycleGrid = document.getElementById("motorcycle-grid") as HTMLElement;const resultsNumber = document.getElementById("results-number") as HTMLElement;

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,  engine: undefined}
async function fetchMotorcycles() {  try {    let response = await fetch("https://cdn.freecodecamp.org/curriculum/labs/data/motorcycles.json");    let userData = await response.json();    return userData;  } catch (error){    console.error(error)  }}


const renderMotorcycleCard = (motorcycle: Motorcycle): string => {    return `<div class="motorcycle-card">   <img class="motorcycle-card-image-container" src="${motorcycle.image_url}" />   <div class="motorcycle-card-year-badge">${motorcycle.year}</div>   <div class="motorcycle-card-title">${motorcycle.name}</div>   <div class="motorcycle-card-manufacturer">${motorcycle.manufacturer}</div>   <div class="motorcycle-card-category">${motorcycle.category}</div>   <div class="motorcycle-card-description">${motorcycle.description}</div>   <div class="motorcycle-card-price">${motorcycle.price}</div>   <div class="motorcycle-card-engine">${motorcycle.engine}</div>   </div>`}


class MotorcycleGalleryApp {  private allMotorcycles:  Motorcycle[];     constructor(allMotorcycles: Motorcycle[]) {    this.allMotorcycles = allMotorcycles;  }
  renderMotorcycles() {    let count = 0;    this.allMotorcycles.forEach((motors) => {     motorcycleGrid.innerHTML += renderMotorcycleCard(motors)      count += 1;    })    resultsNumber.innerText = String(count);  } }
async function texstEx() { const test = new MotorcycleGalleryApp(await fetchMotorcycles()) test.renderMotorcycles() console.log(motorcycleGrid)}
texstEx()




Your browser information:

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

Challenge Information:

Build a Motorcycle Shop - Lab Motorcycle Shop

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

Hi @dylanpueo,

Please pay attention to all of the red squiggly lines in the index.ts editor. You are missing several semi-colons.

Happy coding!

Hello,

I do not see the red squiggly lines but I checked the lack of semicolons in my code and changed it but it still not passing test No. 13, 17, 18, 19, 20, 21, 22, 23, and 24.

const motorcycleGrid = document.getElementById(“motorcycle-grid”) as HTMLElement;

const resultsNumber = document.getElementById(“results-number”) as HTMLElement;

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;

engine: undefined;

};

async function fetchMotorcycles() {

try {

let response = await fetch("https://cdn.freecodecamp.org/curriculum/labs/data/motorcycles.json");

let userData = await response.json();

return userData;

} catch (error){

console.error(error);

};

};

const renderMotorcycleCard = (motorcycle: Motorcycle): string => {

return `

${motorcycle.year}
${motorcycle.name}
${motorcycle.manufacturer}
${motorcycle.category}
${motorcycle.description}
${motorcycle.price}
${motorcycle.engine}
\`;

};

class MotorcycleGalleryApp {

private allMotorcycles: Motorcycle[];

constructor(allMotorcycles: Motorcycle[]) {

this.allMotorcycles = allMotorcycles;

};

renderMotorcycles() {

let count = 0;

this.allMotorcycles.forEach((motors) => {

 motorcycleGrid.innerHTML += renderMotorcycleCard(motors)

  count += 1;

});

resultsNumber.innerText = String(count);

};

};

async function texstEx() {

const test = new MotorcycleGalleryApp(await fetchMotorcycles());

test.renderMotorcycles();

console.log(motorcycleGrid);

};

texstEx();

When you enter a code block into a forum post, please precede it with three backticks to make it easier to read.

You can also use the “preformatted text” tool in the editor (</>) to add the backticks.

See this post to find the backtick on your keyboard.
Note: Backticks (`) are not single quotes (').

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

satisfy this, then all the test cases will pass. worked for me. because the assertion test cases are written to check this way. this means, the fetch, filtering all should happen inside the renderMotorcycles function.

for every test case from 17 to 24 new class instance is created. and render function is invoked. so, everytime a new instance is created, the data should be fetched and ready. then render happens. after that assertion test will check if there required number of elements with that specific class are there in the document or not.

Wow, this is not obvious at all from the requirements for the lab… I was expecting that there should be a non-default constructor for the allMotorcycles array but I tried your way and the tests passed… Thank you for your suggestion