Skip to content

Commit

Permalink
Animations and search
Browse files Browse the repository at this point in the history
  • Loading branch information
PleatherStarfish committed Dec 9, 2024
1 parent ca2d5fe commit 454fc12
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 33 deletions.
40 changes: 25 additions & 15 deletions backend/components/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

from django.core.paginator import Paginator
from django.contrib.postgres.search import TrigramSimilarity
from django.db.models import Q
from django.db.models import Q, FloatField, Value
from django.contrib.auth.decorators import login_required
from rest_framework import status
from rest_framework.decorators import api_view
Expand Down Expand Up @@ -83,20 +83,33 @@ def get(self, request):
filters = {key: value for key, value in filters.items() if value is not None}

# Start with a base queryset
components = Component.objects.select_related("manufacturer", "supplier").all()
components = (
Component.objects.select_related("manufacturer", "type")
.prefetch_related("supplier_items", "supplier_items__supplier")
.all()
)

# Apply TrigramSimilarity search if present
if search_query:
components = (
components.annotate(
similarity=TrigramSimilarity("description", search_query)
+ TrigramSimilarity("manufacturer__name", search_query)
+ TrigramSimilarity("supplier__name", search_query)
+ TrigramSimilarity("type__name", search_query)
)
.filter(similarity__gt=0.1)
.order_by("-similarity")
)
trigram_components = components.annotate(
similarity=TrigramSimilarity("description", search_query)
+ TrigramSimilarity("manufacturer__name", search_query)
+ TrigramSimilarity("manufacturer_part_no", search_query)
+ TrigramSimilarity("supplier_items__supplier__name", search_query)
+ TrigramSimilarity("supplier_items__supplier_item_no", search_query)
+ TrigramSimilarity("type__name", search_query)
).filter(similarity__gt=0.4)

ilike_components = components.filter(
Q(description__icontains=search_query)
| Q(manufacturer__name__icontains=search_query)
| Q(manufacturer_part_no__icontains=search_query)
| Q(supplier_items__supplier__name__icontains=search_query)
| Q(supplier_items__supplier_item_no__icontains=search_query)
| Q(type__name__icontains=search_query)
).annotate(similarity=Value(0.0, output_field=FloatField()))

components = (trigram_components | ilike_components).distinct()

# Dynamically apply filters
try:
Expand Down Expand Up @@ -134,9 +147,6 @@ def get(self, request):
if "type" in filters:
components = components.filter(type__name__icontains=filters["type"])

# Sort by description after applying all filters
components = components.order_by("description")

# Create a paginator instance
paginator = Paginator(components, 30)

Expand Down
2 changes: 1 addition & 1 deletion backend/static/css/styles.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion backend/static/js/main.bundle.js

Large diffs are not rendered by default.

82 changes: 82 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"@headlessui/react": "^1.7.13",
"@heroicons/react": "^2.0.16",
"@react-hook/window-size": "^3.1.1",
"@react-spring/web": "^9.7.5",
"@sentry/react": "^7.118.0",
"@sentry/wizard": "^3.25.2",
"@tailwindcss/aspect-ratio": "^0.4.2",
Expand Down Expand Up @@ -41,6 +42,7 @@
"react-helmet-async": "^2.0.5",
"react-hook-form": "^7.43.9",
"react-infinite-scroll-component": "^6.1.0",
"react-intersection-observer": "^9.13.1",
"react-numeric-input": "^2.2.3",
"react-router-dom": "^6.10.0",
"react-scripts": "^5.0.1",
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/ModuleDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ const ModuleDetail: React.FC = () => {
<div>
<div className="pb-5">
<HeaderWithButton
buttonText="Quick BOM Export"
buttonText={`Get Cart for ${module.name}`}
onButtonClick={() => handleModalOpenClose(true)}
title="Components"
/>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/styles/styles.css

Large diffs are not rendered by default.

70 changes: 56 additions & 14 deletions frontend/src/ui/HeaderWithButton.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import React, { ReactNode } from "react";
import React, {useState, useEffect, useRef} from "react";
import { useInView } from "react-intersection-observer";
import { useSpring, animated } from "@react-spring/web";
import { ArrowDownOnSquareIcon } from "@heroicons/react/24/outline";


interface HeaderWithButtonProps {
title: string;
onButtonClick: () => void;
buttonText: string;
icon?: ReactNode; // Allow passing a custom icon
icon?: React.ReactNode;
}

const HeaderWithButton: React.FC<HeaderWithButtonProps> = ({
Expand All @@ -14,26 +17,65 @@ const HeaderWithButton: React.FC<HeaderWithButtonProps> = ({
buttonText,
icon = <ArrowDownOnSquareIcon className="w-5 h-5" />, // Default to ArrowDownOnSquareIcon
}) => {
const [isNear, setIsNear] = useState(false);
const buttonRef = useRef<HTMLDivElement>(null);
const { ref, inView } = useInView({ triggerOnce: true });

// Threshold for applying the pulse animation (in pixels)
const THRESHOLD = 300;

const handleMouseMove = (event: MouseEvent) => {
if (buttonRef.current) {
const rect = buttonRef.current.getBoundingClientRect();
const buttonCenterX = rect.left + rect.width / 2;
const buttonCenterY = rect.top + rect.height / 2;

// Calculate the distance from the cursor to the button's center
const distance = Math.sqrt(
Math.pow(event.clientX - buttonCenterX, 2) +
Math.pow(event.clientY - buttonCenterY, 2)
);

setIsNear(distance < THRESHOLD);
}
};

useEffect(() => {
window.addEventListener("mousemove", handleMouseMove);

return () => {
window.removeEventListener("mousemove", handleMouseMove);
};
}, []);

const styles = useSpring({
config: { bounce: 0.4, friction: 12, tension: 180 },
opacity: inView ? 1 : 0,
transform: inView ? "translateX(0px)" : "translateX(300px)",
});

return (
<div className="mb-6 relative">
{/* Divider Line */}
<div className="relative mb-6">
<div aria-hidden="true" className="absolute inset-0 flex items-center">
<div className="w-full border-t border-gray-300" />
</div>

{/* Title and Button */}
<div className="relative flex items-center justify-between">
<h1 className="bg-white pr-3 text-3xl font-semibold text-gray-900 font-display">
<h1 className="pr-3 text-3xl font-semibold text-gray-900 bg-white font-display">
{title}
</h1>
<button
className="inline-flex items-center gap-x-1.5 rounded-full bg-white px-3 py-1.5 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:text-white hover:bg-[#4f7f63] transition-all"
onClick={onButtonClick}
type="button"
>
{icon}
<span>{buttonText}</span>
</button>
<div className={isNear ? "animate-bounce" : ""} ref={buttonRef}>
<animated.button
className="inline-flex items-center gap-x-1.5 rounded-full bg-[#588a6d] px-3 py-1.5 text-md font-semibold shadow-md text-white hover:text-white hover:bg-[#588a6d] transition-all duration-500"
onClick={onButtonClick}
ref={ref}
style={styles}
type="button"
>
{icon}
<span>{buttonText}</span>
</animated.button>
</div>
</div>
</div>
);
Expand Down

0 comments on commit 454fc12

Please sign in to comment.