From bd2936fa0a46968d5744562dc315039fce825387 Mon Sep 17 00:00:00 2001 From: Endri Islami Date: Sun, 17 Nov 2024 23:08:24 -0500 Subject: [PATCH] Linked the API endpoints from Django to the React Frontend --- Backend/category/artcategories/models.py | 20 +++--- Backend/category/artcategories/serializers.py | 12 ++++ Backend/category/artcategories/tests.py | 3 - Backend/category/artcategories/urls.py | 24 +++++-- Backend/category/artcategories/views.py | 4 +- Backend/category/category/settings.py | 3 + Backend/category/category/urls.py | 16 +++++ src/app/backend/page.tsx | 24 +++++++ src/app/homepage/page.tsx | 67 ++++++++++++++----- 9 files changed, 135 insertions(+), 38 deletions(-) create mode 100644 Backend/category/artcategories/serializers.py delete mode 100644 Backend/category/artcategories/tests.py create mode 100644 src/app/backend/page.tsx diff --git a/Backend/category/artcategories/models.py b/Backend/category/artcategories/models.py index 1fab58ed..739beb9f 100644 --- a/Backend/category/artcategories/models.py +++ b/Backend/category/artcategories/models.py @@ -1,25 +1,25 @@ -from django.db import models # Importing Django's models module to define database models +from django.db import models # Category Model: Represents a category for Types class Category(models.Model): - name = models.CharField(max_length=100) # 'name' field to store the category name with a max length of 100 characters + name = models.CharField(max_length=100, unique=True) # Ensuring category names are unique to prevent duplicates - # This method returns the name of the category when the model instance is printed def __str__(self): return self.name - # Meta class is used to specify metadata about the model - # Here, we specify the plural name of the model as 'categories' class Meta: verbose_name_plural = 'categories' # Type Model: Represents a specific type, associated with a category class Type(models.Model): - image = models.ImageField(upload_to='uploads/types/') # 'image' field to store an image file, uploaded to the 'uploads/types/' directory - category = models.ForeignKey(Category, on_delete=models.CASCADE, default=1) # ForeignKey field linking each Type to a Category; default is set to 1 - name = models.CharField(max_length=200) # 'name' field to store the type name with a max length of 200 characters - description = models.CharField(max_length=500, default='', blank=True, null=True) # Optional description field with max length of 500 characters + image = models.ImageField(upload_to='uploads/types/') # Image field for storing uploaded images + category = models.ForeignKey( + Category, + on_delete=models.CASCADE, + related_name='types' # Optional: allows reverse access from Category to Type + ) + name = models.CharField(max_length=200) + description = models.CharField(max_length=500, default='', blank=True, null=True) - # This method returns the name of the type when the model instance is printed def __str__(self): return self.name diff --git a/Backend/category/artcategories/serializers.py b/Backend/category/artcategories/serializers.py new file mode 100644 index 00000000..07195133 --- /dev/null +++ b/Backend/category/artcategories/serializers.py @@ -0,0 +1,12 @@ +from rest_framework import serializers +from .models import Type, Category + +class CategorySerializer(serializers.ModelSerializer): + class Meta: + model = Category + fields = '__all__' + +class TypeSerializer(serializers.ModelSerializer): + class Meta: + model = Type + fields = '__all__' diff --git a/Backend/category/artcategories/tests.py b/Backend/category/artcategories/tests.py deleted file mode 100644 index 7ce503c2..00000000 --- a/Backend/category/artcategories/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/Backend/category/artcategories/urls.py b/Backend/category/artcategories/urls.py index 0a8f096b..abb2f7b5 100644 --- a/Backend/category/artcategories/urls.py +++ b/Backend/category/artcategories/urls.py @@ -1,9 +1,21 @@ -from django.urls import path # Importing 'path' function for defining URL patterns -from . import views # Importing the views module from the current directory +"""artcategory URL Configuration + + The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/4.1/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.urls import path +from .views import home # Replace 'home' with the actual view functions you have in views.py -# Defining URL patterns for the app urlpatterns = [ - # Mapping the root URL ('') to the 'home' view function - # When a user visits the root of the site (e.g., '/'), the 'home' function will be executed - path('', views.home, name='home'), + path('', home, name='home'), # Ensure the view name matches what's in views.py ] diff --git a/Backend/category/artcategories/views.py b/Backend/category/artcategories/views.py index 06daf69b..d02da207 100644 --- a/Backend/category/artcategories/views.py +++ b/Backend/category/artcategories/views.py @@ -3,11 +3,11 @@ from django.db.models import Q def home(request): - query = request.GET.get('q') # get the search query + query = request.GET.get('q') if query: types = Type.objects.filter( Q(name__icontains=query) | - Q(category__name__icontains=query) # search by type name or category name + Q(category__name__icontains=query) ) else: types = Type.objects.all() diff --git a/Backend/category/category/settings.py b/Backend/category/category/settings.py index fe4d7132..9f793b58 100644 --- a/Backend/category/category/settings.py +++ b/Backend/category/category/settings.py @@ -32,6 +32,7 @@ # Application definition INSTALLED_APPS = [ + 'corsheaders', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', @@ -49,9 +50,11 @@ 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', + 'corsheaders.middleware.CorsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] +CORS_ALLOW_ORIGINS = True ROOT_URLCONF = 'category.urls' TEMPLATES = [ diff --git a/Backend/category/category/urls.py b/Backend/category/category/urls.py index 486105f9..5d5b44b6 100644 --- a/Backend/category/category/urls.py +++ b/Backend/category/category/urls.py @@ -1,3 +1,19 @@ +"""Category URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/4.1/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" + from django.contrib import admin from django.urls import path, include from . import settings diff --git a/src/app/backend/page.tsx b/src/app/backend/page.tsx new file mode 100644 index 00000000..754c90b4 --- /dev/null +++ b/src/app/backend/page.tsx @@ -0,0 +1,24 @@ +"use client"; +import { useState, useEffect } from 'react'; + + +const MyPage = () => { + const [message, setMessage] = useState(''); + + + useEffect(() => { + // Make API call to Django backend + fetch('http://127.0.0.1:8000/') + .then(response => response.json()) + .then(data => setMessage(data.message)) + .catch(error => console.error('Error fetching data:', error)); + }, []); + + + return ( +
+

{message}

+
+ ); +}; +export default MyPage; \ No newline at end of file diff --git a/src/app/homepage/page.tsx b/src/app/homepage/page.tsx index 7bd1bce7..21b6de7c 100644 --- a/src/app/homepage/page.tsx +++ b/src/app/homepage/page.tsx @@ -13,10 +13,11 @@ import barberLogo from '../../public/barber_logo.png'; // Replace with actual lo import newLogo from '../../public/new_logo.png'; // Replace with actual logo import Navbar from '../components/navigationbar'; import Image from 'next/image'; -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; +import axios from 'axios'; import './filter.css'; -const SearchBar: React.FC = () => { +const SearchBar: React.FC<{ onSearch: (term: string) => void }> = ({ onSearch }) => { const [searchTerm, setSearchTerm] = useState(''); const handleSearchChange = (event: React.ChangeEvent) => { @@ -25,7 +26,7 @@ const SearchBar: React.FC = () => { const handleSearchSubmit = (event: React.FormEvent) => { event.preventDefault(); - console.log('Searching for:', searchTerm); + onSearch(searchTerm); }; return ( @@ -46,6 +47,29 @@ const SearchBar: React.FC = () => { export default function Home() { const router = useRouter(); + const [categories, setCategories] = useState([]); + const [searchResults, setSearchResults] = useState([]); + + useEffect(() => { + const fetchCategories = async () => { + try { + const response = await axios.get('http://127.0.0.1:8000/api/category/categories/'); + setCategories(response.data); + } catch (error) { + console.error('Error fetching categories:', error); + } + }; + fetchCategories(); + }, []); + + const handleSearch = async (term: string) => { + try { + const response = await axios.get(`http://127.0.0.1:8000/api/search/search/?q=${term}`); + setSearchResults(response.data); + } catch (error) { + console.error('Error fetching search results:', error); + } + }; const handleRedirect = () => { router.push('schedule'); @@ -55,12 +79,12 @@ export default function Home() {
{/* Logo */}
- Logo {/* Smaller dimensions */} + Logo
- {/* Main Content Area */} + {/* Header/ Main Content Area */}
{/* Header */}
@@ -72,7 +96,27 @@ export default function Home() {
{/* Search Bar */} - + + + {/* Categories Section */} +

Categories

+
    + {categories.map((category, index) => ( +
  • {category.name}
  • + ))} +
+ + {/* Search Results Section */} + {searchResults.length > 0 && ( +
+

Search Results

+
    + {searchResults.map((result, index) => ( +
  • {result.name}
  • + ))} +
+
+ )} {/* Redirect Button */}
@@ -96,52 +140,41 @@ export default function Home() {