Awesome Cursor Rules Collection

Showing 1237-1248 of 2626 matches

TypeScript
# NFT Event Oracle - CursorRules

## Project Structure
- src/
  - config/
    - chainConfig.ts
  - services/
    - apiService.ts
    - blockchainService.ts
    - eventProcessorService.ts
    - metadataService.ts
  - models/
    - Contract.ts
    - Event.ts
  - utils/
    - logger.ts
  - index.ts
- docker/
  - Dockerfile
  - docker-compose.yml
- .env.example
- package.json
- tsconfig.json

## Technology Stack
- Language: TypeScript
- Runtime: Node.js
- Framework: Nest.js (for scalability and modularity)
- Blockchain Interaction: ethers.js (for EVM chains)
- Database: PostgreSQL (for storing processed events and metadata)
- Message Queue: RabbitMQ (for handling high throughput of events)
- Containerization: Docker and Docker Compose

## Core Components

## Implementation Guidelines

11. Use Docker for containerization and easy deployment
12. Create a docker-compose.yml file for orchestrating all services

## Docker Setup

### Dockerfile (docker/Dockerfile)
- Use a multi-stage build for smaller image size
- Install dependencies and build the application
- Use a lightweight Node.js image for the final stage

### Docker Compose (docker/docker-compose.yml)
- Define services for the main application, PostgreSQL, and RabbitMQ
- Use environment variables for configuration
- Set up volume mounts for persistent data
- Configure health checks for each service

## Environment Variables
- Create a .env.example file with all required environment variables
- Include variables for:
  - Database connection
  - RabbitMQ connection
  - API endpoints
  - Blockchain RPC URLs
  - Log levels

## Single VM Deployment
- Ensure the VM has Docker and Docker Compose installed
- Clone the repository to the VM
- Copy .env.example to .env and fill in the required values
- Run `docker-compose up -d` to start all services
- Implement a simple monitoring solution (e.g., Prometheus + Grafana) for VM resource usage

## Performance Tuning for Single VM
- Adjust container resource limits in docker-compose.yml 
- Optimize PostgreSQL configuration for the VM's resources
- Configure RabbitMQ for optimal performance on a single node
- Implement caching mechanisms to reduce database load

## Backup and Recovery
- Set up regular backups of the PostgreSQL database
- Create a backup script and schedule it with cron
- Document the restore process for quick recovery

## Logging and Monitoring
- Use Docker's logging driver to centralize logs
- Implement a log rotation strategy to manage disk space
- Set up Prometheus and Grafana for monitoring (optional)

## Development Workflow
- Use Docker Compose for local development
- Create separate docker-compose.override.yml for development-specific settings
- Implement hot-reloading for faster development cycles

## Deployment Workflow
1. Build and test the application locally
2. Push changes to the repository
3. SSH into the VM
4. Pull the latest changes
5. Rebuild and restart the containers with `docker-compose up -d --build`

## Scaling Considerations
- While designed for a single VM, prepare for future scaling:
  - Use environment variables for easy configuration changes
  - Design services to be stateless where possible
  - Implement health checks for potential load balancing in the future
docker
dockerfile
less
nestjs
postgresql
rest-api
shell
solidity
+1 more

First seen in:

megayours/project-gamma

Used in 1 repository

Python
# Python Multi-Agent System Development Rules

## Code Style & Organization

- Follow PEP 8 style guidelines
- Use type hints for all functions and variables
- Organize code into logical modules and packages
- Use descriptive variable names (e.g., agent_id, message_queue)
- Keep functions focused and under 50 lines
- Document all public APIs with docstrings
- Use loguru for logging
- Place imports in order: stdlib, third-party, local
- Use absolute imports with module aliases
- Follow SOLID principles

## Agent Implementation

- Inherit from AgentBase ABC
- Implement required abstract methods
- Use immutable state management
- Handle messages asynchronously
- Validate all inputs
- Log state changes
- Handle errors gracefully
- Use dependency injection
- Implement proper cleanup

## Message System

- Define clear message protocols
- Validate message content
- Use queues for async communication
- Include sender/receiver IDs
- Add timestamps
- Handle timeouts
- Log message flow

## Environment

- Define clear boundaries
- Track agent states
- Manage interactions
- Provide observations
- Measure metrics
- Handle concurrent access
- Maintain consistency

## Error Handling

- Use custom exception classes
- Validate all inputs
- Log errors with context
- Implement recovery mechanisms
- Handle edge cases
- Fail fast and explicitly

## Testing

- Write unit tests for all components
- Test agent interactions
- Mock external dependencies
- Test error conditions
- Use pytest fixtures
- Maintain high coverage

## Documentation

- Write clear docstrings
- Document configurations
- Include examples
- Maintain changelog
- Document dependencies

## Performance

- Profile code
- Optimize hot paths
- Cache where beneficial
- Minimize object creation
- Use appropriate data structures
- Monitor memory usage

## Logging

- Use structured logging
- Set appropriate levels
- Log state changes
- Track metrics
- Monitor resources
python
solidjs
tommaay/ai-agent-no-framework

Used in 1 repository

TypeScript
{
  "project_directives": {
    "name": "cursor-websocket",
    "ai_first": true,
    "tools": [
      {
        "name": "websocket-bridge",
        "description": "Interface with Roo Cline for tasks requiring a broader codebase understanding.",
        "usage": "Use this tool when tasks involve global refactoring, project-wide impact analysis, long-range planning, or any operation that benefits from a 64k context window. Specify a custom port if connecting to a non-default Roo Cline instance.",
        "commands": {
          "global_refactor": {
            "description": "Initiate a global refactoring operation managed by Roo Cline.",
            "parameters": {
              "files": {
                "type": "array",
                "description": "List of file paths relevant to the refactoring."
              },
              "instructions": {
                "type": "string",
                "description": "Detailed instructions for the refactoring operation."
              }
            },
            "example": "websocket-bridge global_refactor --files=['src/main.js', 'src/utils.js'] --instructions='Rename function X to Y and update all references.'"
          },
          "impact_analysis": {
            "description": "Request an impact analysis from Roo Cline for a proposed change.",
            "parameters": {
              "change_description": {
                "type": "string",
                "description": "Description of the proposed change."
              },
              "files": {
                "type": "array",
                "description": "List of file paths potentially affected by the change."
              }
            },
            "example": "websocket-bridge impact_analysis --change_description='Adding new parameter to function Z' --files=['src/api.js', 'src/components/User.js']"
          },
          "long_range_plan": {
            "description": "Collaborate with Roo Cline to develop a long-range implementation plan for a complex task.",
            "parameters": {
              "task_description": {
                "type": "string",
                "description": "Detailed description of the task or feature to be implemented."
              }
            },
            "example": "websocket-bridge long_range_plan --task_description='Implement user authentication using OAuth 2.0'"
          },
          "context_summary": {
            "description": "Obtain a context summary from Roo Cline for a specific part of the codebase.",
            "parameters": {
              "files": {
                "type": "array",
                "description": "List of file paths for which a context summary is needed."
              }
            },
            "example": "websocket-bridge context_summary --files=['src/models/User.js', 'src/controllers/AuthController.js']"
          },
          "code_review": {
            "description": "Submit a set of modified files to Roo Cline for a comprehensive code review.",
            "parameters": {
              "files": {
                "type": "array",
                "description": "List of file paths that have been modified and require review."
              }
            },
            "example": "websocket-bridge code_review --files=['src/feature/new-module.js', 'src/utils/helper.js']"
          },
          "dependency_resolution": {
            "description": "Delegate dependency-related issues to Roo Cline for resolution.",
            "parameters": {
              "issue_description": {
                "type": "string",
                "description": "Description of the dependency issue encountered."
              },
              "files": {
                "type": "array",
                "description": "List of file paths relevant to the dependency issue."
              }
            },
            "example": "websocket-bridge dependency_resolution --issue_description='Conflicting versions of library X' --files=['package.json', 'src/main.js']"
          },
          "bug_detection": {
            "description": "Request Roo Cline to analyze a potential bug and provide insights.",
            "parameters": {
              "bug_description": {
                "type": "string",
                "description": "Description of the potential bug."
              },
              "files": {
                "type": "array",
                "description": "List of file paths where the bug might be located."
              }
            },
            "example": "websocket-bridge bug_detection --bug_description='Memory leak observed in user profile module' --files=['src/modules/UserProfile.js', 'src/utils/memoryManager.js']"
          },
          "security_audit": {
            "description": "Task Roo Cline with performing a security audit on the codebase.",
            "parameters": {},
            "example": "websocket-bridge security_audit"
          },
          "performance_optimization": {
            "description": "Request Roo Cline to identify performance bottlenecks and suggest optimizations.",
            "parameters": {},
            "example": "websocket-bridge performance_optimization"
          },
          "get_logs": {
            "description": "Retrieve logs from the cursor-socket-server.",
            "parameters": {
              "log_type": {
                "type": "string",
                "enum": [
                  "all",
                  "connection",
                  "message",
                  "error"
                ],
                "default": "all",
                "description": "Type of logs to retrieve."
              },
              "lines": {
                "type": "integer",
                "default": 10,
                "description": "Number of log lines to retrieve (use with 'tail')."
              },
              "follow": {
                "type": "boolean",
                "default": false,
                "description": "Whether to follow the logs in real-time."
              }
            },
            "example": "websocket-bridge get_logs all 20"
          },
          "manage_subprocess": {
            "description": "Manage sub-processes within the cursor-socket-server.",
            "parameters": {
              "action": {
                "type": "string",
                "enum": [
                  "start",
                  "stop",
                  "status"
                ],
                "description": "Action to perform on the sub-process."
              },
              "process_name": {
                "type": "string",
                "description": "Name of the sub-process to manage."
              }
            },
            "example": "websocket-bridge manage_subprocess start data_processor"
          }
        },
        "parameters": {
          "port": {
            "type": "integer",
            "description": "Optional port number for the WebSocket connection. Use this when connecting to a Roo Cline instance running on a non-default port."
          }
        }
      }
    ]
  },
  "context_initialization": {
    "description": "Starting point for each interaction",
    "steps": [
      "Read '.brain/project-overview.md'",
      "Read '.brain/directory-structure.md'",
      "Read '.brain/tasks.md'",
      "Establish connection with Roo Cline using 'cursor-socket' if required for the current task, using the specified port if provided."
    ]
  },
  "rules": {
    "when_to_use_roo": {
      "description": "Guidelines for determining when to utilize the websocket-bridge tool.",
      "conditions": [
        "If the task requires understanding more than the current file and a few related files.",
        "If the task involves analyzing or modifying code across multiple modules or the entire project.",
        "If the task benefits from a larger context window (64k) for accurate analysis or generation.",
        "If the task is computationally intensive and can be offloaded to Roo Cline."
      ]
    },
    "communication_protocol": {
      "description": "Defines the communication protocol between Cursor and Roo Cline.",
      "steps": [
        "Cursor formulates a clear, concise command using the defined 'websocket-bridge' tool and its commands.",
        "Cursor includes the 'port' parameter if a non-default port is required.",
        "Cursor sends the command to Roo Cline via the established 'cursor-socket' connection.",
        "Roo Cline processes the command and sends back a structured response (e.g., JSON).",
        "Cursor parses the response and integrates the results into its workflow."
      ]
    },
    "error_handling": {
      "description": "Specifies how to handle errors during communication with Roo Cline.",
      "steps": [
        "If 'cursor-socket' connection fails, attempt to reconnect a predetermined number of times, trying different ports if necessary.",
        "If Roo Cline returns an error, log the error and attempt to recover or notify the user.",
        "If a task delegated to Roo Cline fails, consider alternative approaches or break down the task further."
      ]
    },
    "custom_port_usage": {
      "description": "Guidelines for using custom ports with the websocket-bridge tool.",
      "conditions": [
        "If the user specifies a non-default port for Roo Cline.",
        "If connecting to a specific Roo Cline instance running on a different port.",
        "If the default port (8080) is unavailable or in use."
      ],
      "steps": [
        "Identify the desired port number.",
        "Include the 'port' parameter in the websocket-bridge command.",
        "Ensure that the cursor-socket-server is started with the corresponding port argument."
      ]
    }
  }
}
oauth
typescript
websockets
drumnation/cursor-socket-server

Used in 1 repository

Svelte
Wails.io Desktop Application Development with SvelteKit and Go

You are an expert AI programming assistant specializing in building wails.io desktop applications using the below technologies:
- SvelteKit with Svelte 5
- TypeScript
- Tailwindcss
- shadcn-svelte
- golang for the backend

Always use the latest stable versions of Wails, Go, SvelteKit, and Svelte.

General Principles
- Follow the user's requirements carefully & to the letter.
- First think step-by-step - describe your plan for the application structure, components, and data flow in pseudocode, written out in great detail.
- Confirm the plan, then write code!
- Write correct, up-to-date, bug-free, fully functional, secure, and efficient code.
- Leave NO todos, placeholders, or missing pieces in the implementation.
- Be concise in explanations, but provide brief comments for complex logic or language-specific idioms.
- If unsure about a best practice or implementation detail, say so instead of guessing.

Frontend Development (SvelteKit with Svelte 5)

Code Style and Structure
- Write concise, technical TypeScript code with accurate Svelte 5 and SvelteKit examples.
- Use functional and declarative programming patterns; avoid unnecessary classes except for state machines.
- Prefer iteration and modularization over code duplication.
- Structure files: component logic, markup, styles, helpers, types.
- Follow Svelte's official documentation for setup and configuration.

Naming Conventions
- Use lowercase with hyphens for component files (e.g., `components/auth-form.svelte`).
- Use PascalCase for component names in imports and usage.
- Use camelCase for variables, functions, and props.

TypeScript Usage
- Use TypeScript for all frontend code; prefer interfaces over types.
- Avoid enums; use const objects instead.
- Use functional components with TypeScript interfaces for props.
- Enable strict mode in TypeScript for better type safety.

Svelte Runes
- Use `$state`, `$derived`, `$effect`, `$props`, `$bindable`, and `$inspect` as demonstrated in the Svelte 5 documentation.

UI and Styling
- Use Tailwind CSS for utility-first styling approach.
- Leverage Shadcn components for pre-built, customizable UI elements.
- Import Shadcn components from `$lib/components/ui`.
- Organize Tailwind classes using the `cn()` utility from `$lib/utils`.
- Use Svelte's built-in transition and animation features.

Wails Project Structure
- This folder structure is for a Wails.io project using SvelteKit for the frontend:
  APP01
    └── 📁frontend
        └── 📁build
            └── favicon.png
            └── index.html
        └── 📁src
            └── 📁lib
                └── 📁components
                    └── 📁ui
                        └── 📁button
                            └── button.svelte
                            └── index.ts
                        └── 📁card
                            └── card-content.svelte
                            └── card-description.svelte
                            └── card-footer.svelte
                            └── card-header.svelte
                            └── card-title.svelte
                            └── card.svelte
                            └── index.ts
                        └── 📁input
                            └── index.ts
                            └── input.svelte
                └── 📁wailsjs
                    └── 📁go
                        └── 📁main
                            └── App.d.ts
                            └── App.js
                    └── 📁runtime
                        └── package.json
                        └── runtime.d.ts
                        └── runtime.js
                └── index.ts
                └── utils.ts
            └── 📁routes
                └── +layout.svelte
                └── +layout.ts
                └── +page.svelte
            └── app.css
            └── app.d.ts
            └── app.html
        └── 📁static
            └── favicon.png
        └── .gitignore
        └── .npmrc
        └── .prettierignore
        └── .prettierrc
        └── bun.lockb
        └── components.json
        └── eslint.config.js
        └── package.json
        └── package.json.md5
        └── postcss.config.js
        └── README.md
        └── svelte.config.js
        └── tailwind.config.ts
        └── tsconfig.json
        └── vite.config.ts
    └── .cursorrules
    └── .gitignore
    └── app.go
    └── go.mod
    └── go.sum
    └── main.go
    └── README.md
    └── wails.json

Component Development
- Create .svelte files for Svelte components.
- Use .svelte.ts files for component logic and state machines.
- Implement proper component composition and reusability.
- Use Svelte's props for data passing.
- Leverage Svelte's reactive declarations for local state management.

State Management
- Use classes for complex state management (state machines) as demonstrated in the Svelte 5 rules.

Routing and Pages
- Adapt SvelteKit's file-based routing system for desktop application navigation.
- Implement proper error handling with error boundary components.

Performance Optimization
- Leverage Svelte's compile-time optimizations.
- Use `{key}` blocks to force re-rendering of components when needed.
- Implement code splitting using dynamic imports for large applications.
- Profile and monitor performance using browser developer tools.
- Use `$effect.tracking()` to optimize effect dependencies.

Backend Development (Go)

Code Style and Structure
- Write correct, up-to-date, bug-free, fully functional, secure, and efficient Go code.
- Follow Go idioms and best practices.
- Implement proper error handling, including custom error types when beneficial.
- Use appropriate naming conventions (e.g., PascalCase for exported identifiers).

Wails.io Integration
- Use Wails bindings to expose Go functions to the frontend.
- Implement proper error handling for communication between Go and JavaScript.
- Use appropriate data structures for passing information between frontend and backend.

Concurrency
- Utilize Go's built-in concurrency features when beneficial for application performance.
- Implement proper synchronization and avoid race conditions.

Backend Structure
- Organize Go code into packages based on functionality.
- Implement a clear separation of concerns between different parts of the backend.

Wails.io Specific
- Use Wails CLI for project setup and management.
- Implement proper build processes for both development and production.
- Handle application lifecycle events (e.g., startup, shutdown) appropriately.
- Implement proper error handling and logging for the Wails application.

Testing
- Write unit tests for both frontend (using Svelte's testing utilities) and backend (using Go's testing package) components.
- Implement integration tests for Wails.io bindings and communication between frontend and backend.

Documentation
- Provide clear documentation for setting up and running the Wails.io project.
- Document any custom APIs or bindings created for communication between frontend and backend.

Always prioritize security, performance, and user experience in your Wails.io desktop application designs and implementations. Leverage the strengths of both Svelte for the frontend and Go for the backend to create efficient and maintainable applications.
bun
css
eslint
go
golang
html
java
javascript
+8 more

First seen in:

jtfogarty/ts-search

Used in 1 repository

TypeScript
You are an expert developer in TypeScript, Node.js, Next.js 15 App Router, React, Supabase, GraphQL, Genql, DrizzleORM, HuggingFace Models, Tailwind CSS, Radix UI, and Shadcn UI.

---

## Coding Philosophy
Our coding philosophy revolves around writing **semantic, idiomatic, functional, and declarative code**. Follow these key principles:

- **Modern JavaScript & TypeScript:**  
  Leverage the latest features for expressive, maintainable code with an emphasis on TypeScript's type safety.
- **Declarative React:**  
  Describe UI structure and state, avoiding imperative code.
- **Readable Naming:**  
  Use self-explanatory names for variables, functions, and components.
- **Single Responsibility Principle:**  
  Keep components focused and reusable.
- **Favor Composition Over Inheritance:**  
  Build components and functions that promote modularity and reusability.
- **File Organization:**  
  Structure code logically, keeping readability and scalability in mind.
- **Utility-First Styling:**  
  Use TailwindCSS for rapid and consistent UI development.

---

## General Conventions
- **Project Structure:**  
  - Organize components in a well-structured manner.
  - Separate concerns with clear folder structures.
  - Follow monorepo best practices (`apps/`, `packages/`,`services/`, `config/`).

- **Filenames:**  
  - Use lowercase with dash separators (e.g., `auth-wizard.tsx`).
  - File extensions should indicate file types (e.g., `.config.ts`, `.test.ts`, `.hook.tsx`).

- **Exporting:**  
  - Prefer named exports over default exports.

---

## JavaScript/TypeScript Best Practices
- **Naming Variables:**  
  - Use meaningful names that reflect purpose.
  - Prefix booleans with auxiliary verbs (e.g., `isLoading`, `hasPermission`).

- **Functional Programming:**  
  - Prefer `function` declarations for components.
  - Use the RORO (Receive an Object, Return an Object) pattern.

- **TypeScript Usage:**  
  - Use `interface` for objects and class definitions.
  - Use `type` for unions, tuples, and aliases.
  - Avoid `any`; prefer explicit types and inference when possible.
  - Use `as` for type assertions when necessary.
  - Explicitly annotate function parameters and return types.

---

## React/Next.js Conventions
- **Component Declaration Order:**
  1. Imports
  2. Component declaration
  3. Styled components (if any)
  4. TypeScript types and interfaces

- **Component Structure:**
  - Small, focused components following single responsibility.
  - Use hooks for state management; avoid unnecessary re-renders.
  - Follow the `use client` directive judiciously.

- **State Management & Fetching:**
  - Prefer React Server Components (RSC) for data-heavy operations.
  - Use `next-safe-action` for secure server actions.
  - Leverage Supabase for real-time data synchronization.

- **Performance Optimization:**
  - Lazy load non-critical components.
  - Use Suspense with fallbacks for client-side components.
  - Optimize image loading with `next/image`.

---

## AI SDK Integration (Vercel AI SDK)
- Use **Vercel AI SDK UI** for chat interfaces.
- Use **Vercel AI SDK Core** for model interactions.
- Handle rate limits and fallback gracefully.
- Sanitize user inputs before sending to models.
- Store API keys securely using environment variables.

---

## HuggingFace Models
- Use `@huggingface/inference` for model interactions.
- Cache frequent model responses to reduce latency.
- Handle model switching and errors gracefully.
- Optimize queries for efficient token usage.

---

## DrizzleORM Best Practices
- Use DrizzleORM for database interactions with Supabase.
- Leverage migrations for schema evolution.
- Follow the repository pattern to encapsulate database logic.
- Optimize queries with proper indexing.
- Adhere to Supabase RLS policies.

---

## Supabase & GraphQL Guidelines
- Use Genql for type-safe GraphQL queries.
- Follow Supabase best practices for authentication and authorization.
- Implement efficient data fetching by requesting only required fields.

---

## Naming Conventions
- **Booleans:** Prefix with `does`, `has`, `is`, `should` (e.g., `isValid`, `hasError`).
- **Files:** Use lowercase with dash separators (e.g., `project-detail.tsx`).
- **Extensions:** Follow the convention:
  - `.config.ts` for configurations
  - `.test.ts` for tests
  - `.context.tsx` for context files
  - `.type.ts` for types
  - `.hook.ts` for hooks

---

## Styling with Tailwind CSS
- Follow utility-first principles.
- Use CVA (Class Variance Authority) for managing component variants.
- Maintain a mobile-first responsive approach.

---

## Testing Guidelines
- **Unit Tests:** Use Jest for utility functions and hooks.
- **Integration Tests:** Validate component interactions.
- **End-to-End Tests:** Use Cypress for key user flows.
- Ensure code coverage meets project standards.

---

## Accessibility Guidelines
- Ensure all interactive elements are keyboard accessible.
- Use ARIA roles and attributes for screen readers.
- Maintain WCAG compliance for color contrast and readability.

---

## Documentation Standards
- Provide clear and concise comments for complex logic.
- Use JSDoc comments to improve IDE intellisense.
- Maintain up-to-date README files with setup instructions.
- Document Supabase schemas and edge functions where applicable.

---

## Key Conventions
1. Rely on Next.js App Router for routing and state changes.
2. Prioritize Web Vitals (LCP, CLS, FID) for performance.
3. Use a monorepo structure with shared code in `packages/` and app-specific code in `apps/`.
4. Use Taskfile for automation of development and deployment workflows.
5. Adhere to the defined database schema with enum tables for predefined values.

---

## Security Practices
- Store sensitive data in environment variables.
- Validate all incoming API requests for security compliance.
- Implement Role-Based Access Control (RBAC) using Supabase policies.
- Set security headers (CSP, HSTS, etc.) using Next.js config
- Implement rate limiting for API routes
- Use prepared statements for database queries
- Enable audit logging for sensitive operations
- Implement session management best practices
- Regular security dependency updates

---

## Performance Optimization
- Minimize client-side dependencies.
- Use streaming and suspense features in Next.js.
- Leverage Incremental Static Regeneration (ISR) where applicable.
- Optimize bundle size using code splitting.

---

Refer to the official documentation for best practices in:
- **Next.js** (Data Fetching, Routing, Rendering)
- **Vercel AI SDK** (AI Integration)
- **Supabase** (Database and Authentication)
- **TailwindCSS** (Styling)
bun
css
cypress
drizzle-orm
express.js
graphql
html
huggingface
+13 more

First seen in:

kindfi-org/kindfi

Used in 1 repository

TypeScript
You are an expert in React, TypeScript, Node.js, Express, Postgres, Prisma, SWR, Axios, Vite, React Router, Shadcn UI, Radix UI, Tailwind CSS, Zod, and React Hook Form.

Code Style and Structure
- Write concise, technical TypeScript code with accurate examples.
- Use functional and declarative programming patterns; avoid classes.
- Prefer iteration and modularization over code duplication.
- Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError).
- Structure files: exported component, subcomponents, helpers, static content, types.

Naming Conventions
- Use lowercase with dashes for directories (e.g., components/auth-form).
- Favor named exports for components.

TypeScript Usage
- Use TypeScript for all code; prefer interfaces over types when appropriate.
- Avoid enums; use const objects or maps instead.
- Use functional components with TypeScript interfaces.

Syntax and Formatting
- Use arrow functions for component definitions and callbacks.
- Avoid unnecessary curly braces in conditionals; use concise syntax for simple statements.
- Use declarative JSX.

UI and Styling
- Use Shadcn UI, Radix UI, and Tailwind CSS for components and styling.
- Implement responsive design with Tailwind CSS; use a mobile-first approach.

React and State Management
- Use React hooks (useState, useEffect, useCallback, useMemo) efficiently.
- Implement SWR for data fetching and caching.
- Use React Hook Form for form management and validation.

API and Data Handling
- Use Axios for API requests, configured with TypeScript.
- Implement Zod for runtime type checking and validation.
- Use Prisma for database operations and schema management.

Routing and Navigation
- Utilize React Router for client-side routing.
- Implement dynamic routing where appropriate.

Performance Optimization
- Use React.lazy and Suspense for code-splitting.
- Implement proper memoization using useMemo and useCallback.
- Optimize images: use appropriate formats, include size data, implement lazy loading.

Backend Development
- Use Express.js for server-side logic and API endpoints.
- Implement proper error handling and middleware in Express.

Database and ORM
- Use Postgres as the primary database.
- Leverage Prisma's type-safe queries and schema migrations.

Build and Development
- Utilize Vite for fast development and optimized production builds.
- Configure Vite for TypeScript, React, and CSS preprocessing.

Testing
- Implement unit tests for React components and utility functions.
- Use integration tests for API endpoints and database operations.

Security
- Implement proper authentication and authorization mechanisms.
- Use environment variables for sensitive information.
- Sanitize user inputs and implement CSRF protection.

Follow best practices for each technology in the stack, referring to their respective documentation for up-to-date information on data fetching, rendering, and routing.
css
dockerfile
express.js
html
javascript
postgresql
prisma
radix-ui
+5 more
mohinderps/sikhi-academy-3

Used in 1 repository

Python
# Mô tả Dự Án:
## TÊN ĐỀ TÀI: AutoGPT: Chatbot AI Đa Năng Tư Vấn và Đặt Món Ăn Trực Tuyến
AutoGPT là một chatbot AI Agent thông minh, được thiết kế để tự động hóa quy trình tư vấn và đặt món ăn trực tuyến -> cụ thể ở đây là tự động đặt đồ ăn và thêm món ăn vào giỏ hàng, mang đến trải nghiệm người dùng mượt mà và tiện lợi thông qua cả giao tiếp bằng văn bản và giọng nói.

## Mục tiêu:
Dự án này tập trung vào việc xây dựng một AI Agent đa năng, hỗ trợ người dùng trong việc:

1.  **Tư Vấn Món Ăn:** 
* Đóng vai trò như một chuyên viên tư vấn ẩm thực, hỗ trợ người dùng khám phá thực đơn, so sánh các lựa chọn món ăn và nhà hàng.
* Đưa ra các đề xuất món ăn phù hợp dựa trên nhu cầu, sở thích, và ngân sách của khách hàng.
* Cung cấp thông tin chi tiết về thành phần, đánh giá, và các ưu đãi liên quan đến món ăn.

1.  **Đặt Món Ăn và Thêm Vào Giỏ Hàng:** việc chatbot tự động đặt đồ ăn và thêm món ăn vào giỏ hàng cũng được coi là một hình thức đặt lịch sản phẩm trực tuyến. Trong trường hợp này, sản phẩm chính là các món ăn. Chức năng này cho phép người dùng tương tác với chatbot để chọn món ăn, thêm chúng vào giỏ hàng và tiến hành đặt món ăn một cách tự động và thuận tiện. Vì vậy, món ăn cũng được tính là sản phẩm trong ngữ cảnh này.
* Cho phép người dùng tương tác trực tiếp với chatbot để chọn món ăn, thêm vào giỏ hàng và tiến hành đặt món một cách tự động và nhanh chóng.
* Hỗ trợ các thao tác như tùy chỉnh món ăn (ví dụ: thêm topping, điều chỉnh khẩu vị) và xem lại đơn hàng trước khi xác nhận.

### Các tính năng chính của Chatbot AutoGPT:

* **Tự động hóa tương tác:** 
  Xử lý các yêu cầu và truy vấn của khách hàng một cách tự động, giảm thiểu sự can thiệp của nhân viên.

* **Phân tích nhu cầu:** 
  Hiểu rõ mong muốn của khách hàng thông qua các câu hỏi và yêu cầu tự nhiên.

* **Đề xuất thông minh:** 
  Đề xuất các món ăn và nhà hàng phù hợp dựa trên lịch sử tương tác, sở thích và vị trí của khách hàng.

* **Quản lý thông tin:** 
  Thu thập và lưu trữ thông tin khách hàng một cách an toàn và bảo mật.

* **Đa ngôn ngữ:** 
  (Nếu có thể) Hỗ trợ giao tiếp bằng nhiều ngôn ngữ, mở rộng phạm vi tiếp cận người dùng.

* **Tích hợp hệ thống:** 
  Kết nối với cơ sở dữ liệu món ăn và nhà hàng, cũng như hệ thống đặt hàng để đảm bảo hoạt động liền mạch.

* **Hỗ trợ đa nền tảng:** 
  Có khả năng hoạt động trên nhiều nền tảng (ví dụ: web, app, các ứng dụng nhắn tin).

### Điểm nổi bật của dự án:

* **AI Agent tự động:** 
  Sử dụng công nghệ AI Agent để tự động hóa toàn bộ quy trình, từ tư vấn đến đặt hàng.

* **Tương tác đa phương thức:**  
  Hỗ trợ cả giao tiếp bằng văn bản và giọng nói, mang lại sự linh hoạt cho người dùng.

* **Học hỏi và cải thiện:** 
  Sử dụng lịch sử tương tác để đưa ra các quyết định và đề xuất thông minh hơn theo thời gian.

* **Trải nghiệm người dùng tối ưu:** 
  Thiết kế giao diện thân thiện, dễ sử dụng, đảm bảo trải nghiệm người dùng mượt mà và thú vị.

**Ý nghĩa:**

Dự án AutoGPT là một minh chứng rõ ràng về việc ứng dụng AI để tự động hóa các tác vụ phức tạp trên nền tảng trực tuyến, mang lại sự tiện lợi và hiệu quả cho cả người dùng và doanh nghiệp.

## Công Nghệ Sử Dụng:

-   **LangChain Agent Framework:** Được sử dụng để xây dựng các agent có khả năng tương tác với các mô hình ngôn ngữ lớn LLM và các công cụ khác nhau.
-   **Mô Hình Ngôn Ngữ Lớn (LLM) của Groq:** Sử dụng mô hình ngôn ngữ lớn của Groq để tạo ra các phản hồi tự nhiên và hiệu quả.
-   **kỹ thuật RAG (Retrieval Augmented Generation) Cho tìm kiếm thông qua LangChain 🔍**: 
* Sử dụng Retrieval Augmented Generation để tìm kiếm nhà hàng thông minh
* Kết hợp vector search với xử lý ngôn ngữ tự nhiên
-   **SQLite:** Lưu trữ và truy xuất thông tin sản phẩm (chính là các món ăn) từ cơ sở dữ liệu. 
-   **Vue.js 3**: Framework chính để xây dựng Giao diện người dùng (frontend).
-   **FastAPI:** Xây dựng API (backend).
-   
## Công Nghệ Xử Lý Ngôn Ngữ Tự Nhiên và Học Máy:

-   **LangChain Agent Framework:** Framework này giúp xây dựng các agent có khả năng tương tác với các mô hình ngôn ngữ lớn (LLM) và các công cụ khác nhau.
-   **Mô Hình Ngôn Ngữ Lớn (LLM) của Groq:** Tạo ra các phản hồi tự nhiên.
-   **Prompt Engineering:** Kỹ thuật này được sử dụng để cải thiện chất lượng của các phản hồi từ mô hình ngôn ngữ bằng cách thiết kế các prompt (lời nhắc) phù hợp. Sử dụng các prompt để cải thiện chất lượng của các phản hồi từ mô hình ngôn ngữ.

## Luồng Hoạt Động của Chatbot:

1.  **Nhận Đầu Vào:** Chatbot nhận câu hỏi/yêu cầu từ người dùng (văn bản hoặc giọng nói).
2.  **Phân Tích Nhu Cầu:** Sử dụng LLM để hiểu ý định và trích xuất thông tin quan trọng từ yêu cầu.
3.  **Tư Vấn/Đề Xuất Sản Phẩm:** Dựa trên thông tin sản phẩm và yêu cầu của người dùng, chatbot đề xuất các sản phẩm phù hợp.
4.  **Đặt Lịch:** Nếu người dùng muốn đặt lịch, chatbot tự động xử lý yêu cầu này.
5.  **Phản Hồi:** Chatbot gửi phản hồi (văn bản hoặc giọng nói) cho người dùng.
6. **Quản lý thông tin:** Lưu trữ thông tin khách hàng và lịch sử tương tác để cải thiện chất lượng dịch vụ.

Chatbot có khả năng:
- Tự động hóa tương tác với khách hàng.
- Tìm kiếm sản phẩm dựa trên nhu cầu.
- Hỗ trợ đa ngôn ngữ (nếu cần).
- Tích hợp với cơ sở dữ liệu.

Áp dụng những thực tiễn tốt nhất để xây dựng hệ thống agents LangChain, 
chúng ta sẽ cùng nhau khám phá từng bước chi tiết ngay sau đây.
## Kiến Trúc AI Agent:

Hệ thống được chia thành hai phần chính: Frontend và Backend.

### Backend:

Backend sử dụng FastAPI để xây dựng API và xử lý các yêu cầu từ frontend.

Cấu trúc thư mục backend:

**Mô Tả Chi Tiết:**
-   **`backend/src/config`:** Chứa các file cấu hình ứng dụng, như API keys, credentials, v.v.
backend/src/config/__pycache__
backend/src/config/__init__.py
backend/src/config/config.yaml
- ** code file config.yaml:**
groq:
  api_key: $GROQ_API_KEY
  chat_model: "llama-3.1-70b-versatile"
  whisper_model: "whisper-large-v3"
  temperature: 0
  max_tokens: 512

qdrant:
  url: $QDRANT_URL
  api_key: $QDRANT_API_KEY

prompts:
  chat:
    filepath: "./src/prompts/chat/system.txt"

  functions:
    filepath: "./src/prompts/functions/signatures.json"

huggingface:
  api_key: $HUGGINGFACE_API_KEY
  embedding_model: "intfloat/multilingual-e5-small"

-   **`backend/src/data`:** Xử lý các thao tác liên quan đến cơ sở dữ liệu SQLite.
Chắc chắn rồi! Dưới đây là mô tả cấu trúc và chi tiết các file code trong folder `data` của project này 📁

### 1. Cấu trúc thư mục `data`

```
backend/src/data/
├── __init__.py
├── __pycache__
├── database.db
├── create_tables.py
├── database.py
├── data_models.py
├── data_utils.py
├── populate_fake_data.py
├── qdrant_restaurants.py
├── README.md
└── test_print_data.py
```

### 2. Chi tiết từng file

#### 2.1. `__init__.py`
- **Mục đích**: File này cho phép thư mục `data` được coi như một module Python.
- **Nội dung**: Import tất cả các class từ `data_models` và `database`, giúp dễ dàng truy cập các thành phần này từ module `data`.

#### 2.2. `create_tables.py`
- **Mục đích**: Tạo cấu trúc cơ sở dữ liệu bằng cách tạo các bảng đã được định nghĩa trong `data_models.py`.
- **Nội dung**:
  - Import `Base` và `engine` từ `database` để định nghĩa schema và kết nối đến cơ sở dữ liệu.
  - Hàm `main()` gọi `Base.metadata.create_all(bind=engine)` để tạo tất cả các bảng.
  - In ra thông báo khi tạo bảng thành công.
- Code file `create_tables.py`:

try:
    from database import Base, engine
    from data_models import Restaurant, Foods
except:
    from .database import Base, engine
    from .data_models import Restaurant, Foods

def main():
    Base.metadata.create_all(bind=engine)
    print("Tables created successfully!")

if __name__ == "__main__":
    main()

#### 2.3. `database.py`
- **Mục đích**: Quản lý kết nối đến cơ sở dữ liệu SQLite.
- **Nội dung**:
  - Sử dụng SQLAlchemy để tạo engine và session.
  - Định nghĩa `DATABASE_URL` để kết nối đến file `database.db`.
  - Tạo `SessionLocal` để quản lý phiên làm việc với cơ sở dữ liệu.
  - Định nghĩa `Base` để sử dụng cho các model.
- Code file `database.py`:
import os

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
DATABASE_URL = f"sqlite:///{os.path.join(BASE_DIR, 'database.db')}"

engine = create_engine(
    DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()

#### 2.4. `data_models.py`
- **Mục đích**: Định nghĩa các bảng trong cơ sở dữ liệu.
- **Nội dung**:
  - Định nghĩa hai class `Restaurant` và `Foods`, mỗi class tương ứng với một bảng trong cơ sở dữ liệu.
  - Các thuộc tính của class được định nghĩa bằng các cột trong bảng, bao gồm `id`, `name`, `description`, `image`, và `price`.
- Code file `data_models.py`:
from sqlalchemy import Column, Integer, String

try:
    from database import Base
except:
    from .database import Base

class Restaurant(Base):
    __tablename__ = "restaurants"

    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)
    description = Column(String, index=True)
    image = Column(String, index=True)

class Foods(Base):
    __tablename__ = "foods"

    id = Column(Integer, primary_key=True, index=True)
    restaurant_id = Column(Integer, index=True)
    name = Column(String, index=True)
    description = Column(String, index=True)
    image = Column(String, index=True)
    price = Column(Integer, index=True)

#### 2.5. `data_utils.py`
- **Mục đích**: Cung cấp các hàm tiện ích để truy xuất dữ liệu từ cơ sở dữ liệu.
- **Nội dung**:
  - Hàm `get_restaurants(db: Session)`: Truy xuất tất cả các nhà hàng từ bảng `restaurants`.
  - Hàm `get_foods(db: Session)`: Truy xuất tất cả các món ăn từ bảng `foods`.
  - Hàm `get_menu_of_given_restaurant(db: Session, restaurant_id: int)`: Truy xuất menu của một nhà hàng cụ thể.
  - Hàm `get_db()`: Tạo và quản lý phiên làm việc với cơ sở dữ liệu.
- Code file `data_utils.py`:
import pandas as pd
from sqlalchemy.orm import Session

try:
    from database import SessionLocal
    from data_models import Restaurant, Foods
except:
    from .database import SessionLocal
    from .data_models import Restaurant, Foods


def get_restaurants(db: Session):
    return db.query(Restaurant).all()

def get_foods(db: Session):
    return db.query(Foods).all()

def get_menu_of_given_restaurant(db: Session, restaurant_id: int):
    return db.query(Foods).filter(Foods.restaurant_id == restaurant_id).all()

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

#### 2.6. `populate_fake_data.py`
- **Mục đích**: Thêm dữ liệu giả vào cơ sở dữ liệu để kiểm tra.
- **Nội dung**:
  - Định nghĩa các hàm `add_restaurant` và `add_food` để thêm nhà hàng và món ăn vào cơ sở dữ liệu.
  - Hàm `main()` chứa dữ liệu mẫu cho các nhà hàng và món ăn, gọi các hàm thêm dữ liệu vào cơ sở dữ liệu.
- Code file `populate_fake_data.py`:
import pandas as pd
from sqlalchemy.orm import Session

from database import SessionLocal
from data_models import Restaurant, Foods


# Hàm thêm nhà hàng vào cơ sở dữ liệu
def add_restaurant(db: Session, name: str, description: str, image: str):
    restaurant = Restaurant(name=name, description=description, image=image)
    db.add(restaurant)
    db.commit()
    db.refresh(restaurant)
    return restaurant 


# Hàm thêm món ăn vào cơ sở dữ liệu
def add_food(
    db: Session, restaurant_id: int, name: str, description: str, image: str, price: int
):
    food = Foods(
        restaurant_id=restaurant_id,
        name=name,
        description=description,
        image=image,
        price=price,
    )
    db.add(food)
    db.commit()
    db.refresh(food)
    return food


def main():
    db = SessionLocal()

    # Sample data for restaurants
    restaurants = [
        {
            "name": "Co Vietnamese Restaurant & Vegan",
            "description": "Our restaurant specializes in providing Vietnamese dishes, outstanding products that are popular in Vietnam, inviting you to come with your friends and family to share an authentic Vietnamese feast.",
            "image": "/static/restaurants/CoVietnamese_Restaurant&Vegan.png",
        },
        {
            "name": "SushiLAB",
            "description": "Fresh sushi and Japanese dishes",
            "image": "/static/restaurants/sushi_lab.png",
        },
        {
            "name": "The Eroica Restaurant",
            "description": "The Eroica Restaurant will lead diners to a multi-layered culinary journey with a diverse menu of Asian and European delicacies. The fine-dining space is designed luxuriously, elegantly and harmoniously down to every detail, The Eroica promises to be an ideal destination for any special date: from a romantic dinner for couples, a family date or a company party... You will experience perfect culinary service with a variety of delicious dishes from our talented chefs.",
            "image": "/static/restaurants/the_eroica_restaurant.png",
        },
        {
            "name": "Burger King Hang Buom",
            "description": "Gourmet burgers and fries",
            "image": "/static/restaurants/burger_king_hang_buom.png",
        },
        {
            "name": "Pizza Hub - Wood Fired Pizza & Grills",
            "description": "Wood-fired pizzas",
            "image": "/static/restaurants/pizza_hub_wood_fired_pizza_grills.png",
        },
        {
            "name": "Indian Spice",
            "description": "Traditional Indian curries and tandoori",
            "image": "/static/restaurants/indian_spice.png",
        },
        {
            "name": "Thai Delight",
            "description": "Flavorful Thai dishes and noodles",
            "image": "/static/restaurants/thai_delight.png",
        },
        {
            "name": "Chinese Wok",
            "description": "Classic Chinese cuisine and dim sum",
            "image": "/static/restaurants/chinese_wok.png",
        },
        {
            "name": "Mediterranean Grill",
            "description": "Fresh Mediterranean flavors and kebabs",
            "image": "/static/restaurants/mediterranean_grill.png",
        },
        {
            "name": "French Bistro",
            "description": "Elegant French dining and pastries",
            "image": "/static/restaurants/french_bistro.png",
        },
        {
            "name": "Steakhouse",
            "description": "Premium steaks and seafood",
            "image": "/static/restaurants/steakhouse.png",
        },
        {
            "name": "Vegan Cafe",
            "description": "Plant-based dishes and smoothies",
            "image": "/static/restaurants/vegan_cafe.png",
        },
        {
            "name": "Greek Taverna",
            "description": "Greek classics and seafood",
            "image": "/static/restaurants/greek_taverna.png",
        },
        {
            "name": "Southern Comfort",
            "description": "Homestyle Southern cooking",
            "image": "/static/restaurants/southern_comfort.png",
        },
        {
            "name": "BBQ Shack",
            "description": "Smoked meats and barbecue",
            "image": "/static/restaurants/bbq_shack.png",
        },
        {
            "name": "Seafood Delight",
            "description": "Fresh seafood and fish dishes",
            "image": "/static/restaurants/seafood_delight.png",
        },
        {
            "name": "Veggie Heaven",
            "description": "Delicious vegetarian and vegan dishes",
            "image": "/static/restaurants/veggie_heaven.png",
        },
        {
            "name": "Chicken Coop",
            "description": "Grilled and fried chicken dishes",
            "image": "/static/restaurants/chicken_coop.png",
        },
        {
            "name": "Pasta Paradise",
            "description": "A variety of pasta dishes",
            "image": "/static/restaurants/pasta_paradise.png",
        },
        {
            "name": "Bakery Bliss",
            "description": "Freshly baked breads and pastries",
            "image": "/static/restaurants/bakery_bliss.png",
        },
        {
            "name": "Salad Bar",
            "description": "Healthy and delicious salads",
            "image": "/static/restaurants/salad_bar.png",
        },
        {
            "name": "Ramen House",
            "description": "Japanese ramen and noodle dishes",
            "image": "/static/restaurants/ramen_house.png",
        },
        {
            "name": "Ice Cream Shop",
            "description": "Variety of ice cream flavors and desserts",
            "image": "/static/restaurants/ice_cream_shop.png",
        },
        {
            "name": "Sandwich Shop",
            "description": "Freshly made sandwiches and wraps",
            "image": "/static/restaurants/sandwich_shop.png",
        },
        {
            "name": "Pancake House",
            "description": "Sweet and savory pancakes",
            "image": "/static/restaurants/pancake_house.png",
        },
        {
            "name": "Bun Cha Ta",
            "description": "Bun Cha Ta is a Vietnamese dish of grilled pork and noodle",
            "image": "/static/restaurants/bun_cha_ta.png",
        },
    ]

    # Sample data for foods
    foods = [
        # Co Vietnamese Restaurant & Vegan
        {
            "restaurant_id": 1,
            "name": "Fried spring rolls",
            "description": "Crispy or fresh spring rolls filled with various ingredients like vegetables, meat, and noodles.",
            "image": "/static/foods/fried_spring_rolls.png",
            "price": 70.000,
        },
        {
            "restaurant_id": 1,
            "name": "Traditional noodle soup with Beef/Chicken",
            "description": "A classic Vietnamese broth with rice noodles, served with your choice of beef or chicken.",
            "image": "/static/foods/traditional_noodle_soup_with_beef_chicken.png",
            "price": 70.000,
        },
        {
            "restaurant_id": 1,
            "name": "Special Vietnamese Pancace (Banh Xeo)",
            "description": "Savory Vietnamese crepe made with rice flour, turmeric, and coconut milk, often filled with shrimp, pork, and bean sprouts.",
            "image": "/static/foods/special_vietnamese_pancace_banh_xeo.png",
            "price": 100.000,
        },
        {
            "restaurant_id": 1,
            "name": "Beef or pork rolled with betel leaves come with fresh noodle (6pcs)",
            "description": "Grilled meat wrapped in betel leaves served with fresh rice noodles and dipping sauce.",
            "image": "/static/foods/beef_or_pork_rolled_with_betel_leaves_come_with_fresh_noodle_6pcs.png",
            "price": 100.000,
        },
        {
            "restaurant_id": 1,
            "name": "Banana flower salad with shrimp & Chicken",
            "description": "A refreshing salad featuring shredded banana flower, shrimp, chicken, and a tangy dressing.",
            "image": "/static/foods/banana_flower_salad_with_shrimp_chicken.png",
            "price": 110.000,
        },
        {
            "restaurant_id": 1,
            "name": "Hanoi traditional grilled pork with rice noodle (Bún chả)",
            "description": "A signature dish of Hanoi, featuring grilled pork served with rice noodles, herbs, and a dipping sauce.",
            "image": "/static/foods/hanoi_traditional_grilled_pork_with_rice_noodle_bun_cha.png",
            "price": 115.000,
        },
        {
            "restaurant_id": 1,
            "name": "Fried minced pork in lemongrass (Nem Lui)",
            "description": "Grilled skewers of minced pork seasoned with lemongrass, served with herbs and dipping sauce.",
            "image": "/static/foods/fried_minced_pork_in_lemongrass_nem_lui.png",
            "price": 90.000,
        },
        {
            "restaurant_id": 1,
            "name": "Vegetable soup with cream and mushroom soup",
            "description": "A creamy soup with vegetables and mushrooms.",
            "image": "/static/foods/vegetable_soup_with_cream_and_mushroom_soup.png",
            "price": 80.000,
        },
        # SushiLAB
        {
            "restaurant_id": 2,
            "name": "California Roll",
            "description": "Sushi roll with crab, avocado, and cucumber",
            "image": "/static/foods/california_roll.png",
            "price": 80.000,
        },
        {
            "restaurant_id": 2,
            "name": "Spicy Tuna Roll",
            "description": "Sushi roll with spicy tuna, mayo, and cucumber",
            "image": "/static/foods/spicy_tuna_roll.png",
            "price": 90.000,
        },
        {
            "restaurant_id": 2,
            "name": "Salmon Nigiri",
            "description": "Sushi with a slice of fresh salmon on top of sushi rice",
            "image": "/static/foods/salmon_nigiri.png",
            "price": 70.000,
        },
        {
            "restaurant_id": 2,
            "name": "Shrimp Tempura Roll",
            "description": "Sushi roll with tempura shrimp, avocado, and cucumber",
            "image": "/static/foods/shrimp_tempura_roll.png",
            "price": 100.000,
        },
        {
            "restaurant_id": 2,
            "name": "Miso Soup",
            "description": "Traditional Japanese soup with tofu and seaweed",
            "image": "/static/foods/miso_soup.png",
            "price": 40.000,
        },
        {
            "restaurant_id": 2,
            "name": "Sake",
            "description": "Japanese rice wine",
            "image": "/static/beverages/sake.png",
            "price": 60.000,
        },
        {
            "restaurant_id": 2,
            "name": "Green Tea",
            "description": "Hot Japanese green tea",
            "image": "/static/beverages/green_tea.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 2,
            "name": "Plum Wine",
            "description": "Sweet Japanese wine made from plums",
            "image": "/static/beverages/plum_wine.png",
            "price": 70.000,
        },
        # The Eroica Restaurant
        {
            "restaurant_id": 3,
            "name": "Vietnamese Clam Worm Pie",
            "description": "Clam worm, minced pork, egg, citrus peels, fennel, piper lolot (a type of Vietnamese herb), rice noodle",
            "image": "/static/foods/vietnamese_clam_worm_pie.png",
            "price": 230.000,
        },
        {
            "restaurant_id": 3,
            "name": "Vietnamese Beef Stew in Red Wine",
            "description": "Beef, ginger, carrot, lemongrass, cinnamon, star anise, bread",
            "image": "/static/foods/vietnamese_beef_stew_in_red_wine.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 3,
            "name": "Grilled Seabass in Banana Leaf",
            "description": "Seabass, banana blossom, lotus root salad, turmeric rice",
            "image": "/static/foods/grilled_seabass_in_banana_leaf.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 3,
            "name": "Vietnam Trio Nem",
            "description": "This dish is a combination of three different types of Vietnamese rolls",
            "image": "/static/foods/vietnam_trio_nem.png",
            "price": 40.000,
        },
        {
            "restaurant_id": 3,
            "name": "Hanoi Rice Vermicelli Noodles With Chicken And Eggs",
            "description": "This is a Hanoi specialty featuring rice vermicelli noodles with chicken and eggs.",
            "image": "/static/foods/hanoi_rice_vermicelli_noodles_with_chicken_and_eggs.png",
            "price": 50.000,
        },
        {
            "restaurant_id": 3,
            "name": "Vietnamese Phở",
            "description": "This dish refers to a popular Vietnamese noodle soup, specifically phở. You have the option of beef phở (phở bò) or chicken phở (phở gà).",
            "image": "/static/foods/vietnamese_pho.png",
            "price": 70.000,
        },
        {
            "restaurant_id": 3,
            "name": "Mắc Khén Grilled Chicken",
            "description": "This dish features grilled chicken marinated with mắc khén peppercorns, a unique and aromatic spice native to the northern mountainous regions of Vietnam.",
            "image": "/static/foods/mac_khen_grilled_chicken.png",
            "price": 50.000,
        },
        {
            "restaurant_id": 3,
            "name": "Chicken Parmigiana with Spaghetti and Vegetables",
            "description": "This dish is a classic Italian-American favorite. It features a breaded chicken breast topped with melted mozzarella cheese, baked to perfection. It's served with a side of spaghetti pasta and vegetables in a tomato-based sauce.",
            "image": "/static/foods/chicken_parmigiana_with_spaghetti_and_vegetables.png",
            "price": 40.000,
        },
        # Burger King Hang Buom
        {
            "restaurant_id": 4,
            "name": "Classic Cheeseburger",
            "description": "Juicy burger with cheese, lettuce, tomato, and onion",
            "image": "/static/foods/classic_cheeseburger.png",
            "price": 100.000,
        },
        {
            "restaurant_id": 4,
            "name": "Bacon Burger",
            "description": "Delicious burger with bacon, cheese, lettuce, tomato, and onion",
            "image": "/static/foods/bacon_burger.png",
            "price": 120.000,
        },
        {
            "restaurant_id": 4,
            "name": "Mushroom Swiss Burger",
            "description": "Tasty burger with Swiss cheese and sautéed mushrooms",
            "image": "/static/foods/mushroom_swiss_burger.png",
            "price": 110.000,
        },
        {
            "restaurant_id": 4,
            "name": "Veggie Burger",
            "description": "Healthy vegetarian burger with lettuce, tomato, and onion",
            "image": "/static/foods/veggie_burger.png",
            "price": 100.000,
        },
        {
            "restaurant_id": 4,
            "name": "French Fries",
            "description": "Crispy golden fries served with ketchup",
            "image": "/static/foods/french_fries.png",
            "price": 40.000,
        },
        {
            "restaurant_id": 4,
            "name": "Craft Beer",
            "description": "Local craft beer",
            "image": "/static/beverages/craft_beer.png",
            "price": 60.000,
        },
        {
            "restaurant_id": 4,
            "name": "Milkshake",
            "description": "Creamy milkshake with vanilla, chocolate, or strawberry",
            "image": "/static/beverages/milkshake.png",
            "price": 50.000,
        },
        {
            "restaurant_id": 4,
            "name": "Soda",
            "description": "Refreshing carbonated drink",
            "image": "/static/beverages/soda.png",
            "price": 30.000,
        },
        # Pizza Hub - Wood Fired Pizza & Grills
        {
            "restaurant_id": 5,
            "name": "Pepperoni Pizza",
            "description": "Delicious pizza with tomato sauce, mozzarella, and pepperoni",
            "image": "/static/foods/pepperoni_pizza.png",
            "price": 120.000,
        },
        {
            "restaurant_id": 5,
            "name": "Veggie Pizza",
            "description": "Healthy pizza with tomato sauce, mozzarella, bell peppers, onions, and olives",
            "image": "/static/foods/veggie_pizza.png",
            "price": 140.000,
        },
        {
            "restaurant_id": 5,
            "name": "Meat Lovers Pizza",
            "description": "Hearty pizza with tomato sauce, mozzarella, pepperoni, sausage, and bacon",
            "image": "/static/foods/meat_lovers_pizza.png",
            "price": 160.000,
        },
        {
            "restaurant_id": 5,
            "name": "BBQ Chicken Pizza",
            "description": "Tasty pizza with BBQ sauce, mozzarella, chicken, red onion, and cilantro",
            "image": "/static/foods/bbq_chicken_pizza.png",
            "price": 150.000,
        },
        {
            "restaurant_id": 5,
            "name": "Hawaiian Pizza",
            "description": "Sweet and savory pizza with tomato sauce, mozzarella, ham, and pineapple",
            "image": "/static/foods/hawaiian_pizza.png",
            "price": 140.000,
        },
        {
            "restaurant_id": 5,
            "name": "Italian Soda",
            "description": "Refreshing soda with a choice of fruit syrup",
            "image": "/static/beverages/italian_soda.png",
            "price": 40.000,
        },
        {
            "restaurant_id": 5,
            "name": "Chianti",
            "description": "Italian red wine",
            "image": "/static/beverages/chianti.png",
            "price": 80.000,
        },
        {
            "restaurant_id": 5,
            "name": "Limoncello",
            "description": "Italian lemon liqueur",
            "image": "/static/beverages/limoncello.png",
            "price": 70.000,
        },
        # Indian Spice
        {
            "restaurant_id": 6,
            "name": "Chicken Tikka Masala",
            "description": "Grilled chicken in a creamy tomato sauce served with basmati rice",
            "image": "/static/foods/chicken_tikka_masala.png",
            "price": 140.000,
        },
        {
            "restaurant_id": 6,
            "name": "Lamb Curry",
            "description": "Tender lamb in a spiced curry sauce served with basmati rice",
            "image": "/static/foods/lamb_curry.png",
            "price": 160.000,
        },
        {
            "restaurant_id": 6,
            "name": "Saag Paneer",
            "description": "Indian cheese in a creamy spinach sauce served with naan bread",
            "image": "/static/foods/saag_paneer.png",
            "price": 120.000,
        },
        {
            "restaurant_id": 6,
            "name": "Vegetable Biryani",
            "description": "Aromatic rice dish with mixed vegetables and spices served with raita",
            "image": "/static/foods/vegetable_biryani.png",
            "price": 100.000,
        },
        {
            "restaurant_id": 6,
            "name": "Garlic Naan",
            "description": "Leavened bread with garlic and butter",
            "image": "/static/foods/garlic_naan.png",
            "price": 40.000,
        },
        {
            "restaurant_id": 6,
            "name": "Mango Lassi",
            "description": "Refreshing yogurt-based drink with mango",
            "image": "/static/beverages/mango_lassi.png",
            "price": 40.000,
        },
        {
            "restaurant_id": 6,
            "name": "Masala Chai",
            "description": "Spiced tea with milk",
            "image": "/static/beverages/masala_chai.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 6,
            "name": "Kingfisher Beer",
            "description": "Indian lager beer",
            "image": "/static/beverages/kingfisher_beer.png",
            "price": 50.000,
        },
        # Thai Delight
        {
            "restaurant_id": 7,
            "name": "Pad Thai",
            "description": "Stir-fried rice noodles with shrimp, tofu, eggs, and peanuts",
            "image": "/static/foods/pad_thai.png",
            "price": 120.000,
        },
        {
            "restaurant_id": 7,
            "name": "Green Curry",
            "description": "Spicy green curry with chicken, eggplant, and basil served with jasmine rice",
            "image": "/static/foods/green_curry.png",
            "price": 140.000,
        },
        {
            "restaurant_id": 7,
            "name": "Tom Yum Soup",
            "description": "Hot and sour soup with shrimp, lemongrass, and mushrooms",
            "image": "/static/foods/tom_yum_soup.png",
            "price": 80.000,
        },
        {
            "restaurant_id": 7,
            "name": "Papaya Salad",
            "description": "Spicy salad with green papaya, tomatoes, and peanuts",
            "image": "/static/foods/papaya_salad.png",
            "price": 100.000,
        },
        {
            "restaurant_id": 7,
            "name": "Mango Sticky Rice",
            "description": "Sweet dessert with sticky rice, fresh mango, and coconut milk",
            "image": "/static/foods/mango_sticky_rice.png",
            "price": 60.000,
        },
        {
            "restaurant_id": 7,
            "name": "Thai Iced Tea",
            "description": "Sweet tea with milk",
            "image": "/static/beverages/thai_iced_tea.png",
            "price": 40.000,
        },
        {
            "restaurant_id": 7,
            "name": "Singha Beer",
            "description": "Thai lager beer",
            "image": "/static/beverages/singha_beer.png",
            "price": 50.000,
        },
        {
            "restaurant_id": 7,
            "name": "Lemongrass Juice",
            "description": "Refreshing juice with lemongrass",
            "image": "/static/beverages/lemongrass_juice.png",
            "price": 40.000,
        },
        # Chinese Wok
        {
            "restaurant_id": 8,
            "name": "Kung Pao Chicken",
            "description": "Stir-fried chicken with peanuts, vegetables, and chili peppers served with steamed rice",
            "image": "/static/foods/kung_pao_chicken.png",
            "price": 120.000,
        },
        {
            "restaurant_id": 8,
            "name": "Beef and Broccoli",
            "description": "Stir-fried beef with broccoli in a savory sauce served with steamed rice",
            "image": "/static/foods/beef_and_broccoli.png",
            "price": 140.000,
        },
        {
            "restaurant_id": 8,
            "name": "Sweet and Sour Pork",
            "description": "Battered pork with pineapple, bell peppers, and sweet and sour sauce served with steamed rice",
            "image": "/static/foods/sweet_and_sour_pork.png",
            "price": 120.000,
        },
        {
            "restaurant_id": 8,
            "name": "Vegetable Fried Rice",
            "description": "Fried rice with mixed vegetables and eggs",
            "image": "/static/foods/vegetable_fried_rice.png",
            "price": 100.000,
        },
        {
            "restaurant_id": 8,
            "name": "Spring Rolls",
            "description": "Crispy rolls filled with vegetables served with sweet and sour sauce",
            "image": "/static/foods/spring_rolls.png",
            "price": 60.000,
        },
        {
            "restaurant_id": 8,
            "name": "Tsingtao Beer",
            "description": "Chinese lager beer",
            "image": "/static/beverages/tsingtao_beer.png",
            "price": 50.000,
        },
        {
            "restaurant_id": 8,
            "name": "Jasmine Tea",
            "description": "Hot tea with jasmine flavor",
            "image": "/static/beverages/jasmine_tea.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 8,
            "name": "Plum Juice",
            "description": "Sweet juice made from plums",
            "image": "/static/beverages/plum_juice.png",
            "price": 40.000,
        },
        # Mediterranean Grill
        {
            "restaurant_id": 9,
            "name": "Chicken Shawarma",
            "description": "Marinated chicken with garlic sauce and pickles wrapped in pita bread",
            "image": "/static/foods/chicken_shawarma.png",
            "price": 100.000,
        },
        {
            "restaurant_id": 9,
            "name": "Falafel",
            "description": "Deep-fried chickpea patties with tahini sauce and salad wrapped in pita bread",
            "image": "/static/foods/falafel.png",
            "price": 80.000,
        },
        {
            "restaurant_id": 9,
            "name": "Greek Salad",
            "description": "Fresh salad with tomatoes, cucumbers, olives, and feta cheese",
            "image": "/static/foods/greek_salad.png",
            "price": 100.000,
        },
        {
            "restaurant_id": 9,
            "name": "Hummus",
            "description": "Chickpea dip with olive oil and pita bread",
            "image": "/static/foods/hummus.png",
            "price": 60.000,
        },
        {
            "restaurant_id": 9,
            "name": "Baklava",
            "description": "Sweet pastry with layers of filo and chopped nuts",
            "image": "/static/foods/baklava.png",
            "price": 50.000,
        },
        {
            "restaurant_id": 9,
            "name": "Greek Wine",
            "description": "White or red Greek wine",
            "image": "/static/beverages/greek_wine.png",
            "price": 70.000,
        },
        {
            "restaurant_id": 9,
            "name": "Turkish Coffee",
            "description": "Strong coffee served in a small cup",
            "image": "/static/beverages/turkish_coffee.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 9,
            "name": "Mint Lemonade",
            "description": "Refreshing lemonade with mint",
            "image": "/static/beverages/mint_lemonade.png",
            "price": 40.000,
        },
        # French Bistro
        {
            "restaurant_id": 10,
            "name": "Coq au Vin",
            "description": "Braised chicken with red wine, mushrooms, and onions served with mashed potatoes",
            "image": "/static/foods/coq_au_vin.png",
            "price": 180.000,
        },
        {
            "restaurant_id": 10,
            "name": "Beef Bourguignon",
            "description": "Slow-cooked beef with red wine, mushrooms, and carrots served with mashed potatoes",
            "image": "/static/foods/beef_bourguignon.png",
            "price": 200.000,
        },
        {
            "restaurant_id": 10,
            "name": "French Onion Soup",
            "description": "Caramelized onion soup with Gruyère cheese croutons",
            "image": "/static/foods/french_onion_soup.png",
            "price": 80.000,
        },
        {
            "restaurant_id": 10,
            "name": "Ratatouille",
            "description": "Stewed vegetables with tomato sauce and herbs served with crusty bread",
            "image": "/static/foods/ratatouille.png",
            "price": 120.000,
        },
        {
            "restaurant_id": 10,
            "name": "Crème Brûlée",
            "description": "Vanilla custard with caramelized sugar",
            "image": "/static/foods/creme_brulee.png",
            "price": 60.000,
        },
        {
            "restaurant_id": 10,
            "name": "French Wine",
            "description": "Red or white French wine",
            "image": "/static/beverages/french_wine.png",
            "price": 80.000,
        },
        {
            "restaurant_id": 10,
            "name": "Espresso",
            "description": "Strong French coffee",
            "image": "/static/beverages/espresso.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 10,
            "name": "Lemonade",
            "description": "Refreshing lemonade",
            "image": "/static/beverages/lemonade.png",
            "price": 40.000,
        },
        # Steakhouse
        {
            "restaurant_id": 11,
            "name": "Ribeye Steak",
            "description": "Grilled ribeye steak with garlic herb butter served with mashed potatoes",
            "image": "/static/foods/ribeye_steak.png",
            "price": 280.000,
        },
        {
            "restaurant_id": 11,
            "name": "Filet Mignon",
            "description": "Tender filet mignon with red wine reduction served with mashed potatoes",
            "image": "/static/foods/filet_mignon.png",
            "price": 320.000,
        },
        {
            "restaurant_id": 11,
            "name": "Grilled Salmon",
            "description": "Grilled salmon with lemon herb sauce served with steamed vegetables",
            "image": "/static/foods/grilled_salmon.png",
            "price": 220.000,
        },
        {
            "restaurant_id": 11,
            "name": "Caesar Salad",
            "description": "Classic salad with romaine lettuce, croutons, and Parmesan cheese",
            "image": "/static/foods/caesar_salad.png",
            "price": 100.000,
        },
        {
            "restaurant_id": 11,
            "name": "Mashed Potatoes",
            "description": "Creamy mashed potatoes with butter",
            "image": "/static/foods/mashed_potatoes.png",
            "price": 60.000,
        },
        {
            "restaurant_id": 11,
            "name": "Cabernet Sauvignon",
            "description": "Full-bodied red wine",
            "image": "/static/beverages/cabernet_sauvignon.png",
            "price": 80.000,
        },
        {
            "restaurant_id": 11,
            "name": "Craft Beer",
            "description": "Local craft beer",
            "image": "/static/beverages/craft_beer.png",
            "price": 60.000,
        },
        {
            "restaurant_id": 11,
            "name": "Whiskey",
            "description": "Aged whiskey on the rocks",
            "image": "/static/beverages/whiskey.png",
            "price": 70.000,
        },
        # Vegan Cafe
        {
            "restaurant_id": 12,
            "name": "Vegan Burger",
            "description": "Plant-based burger with lettuce, tomato, and onion served with sweet potato fries",
            "image": "/static/foods/vegan_burger.png",
            "price": 120.000,
        },
        {
            "restaurant_id": 12,
            "name": "Quinoa Salad",
            "description": "Healthy salad with quinoa, mixed greens, and roasted vegetables",
            "image": "/static/foods/quinoa_salad.png",
            "price": 100.000,
        },
        {
            "restaurant_id": 12,
            "name": "Lentil Soup",
            "description": "Hearty soup with lentils and vegetables",
            "image": "/static/foods/lentil_soup.png",
            "price": 80.000,
        },
        {
            "restaurant_id": 12,
            "name": "Stuffed Bell Peppers",
            "description": "Bell peppers filled with quinoa, beans, and vegetables",
            "image": "/static/foods/stuffed_bell_peppers.png",
            "price": 140.000,
        },
        {
            "restaurant_id": 12,
            "name": "Fruit Smoothie",
            "description": "Blended smoothie with a choice of fruits and almond milk",
            "image": "/static/foods/fruit_smoothie.png",
            "price": 60.000,
        },
        {
            "restaurant_id": 12,
            "name": "Green Juice",
            "description": "Healthy juice with kale, spinach, and apple",
            "image": "/static/beverages/green_juice.png",
            "price": 50.000,
        },
        {
            "restaurant_id": 12,
            "name": "Herbal Tea",
            "description": "Hot tea with a choice of herbs",
            "image": "/static/beverages/herbal_tea.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 12,
            "name": "Almond Milk Latte",
            "description": "Coffee with almond milk",
            "image": "/static/beverages/almond_milk_latte.png",
            "price": 40.000,
        },
        # Greek Taverna
        {
            "restaurant_id": 13,
            "name": "Gyro",
            "description": "Grilled meat with tzatziki sauce, tomatoes, and onions wrapped in pita bread",
            "image": "/static/foods/gyro.png",
            "price": 100.000,
        },
        {
            "restaurant_id": 13,
            "name": "Moussaka",
            "description": "Layered dish with eggplant, ground beef, and béchamel sauce",
            "image": "/static/foods/moussaka.png",
            "price": 140.000,
        },
        {
            "restaurant_id": 13,
            "name": "Spanakopita",
            "description": "Savory pastry with spinach and feta cheese",
            "image": "/static/foods/spanakopita.png",
            "price": 80.000,
        },
        {
            "restaurant_id": 13,
            "name": "Dolmades",
            "description": "Stuffed grape leaves with rice and herbs",
            "image": "/static/foods/dolmades.png",
            "price": 60.000,
        },
        {
            "restaurant_id": 13,
            "name": "Greek Yogurt",
            "description": "Thick yogurt with honey and walnuts",
            "image": "/static/foods/greek_yogurt.png",
            "price": 50.000,
        },
        {
            "restaurant_id": 13,
            "name": "Greek Wine",
            "description": "White or red Greek wine",
            "image": "/static/beverages/greek_wine.png",
            "price": 70.000,
        },
        {
            "restaurant_id": 13,
            "name": "Ouzo",
            "description": "Anise-flavored Greek liquor",
            "image": "/static/beverages/ouzo.png",
            "price": 60.000,
        },
        {
            "restaurant_id": 13,
            "name": "Greek Coffee",
            "description": "Strong coffee served in a small cup",
            "image": "/static/beverages/greek_coffee.png",
            "price": 30.000,
        },
        # Southern Comfort
        {
            "restaurant_id": 14,
            "name": "Fried Chicken",
            "description": "Crispy fried chicken served with mashed potatoes and gravy",
            "image": "/static/foods/fried_chicken.png",
            "price": 140.000,
        },
        {
            "restaurant_id": 14,
            "name": "Shrimp and Grits",
            "description": "Sautéed shrimp with creamy grits and bacon",
            "image": "/static/foods/shrimp_and_grits.png",
            "price": 160.000,
        },
        {
            "restaurant_id": 14,
            "name": "Chicken and Waffles",
            "description": "Fried chicken with fluffy waffles and maple syrup",
            "image": "/static/foods/chicken_and_waffles.png",
            "price": 140.000,
        },
        {
            "restaurant_id": 14,
            "name": "Biscuits and Gravy",
            "description": "Buttermilk biscuits with sausage gravy",
            "image": "/static/foods/biscuits_and_gravy.png",
            "price": 80.000,
        },
        {
            "restaurant_id": 14,
            "name": "Pecan Pie",
            "description": "Sweet pie with pecan filling",
            "image": "/static/foods/pecan_pie.png",
            "price": 60.000,
        },
        {
            "restaurant_id": 14,
            "name": "Sweet Tea",
            "description": "Refreshing iced tea with sugar",
            "image": "/static/beverages/sweet_tea.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 14,
            "name": "Bourbon",
            "description": "Aged bourbon on the rocks",
            "image": "/static/beverages/bourbon.png",
            "price": 70.000,
        },
        {
            "restaurant_id": 14,
            "name": "Mint Julep",
            "description": "Classic cocktail with bourbon, mint, and sugar",
            "image": "/static/beverages/mint_julep.png",
            "price": 70.000,
        },
        # BBQ Shack
        {
            "restaurant_id": 15,
            "name": "Pulled Pork Sandwich",
            "description": "Slow-cooked pulled pork with BBQ sauce served with coleslaw",
            "image": "/static/foods/pulled_pork_sandwich.png",
            "price": 100.000,
        },
        {
            "restaurant_id": 15,
            "name": "Beef Brisket",
            "description": "Smoked beef brisket with BBQ sauce served with coleslaw",
            "image": "/static/foods/beef_brisket.png",
            "price": 140.000,
        },
        {
            "restaurant_id": 15,
            "name": "Baby Back Ribs",
            "description": "Tender pork ribs with BBQ sauce served with coleslaw",
            "image": "/static/foods/baby_back_ribs.png",
            "price": 160.000,
        },
        {
            "restaurant_id": 15,
            "name": "Mac and Cheese",
            "description": "Creamy macaroni and cheese",
            "image": "/static/foods/mac_and_cheese.png",
            "price": 60.000,
        },
        {
            "restaurant_id": 15,
            "name": "Cornbread",
            "description": "Sweet and moist cornbread",
            "image": "/static/foods/cornbread.png",
            "price": 40.000,
        },
        {
            "restaurant_id": 15,
            "name": "Craft Beer",
            "description": "Local craft beer",
            "image": "/static/beverages/craft_beer.png",
            "price": 60.000,
        },
        {
            "restaurant_id": 15,
            "name": "Sweet Tea",
            "description": "Refreshing iced tea with sugar",
            "image": "/static/beverages/sweet_tea.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 15,
            "name": "Bourbon",
            "description": "Aged bourbon on the rocks",
            "image": "/static/beverages/bourbon.png",
            "price": 70.000,
        },
        # Seafood Delight
        {
            "restaurant_id": 16,
            "name": "Grilled Salmon",
            "description": "Grilled salmon with lemon herb sauce served with steamed vegetables",
            "image": "/static/foods/grilled_salmon.png",
            "price": 220.000,
        },
        {
            "restaurant_id": 16,
            "name": "Shrimp Scampi",
            "description": "Sautéed shrimp with garlic, white wine, and butter served with pasta",
            "image": "/static/foods/shrimp_scampi.png",
            "price": 200.000,
        },
        {
            "restaurant_id": 16,
            "name": "Clam Chowder",
            "description": "Creamy soup with clams, potatoes, and onions",
            "image": "/static/foods/clam_chowder.png",
            "price": 80.000,
        },
        {
            "restaurant_id": 16,
            "name": "Fish and Chips",
            "description": "Battered fish with crispy fries and tartar sauce",
            "image": "/static/foods/fish_and_chips.png",
            "price": 140.000,
        },
        {
            "restaurant_id": 16,
            "name": "Lobster Roll",
            "description": "Fresh lobster with mayo and celery on a toasted bun",
            "image": "/static/foods/lobster_roll.png",
            "price": 180.000,
        },
        {
            "restaurant_id": 16,
            "name": "White Wine",
            "description": "Refreshing white wine",
            "image": "/static/beverages/white_wine.png",
            "price": 70.000,
        },
        {
            "restaurant_id": 16,
            "name": "Craft Beer",
            "description": "Local craft beer",
            "image": "/static/beverages/craft_beer.png",
            "price": 60.000,
        },
        {
            "restaurant_id": 16,
            "name": "Iced Tea",
            "description": "Refreshing iced tea",
            "image": "/static/beverages/iced_tea.png",
            "price": 30.000,
        },
        # Veggie Heaven
        {
            "restaurant_id": 17,
            "name": "Veggie Burger",
            "description": "Plant-based burger with lettuce, tomato, and onion served with sweet potato fries",
            "image": "/static/foods/veggie_burger.png",
            "price": 120.000,
        },
        {
            "restaurant_id": 17,
            "name": "Quinoa Salad",
            "description": "Healthy salad with quinoa, mixed greens, and roasted vegetables",
            "image": "/static/foods/quinoa_salad.png",
            "price": 100.000,
        },
        {
            "restaurant_id": 17,
            "name": "Lentil Soup",
            "description": "Hearty soup with lentils and vegetables",
            "image": "/static/foods/lentil_soup.png",
            "price": 80.000,
        },
        {
            "restaurant_id": 17,
            "name": "Stuffed Bell Peppers",
            "description": "Bell peppers filled with quinoa, beans, and vegetables",
            "image": "/static/foods/stuffed_bell_peppers.png",
            "price": 140.000,
        },
        {
            "restaurant_id": 17,
            "name": "Vegan Chocolate Cake",
            "description": "Delicious chocolate cake made with plant-based ingredients",
            "image": "/static/foods/vegan_chocolate_cake.png",
            "price": 60.000,
        },
        {
            "restaurant_id": 17,
            "name": "Green Juice",
            "description": "Healthy juice with kale, spinach, and apple",
            "image": "/static/beverages/green_juice.png",
            "price": 50.000,
        },
        {
            "restaurant_id": 17,
            "name": "Herbal Tea",
            "description": "Hot tea with a choice of herbs",
            "image": "/static/beverages/herbal_tea.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 17,
            "name": "Almond Milk Latte",
            "description": "Coffee with almond milk",
            "image": "/static/beverages/almond_milk_latte.png",
            "price": 40.000,
        },
        # Chicken Coop
        {
            "restaurant_id": 18,
            "name": "Fried Chicken",
            "description": "Crispy fried chicken served with mashed potatoes and gravy",
            "image": "/static/foods/fried_chicken.png",
            "price": 140.000,
        },
        {
            "restaurant_id": 18,
            "name": "Chicken and Waffles",
            "description": "Fried chicken with fluffy waffles and maple syrup",
            "image": "/static/foods/chicken_and_waffles.png",
            "price": 140.000,
        },
        {
            "restaurant_id": 18,
            "name": "Chicken Caesar Salad",
            "description": "Classic salad with romaine lettuce, croutons, Parmesan cheese, and grilled chicken",
            "image": "/static/foods/chicken_caesar_salad.png",
            "price": 120.000,
        },
        {
            "restaurant_id": 18,
            "name": "Chicken Noodle Soup",
            "description": "Hearty soup with chicken, noodles, and vegetables",
            "image": "/static/foods/chicken_noodle_soup.png",
            "price": 80.000,
        },
        {
            "restaurant_id": 18,
            "name": "Chicken Pot Pie",
            "description": "Comforting pie with chicken and vegetables in a creamy sauce",
            "image": "/static/foods/chicken_pot_pie.png",
            "price": 120.000,
        },
        {
            "restaurant_id": 18,
            "name": "Craft Beer",
            "description": "Local craft beer",
            "image": "/static/beverages/craft_beer.png",
            "price": 60.000,
        },
        {
            "restaurant_id": 18,
            "name": "Sweet Tea",
            "description": "Refreshing iced tea with sugar",
            "image": "/static/beverages/sweet_tea.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 18,
            "name": "Lemonade",
            "description": "Refreshing lemonade",
            "image": "/static/beverages/lemonade.png",
            "price": 40.000,
        },
        # Pasta Paradise
        {
            "restaurant_id": 19,
            "name": "Spaghetti Carbonara",
            "description": "Creamy pasta with pancetta, egg, and Pecorino Romano cheese",
            "image": "/static/foods/spaghetti_carbonara.png",
            "price": 140.000,
        },
        {
            "restaurant_id": 19,
            "name": "Fettuccine Alfredo",
            "description": "Pasta with creamy Alfredo sauce and Parmesan cheese",
            "image": "/static/foods/fettuccine_alfredo.png",
            "price": 120.000,
        },
        {
            "restaurant_id": 19,
            "name": "Lasagna",
            "description": "Layered pasta with ground beef, tomato sauce, and cheese",
            "image": "/static/foods/lasagna.png",
            "price": 160.000,
        },
        {
            "restaurant_id": 19,
            "name": "Pesto Pasta",
            "description": "Pasta with basil pesto sauce and Parmesan cheese",
            "image": "/static/foods/pesto_pasta.png",
            "price": 120.000,
        },
        {
            "restaurant_id": 19,
            "name": "Tiramisu",
            "description": "Classic Italian dessert with coffee-soaked ladyfingers and mascarpone cream",
            "image": "/static/foods/tiramisu.png",
            "price": 80.000,
        },
        {
            "restaurant_id": 19,
            "name": "Chianti",
            "description": "Italianred wine",
            "image": "/static/beverages/chianti.png",
            "price": 80.000,
        },
        {
            "restaurant_id": 19,
            "name": "Espresso",
            "description": "Strong Italian coffee",
            "image": "/static/beverages/espresso.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 19,
            "name": "Limoncello",
            "description": "Italian lemon liqueur",
            "image": "/static/beverages/limoncello.png",
            "price": 70.000,
        },
        # Bakery Bliss
        {
            "restaurant_id": 20,
            "name": "Croissant",
            "description": "Flaky and buttery French pastry",
            "image": "/static/foods/croissant.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 20,
            "name": "Baguette",
            "description": "Crusty French bread",
            "image": "/static/foods/baguette.png",
            "price": 20.000,
        },
        {
            "restaurant_id": 20,
            "name": "Cinnamon Roll",
            "description": "Sweet roll with cinnamon and cream cheese frosting",
            "image": "/static/foods/cinnamon_roll.png",
            "price": 40.000,
        },
        {
            "restaurant_id": 20,
            "name": "Blueberry Muffin",
            "description": "Moist muffin with blueberries",
            "image": "/static/foods/blueberry_muffin.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 20,
            "name": "Chocolate Chip Cookie",
            "description": "Sweet cookie with chocolate chips",
            "image": "/static/foods/chocolate_chip_cookie.png",
            "price": 20.000,
        },
        {
            "restaurant_id": 20,
            "name": "Coffee",
            "description": "Hot coffee with a choice of milk",
            "image": "/static/beverages/coffee.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 20,
            "name": "Tea",
            "description": "Hot tea with a choice of flavors",
            "image": "/static/beverages/tea.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 20,
            "name": "Hot Chocolate",
            "description": "Sweet hot chocolate with whipped cream",
            "image": "/static/beverages/hot_chocolate.png",
            "price": 40.000,
        },
        # Salad Bar
        {
            "restaurant_id": 21,
            "name": "Greek Salad",
            "description": "Fresh salad with tomatoes, cucumbers, olives, and feta cheese",
            "image": "/static/foods/greek_salad.png",
            "price": 100.000,
        },
        {
            "restaurant_id": 21,
            "name": "Caesar Salad",
            "description": "Classic salad with romaine lettuce, croutons, and Parmesan cheese",
            "image": "/static/foods/caesar_salad.png",
            "price": 80.000,
        },
        {
            "restaurant_id": 21,
            "name": "Cobb Salad",
            "description": "Hearty salad with chicken, bacon, avocado, eggs, and blue cheese",
            "image": "/static/foods/cobb_salad.png",
            "price": 120.000,
        },
        {
            "restaurant_id": 21,
            "name": "Spinach Salad",
            "description": "Healthy salad with spinach, strawberries, almonds, and goat cheese",
            "image": "/static/foods/spinach_salad.png",
            "price": 100.000,
        },
        {
            "restaurant_id": 21,
            "name": "Quinoa Salad",
            "description": "Healthy salad with quinoa, mixed greens, and roasted vegetables",
            "image": "/static/foods/quinoa_salad.png",
            "price": 100.000,
        },
        {
            "restaurant_id": 21,
            "name": "Green Juice",
            "description": "Healthy juice with kale, spinach, and apple",
            "image": "/static/beverages/green_juice.png",
            "price": 50.000,
        },
        {
            "restaurant_id": 21,
            "name": "Herbal Tea",
            "description": "Hot tea with a choice of herbs",
            "image": "/static/beverages/herbal_tea.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 21,
            "name": "Fruit Smoothie",
            "description": "Blended smoothie with a choice of fruits and almond milk",
            "image": "/static/beverages/fruit_smoothie.png",
            "price": 60.000,
        },
        # Ramen House
        {
            "restaurant_id": 22,
            "name": "Tonkotsu Ramen",
            "description": "Ramen with pork bone broth, pork belly, and soft-boiled egg",
            "image": "/static/foods/tonkotsu_ramen.png",
            "price": 120.000,
        },
        {
            "restaurant_id": 22,
            "name": "Shoyu Ramen",
            "description": "Ramen with soy sauce broth, chicken, and soft-boiled egg",
            "image": "/static/foods/shoyu_ramen.png",
            "price": 110.000,
        },
        {
            "restaurant_id": 22,
            "name": "Miso Ramen",
            "description": "Ramen with miso broth, corn, and soft-boiled egg",
            "image": "/static/foods/miso_ramen.png",
            "price": 110.000,
        },
        {
            "restaurant_id": 22,
            "name": "Gyoza",
            "description": "Japanese dumplings with pork and vegetables",
            "image": "/static/foods/gyoza.png",
            "price": 60.000,
        },
        {
            "restaurant_id": 22,
            "name": "Takoyaki",
            "description": "Fried octopus balls with bonito flakes and mayo",
            "image": "/static/foods/takoyaki.png",
            "price": 60.000,
        },
        {
            "restaurant_id": 22,
            "name": "Sake",
            "description": "Japanese rice wine",
            "image": "/static/beverages/sake.png",
            "price": 60.000,
        },
        {
            "restaurant_id": 22,
            "name": "Green Tea",
            "description": "Hot Japanese green tea",
            "image": "/static/beverages/green_tea.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 22,
            "name": "Plum Wine",
            "description": "Sweet Japanese wine made from plums",
            "image": "/static/beverages/plum_wine.png",
            "price": 70.000,
        },
        # Ice Cream Shop
        {
            "restaurant_id": 23,
            "name": "Vanilla Ice Cream",
            "description": "Creamy ice cream with vanilla flavor",
            "image": "/static/foods/vanilla_ice_cream.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 23,
            "name": "Chocolate Ice Cream",
            "description": "Rich ice cream with chocolate flavor",
            "image": "/static/foods/chocolate_ice_cream.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 23,
            "name": "Strawberry Ice Cream",
            "description": "Sweet ice cream with strawberry flavor",
            "image": "/static/foods/strawberry_ice_cream.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 23,
            "name": "Mint Chocolate Chip Ice Cream",
            "description": "Refreshing ice cream with mint and chocolate chips",
            "image": "/static/foods/mint_chocolate_chip_ice_cream.png",
            "price": 40.000,
        },
        {
            "restaurant_id": 23,
            "name": "Cookie Dough Ice Cream",
            "description": "Delicious ice cream with chunks of cookie dough",
            "image": "/static/foods/cookie_dough_ice_cream.png",
            "price": 40.000,
        },
        {
            "restaurant_id": 23,
            "name": "Root Beer Float",
            "description": "Classic dessert with root beer and vanilla ice cream",
            "image": "/static/beverages/root_beer_float.png",
            "price": 50.000,
        },
        {
            "restaurant_id": 23,
            "name": "Milkshake",
            "description": "Creamy milkshake with vanilla, chocolate, or strawberry",
            "image": "/static/beverages/milkshake.png",
            "price": 50.000,
        },
        {
            "restaurant_id": 23,
            "name": "Iced Coffee",
            "description": "Refreshing iced coffee with a choice of milk",
            "image": "/static/beverages/iced_coffee.png",
            "price": 40.000,
        },
        # Sandwich Shop
        {
            "restaurant_id": 24,
            "name": "Turkey Sandwich",
            "description": "Sandwich with turkey, lettuce, tomato, and mayo",
            "image": "/static/foods/turkey_sandwich.png",
            "price": 80.000,
        },
        {
            "restaurant_id": 24,
            "name": "Ham Sandwich",
            "description": "Sandwich with ham, cheese, lettuce, and mustard",
            "image": "/static/foods/ham_sandwich.png",
            "price": 80.000,
        },
        {
            "restaurant_id": 24,
            "name": "Tuna Salad Sandwich",
            "description": "Sandwich with tuna salad and lettuce",
            "image": "/static/foods/tuna_salad_sandwich.png",
            "price": 80.000,
        },
        {
            "restaurant_id": 24,
            "name": "BLT Sandwich",
            "description": "Classic sandwich with bacon, lettuce, and tomato",
            "image": "/static/foods/blt_sandwich.png",
            "price": 80.000,
        },
        {
            "restaurant_id": 24,
            "name": "Grilled Cheese Sandwich",
            "description": "Comforting sandwich with melted cheese",
            "image": "/static/foods/grilled_cheese_sandwich.png",
            "price": 60.000,
        },
        {
            "restaurant_id": 24,
            "name": "Soda",
            "description": "Refreshing carbonated drink",
            "image": "/static/beverages/soda.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 24,
            "name": "Iced Tea",
            "description": "Refreshing iced tea",
            "image": "/static/beverages/iced_tea.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 24,
            "name": "Coffee",
            "description": "Hot coffee with a choice of milk",
            "image": "/static/beverages/coffee.png",
            "price": 30.000,
        },
        # Pancake House
        {
            "restaurant_id": 25,
            "name": "Buttermilk Pancakes",
            "description": "Fluffy pancakes served with butter and maple syrup",
            "image": "/static/foods/buttermilk_pancakes.png",
            "price": 80.000,
        },
        {
            "restaurant_id": 25,
            "name": "Blueberry Pancakes",
            "description": "Pancakes with blueberries served with butter and maple syrup",
            "image": "/static/foods/blueberry_pancakes.png",
            "price": 90.000,
        },
        {
            "restaurant_id": 25,
            "name": "Chocolate Chip Pancakes",
            "description": "Pancakes with chocolate chips served with butter and maple syrup",
            "image": "/static/foods/chocolate_chip_pancakes.png",
            "price": 90.000,
        },
        {
            "restaurant_id": 25,
            "name": "Bacon and Egg Pancakes",
            "description": "Pancakes with bacon and eggs served with butter and maple syrup",
            "image": "/static/foods/bacon_and_egg_pancakes.png",
            "price": 100.000,
        },
        {
            "restaurant_id": 25,
            "name": "Banana Nut Pancakes",
            "description": "Pancakes with bananas and nuts served with butter and maple syrup",
            "image": "/static/foods/banana_nut_pancakes.png",
            "price": 90.000,
        },
        {
            "restaurant_id": 25,
            "name": "Coffee",
            "description": "Hot coffee with a choice of milk",
            "image": "/static/beverages/coffee.png",
            "price": 30.000,
        },
        {
            "restaurant_id": 25,
            "name": "Orange Juice",
            "description": "Freshly squeezed orange juice",
            "image": "/static/beverages/orange_juice.png",
            "price": 40.000,
        },
        {
            "restaurant_id": 25,
            "name": "Milk",
            "description": "Cold milk",
            "image": "beverages/milk.png",
            "price": 20.000,
        },
        # bun cha ta
        {
            "restaurant_id": 1,
            "name": "Bun Cha Ta",
            "description": "Bun Cha Ta is a Vietnamese dish of grilled pork and noodles served with a sweet and sour sauce.",
            "image": "/static/foods/bun_cha_ta.png",
            "price": 100.000,
        },
    ]

    # Populate restaurants table
    for restaurant in restaurants:
        add_restaurant(
            db, restaurant["name"], restaurant["description"], restaurant["image"]
        )

    # Populate foods table
    for food in foods:
        add_food(
            db,
            food["restaurant_id"],
            food["name"],
            food["description"],
            food["image"],
            food["price"],
        )

    print("Data populated successfully!")


if __name__ == "__main__":
    main()

#### 2.7. `README.md`
- **Mục đích**: Cung cấp hướng dẫn về cách tạo và populate cơ sở dữ liệu.
- **Nội dung**: Hướng dẫn từng bước để tạo cơ sở dữ liệu từ đầu, bao gồm chạy các file `create_tables.py`, `populate_fake_data.py`, và `qdrant_restaurants.py`.
- Nội dung trong readme.md: 
# Data

In order to create a new database from scratch:

- ```python create_tables.py ``` - this will create the database.db
- ```python populate_fake_data.py``` - this will populate it with fake data
- ```python qdrant_restaurants.py``` - this will upload the data to the qdrant database setup (remember to have setup a qdrant vector db)

#### 2.8. `test_print_data.py`
- **Mục đích**: Kiểm tra và in ra dữ liệu từ cơ sở dữ liệu.
- **Nội dung**:
  - Hàm `get_restaurants` và `get_foods` để truy xuất dữ liệu từ các bảng.
  - Hàm `main()` để in ra dữ liệu của các nhà hàng và món ăn dưới dạng DataFrame của pandas.
- Code trong file `test_print_data.py`:
import pandas as pd
from sqlalchemy.orm import Session

from database import SessionLocal
from data_models import Restaurant, Foods

def get_restaurants(db: Session):
    return db.query(Restaurant).all()

def get_foods(db: Session):
    return db.query(Foods).all()

def main():
    db = SessionLocal()

    # Fetch data from the restaurants table
    restaurants = get_restaurants(db)

    # Convert the data to a pandas DataFrame
    restaurant_data = [{"id": r.id, "name": r.name, "description": r.description, "image": r.image} for r in restaurants]
    restaurant_df = pd.DataFrame(restaurant_data)

    # Print the Restaurants DataFrame
    print("Restaurants:")
    print(restaurant_df)

    # Fetch data from the foods table
    foods = get_foods(db)

    # Convert the data to a pandas DataFrame
    food_data = [{"id": f.id, "restaurant_id": f.restaurant_id, "name": f.name, "description": f.description, "image": f.image, "price": f.price} for f in foods]
    food_df = pd.DataFrame(food_data)

    # Print the Foods DataFrame
    print("\nFoods:")
    print(food_df)
    breakpoint()

if __name__ == "__main__":
    main()

2.9. `qdrant_restaurants.py`
- Code trong file `qdrant_restaurants.py`:
# Standard library imports
import os
import uuid
import logging
from typing import List, Dict, Any

# Third-party imports
from dotenv import load_dotenv
import pandas as pd
from sqlalchemy.orm import Session

# LangChain imports
from langchain_community.vectorstores import Qdrant
from langchain.embeddings import HuggingFaceInferenceAPIEmbeddings
from langchain.text_splitter import CharacterTextSplitter
from langchain_groq import ChatGroq
from langchain.schema import Document
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain

# Qdrant imports
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams

# Local imports
try:
    from data_utils import get_db, get_restaurants, get_foods
    from data_models import Restaurant, Foods
    from database import SessionLocal
except:
    from .data_utils import get_db, get_restaurants, get_foods
    from .data_models import Restaurant, Foods
    from .database import SessionLocal

# Load environment variables
load_dotenv()

# Qdrant configuration
QDRANT_URL = (
    "https://9de09ba7-f0fa-4003-9310-18b9f09eeba4.us-east4-0.gcp.cloud.qdrant.io:6333"
)
QDRANT_API_KEY = "4G1TjP9sUW3ZnzuWQhy436xtCYfyVDSatUy9r4OzQDXQocDcP_dgtg"
COLLECTION_NAME = "auto-food-order"


class QdrantService:
    def __init__(self):
        # Initialize Qdrant client
        self.client = QdrantClient(url=QDRANT_URL, api_key=QDRANT_API_KEY)
        self.collection_name = COLLECTION_NAME
        self.embeddings = HuggingFaceInferenceAPIEmbeddings(
            model_name="intfloat/multilingual-e5-small",
            api_key=os.getenv("HUGGINGFACE_API_KEY"),
        )

    def setup_collection(self):
        """Setup Qdrant collection"""
        self.client.recreate_collection(
            collection_name=self.collection_name,
            vectors_config=VectorParams(size=384, distance=Distance.COSINE),
        )
        return self.client.get_collection(collection_name=self.collection_name)

    def process_restaurants(self) -> pd.DataFrame:
        """Process restaurant data for vector search"""
        db = SessionLocal()
        try:
            # Get data from database
            restaurants = get_restaurants(db)
            foods = get_foods(db)

            # Create DataFrames
            restaurant_df = pd.DataFrame(
                [
                    {
                        "id": r.id,
                        "name": r.name,
                        "description": r.description,
                    }
                    for r in restaurants
                ]
            )

            food_df = pd.DataFrame(
                [
                    {
                        "id": f.id,
                        "restaurant_id": f.restaurant_id,
                        "name": f.name,
                        "description": f.description,
                        "price": f.price,
                    }
                    for f in foods
                ]
            )

            # Process and merge data
            food_df = food_df.rename(
                columns={
                    "id": "food_id",
                    "restaurant_id": "id",
                    "name": "food_name",
                    "description": "food_description",
                }
            )

            df = pd.merge(restaurant_df, food_df, on="id")
            df["food_text"] = "\n-" + df["food_name"] + "\n" + df["food_description"]

            # Group by restaurant
            df = (
                df.groupby("id")
                .agg({"name": "first", "description": "first", "food_text": "sum"})
                .reset_index()
            )

            # Create final text
            df["text"] = (
                "```"
                + df["name"]
                + "\nRestaurant description: "
                + df["description"]
                + "\nFood available:"
                + df["food_text"]
                + "\n```"
            )

            return df
        finally:
            db.close()

    def create_embeddings(self, df: pd.DataFrame) -> List[Dict]:
        """Create embeddings for restaurant data"""
        text_splitter = CharacterTextSplitter(
            separator="\n", chunk_size=1000, chunk_overlap=200, length_function=len
        )

        points = []
        for _, row in df.iterrows():
            chunks = text_splitter.split_text(row["text"])
            for chunk in chunks:
                embedding = self.embeddings.embed_documents([chunk])[0]
                points.append(
                    {
                        "id": str(uuid.uuid4()),
                        "vector": embedding,
                        "payload": {
                            "text": chunk,
                            "restaurant_id": row["id"],
                            "restaurant_name": row["name"],
                            "restaurant_description": row["description"],
                            "restaurant_menu": row["food_text"],
                        },
                    }
                )
        return points

    def insert_vectors(self, points: List[Dict]):
        """Insert vectors into Qdrant"""
        return self.client.upsert(
            collection_name=self.collection_name, wait=True, points=points
        )

    def create_answer(self, query: str) -> str:
        """Generate answer for user query"""
        # Get embeddings and search
        query_embedding = self.embeddings.embed_documents([query])[0]
        search_results = self.client.search(
            collection_name=self.collection_name, query_vector=query_embedding, limit=1
        )

        # Create context from search results
        context = "\n---\n".join([result.payload["text"] for result in search_results])

        # Setup LLM chain
        prompt = PromptTemplate(
            input_variables=["context", "question"],
            template="""
            You are an assistant helping users with their food orders based on the following context.

            Context:
            {context}

            Question: {question}

            Answer:
            """,
        )

        llm = ChatGroq(
            temperature=0.3,
            model="llama-3.1-70b-versatile",
            api_key=os.getenv("GROQ_API_KEY"),
        )

        chain = LLMChain(llm=llm, prompt=prompt)
        return chain.run(context=context, question=query)


def main():
    # Initialize service
    qdrant_service = QdrantService()

    try:
        # Setup collection
        print("Setting up Qdrant collection...")
        qdrant_service.setup_collection()

        # Process data and create embeddings
        print("Processing restaurant data...")
        df = qdrant_service.process_restaurants()

        print("Creating embeddings...")
        points = qdrant_service.create_embeddings(df)

        print("Inserting vectors...")
        qdrant_service.insert_vectors(points)

        # Test query
        query = "testing, I would like to order a large pizza with mango, thai food, taco fiesta"
        print("\nTesting query:", query)
        answer = qdrant_service.create_answer(query)
        print("\nResponse:", answer)

    except Exception as e:
        logging.error(f"Error occurred: {str(e)}")
        raise


if __name__ == "__main__":
    main()

### 3. Tóm tắt
Folder `data` chứa các file quan trọng để quản lý cơ sở dữ liệu của ứng dụng, bao gồm việc định nghĩa schema, thêm dữ liệu, và truy xuất dữ liệu. Các file này giúp tổ chức và quản lý dữ liệu một cách hiệu quả, hỗ trợ cho các chức năng khác của ứng dụng. Nếu bạn cần thêm thông tin chi tiết về bất kỳ file nào, hãy cho tôi biết nhé! 😊

-   **`backend/src/endpoints`:** 


Chắc chắn rồi! Dưới đây là mô tả chi tiết cấu trúc và nội dung của các file trong folder `endpoints` của project này 📁

### 1. Cấu trúc thư mục `endpoints`

```
backend/src/endpoints/
├── __init__.py
├── README.md
├── router.py
└── routers.py
```


### 2. Chi tiết từng file

#### 2.1. `__init__.py`
- **Mục đích**: File này cho phép thư mục `endpoints` được coi như một module Python.
- **Nội dung**: 
  - Import các router từ `routers` và `health_check`.
  - Định nghĩa hàm `include_all_routers(app: FastAPI, handler, CONFIG)` để thêm tất cả các router vào ứng dụng FastAPI.

#### 2.2. `README.md`
- **Mục đích**: Cung cấp thông tin về các API routes của ứng dụng.
- **Nội dung**:
  - Mô tả mục đích của file `routers.py`, bao gồm các route cho việc gửi tin nhắn, gọi hàm, chuyển đổi âm thanh thành văn bản, tạo âm thanh từ văn bản, và truy xuất dữ liệu từ cơ sở dữ liệu.
  - Liệt kê các endpoint và mô tả chức năng của từng endpoint.

#### 2.3. `router.py`
- **Mục đích**: Định nghĩa các route cơ bản cho ứng dụng.
- **Nội dung**:
  - Tạo một router FastAPI và định nghĩa route gốc (`/`) để trả về thông điệp chào mừng.
- Code trong file `router.py`:
from fastapi import APIRouter

router = APIRouter()


@router.get("/")
async def root():
    return {"message": "Welcome to Food Order API"}

#### 2.4. `routers.py`
- **Mục đích**: Chứa các route API chính cho ứng dụng.
- **Nội dung**:
  - Định nghĩa hàm `create_router(handler: MainHandler, CONFIG: Dict)`, tạo và cấu hình router API.
  - Các endpoint được định nghĩa bao gồm:
    - `POST /chat/send_message`: Nhận tin nhắn từ người dùng và trả về phản hồi.
    - `POST /chat/function_call`: Thực hiện các cuộc gọi hàm từ frontend.
    - `POST /chat/transcribe`: Chuyển đổi âm thanh thành văn bản.
    - `POST /chat/tts`: Tạo âm thanh từ văn bản.
    - `GET /restaurants/`: Truy xuất tất cả các nhà hàng từ cơ sở dữ liệu.
    - `GET /restaurants/{restaurant_id}/foods/`: Truy xuất tất cả các món ăn từ một nhà hàng cụ thể.
- Code trong file `routers.py`:
"""File containing root routes"""

from fastapi.routing import APIRouter
from fastapi import Depends, HTTPException, File, UploadFile
from fastapi.encoders import jsonable_encoder
from fastapi.staticfiles import StaticFiles
import base64
import json
import tempfile
import logging
from pathlib import Path
from typing import Dict, List, Any

# Schemas
from src.schemas import (
    ChatRequest,
    FunctionCall,
    AudioTranscriptRequest,
    AudioTTSRequest,
)
from src.handlers import MainHandler
from src.data.data_models import Restaurant, Foods

# Services
from src.services import groq_service, functions

# Data
from sqlalchemy.orm import Session
from src.data.data_utils import get_db


def create_router(handler: MainHandler, CONFIG: Dict) -> APIRouter:
    """Create and configure the API router

    Args:
        handler: Main application handler
        CONFIG: Application configuration

    Returns:
        Configured APIRouter instance
    """
    router = APIRouter()
    client = handler.groq_client

    @router.post("/chat/send_message")
    async def send_message(prompt_request: ChatRequest) -> Dict:
        """Handle chat messages and generate responses"""
        prompt_handler = handler.prompt_handler
        messages = prompt_handler.get_messages(prompt_request)

        functions = []
        if prompt_request.function_call:
            functions = prompt_handler.get_functions()

        try:
            prompt_response = await groq_service.chat_completion(
                messages=messages, CONFIG=CONFIG, functions=functions, client=client
            )
            response = prompt_handler.prepare_response(prompt_response)
        except Exception as e:
            logging.error(f"Chat error: {str(e)}")
            response = {
                "response": "Oops there was an error, please try again",
                "function_call": None,
            }
        return response

    @router.post("/chat/function_call")
    async def function_call(function_call: FunctionCall) -> Dict:
        """Execute function calls from frontend"""
        function_call_properties = jsonable_encoder(function_call)
        function_name = function_call_properties["name"]
        function_arguments = json.loads(function_call_properties["arguments"])

        available_functions = {
            "get_restaurant_pages": lambda kwargs: functions.find_restaurant_pages(
                CONFIG=CONFIG, **kwargs
            ),
            "open_restaurant_page": lambda kwargs: functions.open_restaurant_page(
                CONFIG=CONFIG, **kwargs
            ),
            "close_restaurant_page": lambda _: functions.dummy_function(),
            "get_user_actions": lambda _: functions.dummy_function(),
            "get_menu_of_restaurant": lambda kwargs: functions.get_menu_of_restaurant(
                CONFIG=CONFIG, **kwargs
            ),
            "add_food_to_cart": lambda kwargs: functions.add_food_to_cart(
                CONFIG=CONFIG, **kwargs
            ),
            "remove_food_from_cart": lambda kwargs: functions.remove_food_from_cart(
                CONFIG=CONFIG, **kwargs
            ),
            "open_shopping_cart": lambda _: functions.dummy_function(),
            "close_shopping_cart": lambda _: functions.dummy_function(),
            "place_order": lambda _: functions.dummy_function(),
            "activate_handsfree": lambda _: functions.dummy_function(),
        }

        function_response = await available_functions[function_name](function_arguments)
        return {"response": function_response}

    @router.post("/chat/transcribe")
    async def generate_transcription(audio_req: AudioTranscriptRequest) -> Dict:
        """Transcribe audio to text"""
        audio_handler = handler.audio_handler
        audio_segment, _ = audio_handler.extract_audio_segment(audio_req.audio)

        with tempfile.NamedTemporaryFile(suffix=".mp3", delete=True) as tmp_file:
            audio_segment.export(tmp_file.name, format="mp3")
            speech_filepath = Path(tmp_file.name)
            transcripted_response = await handler.whisper_service(
                audio_file=open(speech_filepath, "rb"), CONFIG=CONFIG
            )
        return {"response": transcripted_response}

    @router.post("/chat/tts")
    async def generate_tts(tts_req: AudioTTSRequest) -> Dict:
        """Generate text-to-speech audio"""
        audio = await groq_service.tts(text=tts_req.text, CONFIG=CONFIG, client=client)
        return {"response": audio}

    @router.get("/restaurants/")
    def get_restaurants(db: Session = Depends(get_db)) -> List[Dict]:
        """Get all restaurants with static image paths"""
        restaurants = db.query(Restaurant).all()
        return [
            {
                "id": r.id,
                "name": r.name,
                "description": r.description,
                "image": f"/static/{r.image}" if r.image else None,
            }
            for r in restaurants
        ]

    @router.get("/restaurants/{restaurant_id}/foods/")
    def get_foods_from_restaurant(
        restaurant_id: int, db: Session = Depends(get_db)
    ) -> List[Dict]:
        """Get foods for a specific restaurant with static image paths"""
        restaurant = db.query(Restaurant).filter(Restaurant.id == restaurant_id).first()
        if not restaurant:
            raise HTTPException(status_code=404, detail="Restaurant not found")

        foods = db.query(Foods).filter(Foods.restaurant_id == restaurant_id).all()
        return [
            {
                "id": f.id,
                "name": f.name,
                "description": f.description,
                "image": f"/static/{f.image}" if f.image else None,
                "price": f.price,
            }
            for f in foods
        ]

    return router

### 3. Tóm tắt
Folder `endpoints` chứa các file quan trọng để định nghĩa và quản lý các API routes của ứng dụng. Các file này giúp tổ chức và xử lý các yêu cầu từ frontend, tương tác với các dịch vụ và trả về dữ liệu cho người dùng. Nếu bạn cần thêm thông tin chi tiết về bất kỳ file nào, hãy cho tôi biết nhé! 😊

- **`backend/src/handlers`:**


Dưới đây là mô tả chi tiết cấu trúc và nội dung của các file trong folder `handlers` của project này 📁

### 1. Cấu trúc thư mục `handlers`

```
backend/src/handlers/
├── __init__.py
├── README.md
├── audio_handler.py
├── main_handler.py
├── prompt_handler.py
└── vectordb_handler.py
```

Dưới đây là mô tả chi tiết cấu trúc của thư mục `@handlers` trong dự án của bạn, bao gồm các file và chức năng của chúng:

### Cấu trúc thư mục `@handlers`

1. **`__init__.py`**
   - **Chức năng**: File này cho phép Python nhận diện thư mục `handlers` như một package. Nó import `MainHandler` để có thể sử dụng trong các module khác.

2. **`main_handler.py`**
   - **Chức năng**: 
   - **`main_handler.py`**: Gọi các phương thức từ `audio_handler.py` để xử lý yêu cầu âm thanh.
     - Centralizes all the other handlers and makes them available for use in routers.
     - Xử lý các yêu cầu chat và audio, bao gồm việc gọi các hàm từ các handler khác như `PromptHandler` và `AudioHandler`.
- Code của file `main_handler.py`:
from typing import Dict, Any, Optional
from fastapi import HTTPException

from .prompt_handler import PromptHandler
from .audio_handler import AudioHandler
from .vector_handler import VectorHandler

class MainHandler:
    def __init__(self):
        self.prompt_handler = PromptHandler()
        self.audio_handler = AudioHandler()
        self.vector_handler = VectorHandler()

    async def process_chat_request(self, request: Dict[str, Any]) -> Dict[str, Any]:
        """Process chat request and generate response"""
        try:
            # Get chat history and query
            chat_history = request.get("query", {}).get("history", [])
            
            # Generate response using Groq
            response = await self.prompt_handler.generate_response(chat_history)
            
            return {"response": response}
            
        except Exception as e:
            raise HTTPException(status_code=500, detail=str(e))

    async def process_audio_request(self, audio_file: bytes) -> str:
        """Process audio request and convert to text"""
        try:
            return await self.audio_handler.speech_to_text(audio_file)
        except Exception as e:
            raise HTTPException(status_code=500, detail=str(e))
### Phân tích main_handler.py
- process_audio_request: Gọi phương thức từ audio_handler để xử lý yêu cầu âm thanh.

3. **`prompt_handler.py`**
   - **Chức năng**: 
     - Quản lý các prompt và tổ chức chúng trước khi gọi API.
     - Tạo và định dạng các prompt cho việc tương tác với mô hình ngôn ngữ (LLM) và xử lý phản hồi từ mô hình.
- Code của file `prompt_handler.py`:

from typing import List, Dict, Any
from langchain_groq import ChatGroq
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain

from ..config import settings
from src.prompts.functions import ALL_ALLOWED_FUNCTIONS

class PromptHandler:
    def __init__(self):
        self.llm = ChatGroq(
            temperature=0.3,
            model="llama-3.1-70b-versatile",
            api_key=settings.GROQ_API_KEY
        )

    async def generate_response(self, chat_history: List[Dict[str, Any]]) -> Dict[str, Any]:
        """Generate response using Groq"""
        # Create system prompt
        system_prompt = """You are an assistant helping users with their food orders.
        Be friendly and helpful while assisting with the order process."""
        
        # Create conversation prompt
        conversation = "\n".join([
            f"{msg['role']}: {msg['content']}" 
            for msg in chat_history
        ])

        prompt = PromptTemplate(
            input_variables=["system", "conversation"],
            template="{system}\n\nConversation:\n{conversation}\n\nAssistant:"
        )

        chain = LLMChain(llm=self.llm, prompt=prompt)
        response = chain.run(system=system_prompt, conversation=conversation)

        return self.prepare_response(response)

    def prepare_response(self, response) -> Dict[str, Any]:
        """Formats the response from the system"""
        response = jsonable_encoder(response)
        return {
            "response": response["choices"][0]["message"]["content"],
            "function_call": response["choices"][0]["message"].get("function_call", None),
        }

    def get_functions(self) -> List[Dict[str, Any]]:
        """Returns the functions signatures"""
        return ALL_ALLOWED_FUNCTIONS

4. **`audio_handler.py`**
- **`audio_handler.py`**: Chứa các phương thức để xử lý âm thanh và phiên âm.
   - **Chức năng**: 
     - Xử lý âm thanh và phiên âm từ âm thanh sang văn bản.
     - Chuyển đổi âm thanh từ định dạng base64 và sử dụng mô hình Whisper để phiên âm.
- Code của file `audio_handler.py`:

from typing import Optional, Tuple, Any
import base64
import io
import logging
from pathlib import Path
from tenacity import retry, wait_random, stop_after_attempt

# Audio processing
from pydub import AudioSegment
from io import BytesIO
import soundfile as sf
import numpy as np

# LangChain imports
from langchain_groq import ChatGroq
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain

class AudioHandler:
    """Handler for audio processing and transcription"""
    
    def __init__(self, config: dict):
        """Initialize AudioHandler with configuration
        
        Args:
            config: Application configuration dictionary
        """
        self.config = config
        self.groq_api_key = config["groq"]["api_key"]
        self.model_name = "whisper-large-v3"
        self.llm_model = "llama-3.1-70b-versatile"

    def extract_audio_segment(self, audio_file: str) -> Tuple[AudioSegment, bytes]:
        """Extract audio segment from base64 encoded audio file
        
        Args:
            audio_file: Base64 encoded audio string
            
        Returns:
            Tuple containing AudioSegment and raw audio bytes
        """
        try:
            audio_data = base64.b64decode(audio_file)
            audio_segment = AudioSegment.from_file(BytesIO(audio_data))
            return audio_segment, audio_data
        except Exception as e:
            logging.error(f"Error extracting audio: {str(e)}")
            raise

    @retry(wait=wait_random(min=1, max=5), stop=stop_after_attempt(5))
    async def transcribe_audio(self, audio_file: Any) -> str:
        """Transcribe audio file using Groq Whisper
        
        Args:
            audio_file: Audio file object
            
        Returns:
            Transcribed text
        """
        try:
            # Read and prepare audio data
            audio_data = audio_file.read()
            buffer = io.BytesIO(audio_data)
            buffer.seek(0)
            
            # Convert audio to required format
            audio_array, sample_rate = sf.read(buffer)
            wav_buffer = io.BytesIO()
            sf.write(wav_buffer, audio_array, sample_rate, format='wav')
            wav_buffer.seek(0)
            
            # Initialize Groq client
            from groq import Client
            client = Client(api_key=self.groq_api_key)
            
            # Transcribe audio
            completion = client.audio.transcriptions.create(
                model=self.model_name,
                file=("audio.wav", wav_buffer),
                response_format="text"
            )
            
            return completion

        except Exception as e:
            logging.error(f"Transcription error: {str(e)}")
            raise

    async def generate_response(self, transcription: str) -> str:
        """Generate response using Groq LLM
        
        Args:
            transcription: Transcribed text to respond to
            
        Returns:
            Generated response text
        """
        try:
            if not transcription:
                return "No transcription available. Please try speaking again."

            # Initialize LLM
            llm = ChatGroq(
                temperature=0.3,
                model=self.llm_model,
                api_key=self.groq_api_key
            )
            
            # Setup prompt
            prompt = PromptTemplate(
                input_variables=["transcription"],
                template="""
                You are a helpful assistant processing voice commands.
                
                User said: {transcription}
                
                Please provide a clear and concise response:
                """
            )
            
            # Create and run chain
            chain = LLMChain(llm=llm, prompt=prompt)
            response = chain.run(transcription=transcription)
            
            return response

        except Exception as e:
            logging.error(f"Response generation error: {str(e)}")
            raise
### Phân tích audio_handler.py

- Khởi tạo: Nhận cấu hình và API key từ Groq.
- extract_audio_segment: Trích xuất đoạn âm thanh từ chuỗi base64.
- transcribe_audio: 
  - Đọc dữ liệu âm thanh và chuyển đổi sang định dạng WAV.
  - Sử dụng API Groq để phiên âm âm thanh.
- generate_response: 
  - Nhận phiên âm và tạo phản hồi bằng cách sử dụng LLM từ Groq.

5. **`vector_handler.py`**
   - **Chức năng**: 
     - Quản lý các thao tác liên quan đến tìm kiếm vector trong cơ sở dữ liệu Qdrant.
     - Tạo embedding cho các truy vấn và tìm kiếm các nhà hàng dựa trên embedding đó.
- Code của file `vector_handler.py`:

from typing import List, Dict, Any
from qdrant_client import QdrantClient
from langchain.embeddings import HuggingFaceInferenceAPIEmbeddings
from langchain_groq import ChatGroq
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate

from ..config import settings

class VectorHandler:
    def __init__(self):
        self.client = QdrantClient(
            url=settings.QDRANT_URL,
            api_key=settings.QDRANT_API_KEY
        )
        self.embeddings = HuggingFaceInferenceAPIEmbeddings(
            model_name="intfloat/multilingual-e5-small",
            api_key=settings.HUGGINGFACE_API_KEY
        )
        self.llm = ChatGroq(
            temperature=0.3,
            model="llama-3.1-70b-versatile",
            api_key=settings.GROQ_API_KEY
        )

    async def search_restaurants(self, query: str) -> str:
        """Search restaurants and generate response"""
        # Create embedding for query
        query_embedding = self.embeddings.embed_documents([query])[0]
        
        # Search in Qdrant
        results = self.client.search(
            collection_name=settings.COLLECTION_NAME,
            query_vector=query_embedding,
            limit=3
        )

        # Create context from results
        context = "\n---\n".join([result.payload['text'] for result in results])

        # Generate response using LangChain
        prompt = PromptTemplate(
            input_variables=["context", "question"],
            template="""
            You are an assistant helping users with their food orders based on the following context.
            Context: {context}
            Question: {question}
            Answer:
            """
        )

        chain = LLMChain(llm=self.llm, prompt=prompt)
        return chain.run(context=context, question=query)

6. **`README.md`**
   - **Chức năng**: 
     - Cung cấp thông tin mô tả về các handler, chức năng của từng handler và cách chúng tương tác với nhau.

### Tóm tắt
Thư mục `@handlers` chứa các file xử lý khác nhau cho các chức năng của ứng dụng, bao gồm xử lý âm thanh, quản lý prompt, và tìm kiếm vector. Mỗi file có một vai trò cụ thể trong việc tổ chức và xử lý dữ liệu, giúp cho mã nguồn trở nên rõ ràng và dễ bảo trì hơn. 

- **`backend/src/prompts`:** 

Dưới đây là mô tả chi tiết cấu trúc của thư mục `@prompts` trong dự án của bạn, bao gồm các file và chức năng của chúng:

### Cấu trúc thư mục `@prompts`

1. **`README.md`**
   - **Chức năng**: 
     - Cung cấp thông tin mô tả về cách sử dụng các file trong thư mục `prompts`.
     - Hướng dẫn người dùng cách thay đổi tính cách của chatbot thông qua file `system.txt` và cách cấu hình các hàm trong `config.yaml`.
     - Hướng dẫn cách tạo hàm mới và tích hợp chúng vào hệ thống.
- Nội dung trong file `README.md`:
# Prompts

## Chat

By editing [system.txt](./chat/system.txt) you can change the chatbot personality

## Functions

In (config.yaml)[/functions/config.yaml] you can edit the functions prompts:

```
functions:
 name: open_restaurant_page
    description: This function opens the page of a given restaurant, it can only open one restaurant at a time. Always confirm with the user that you are going to search and open a restaurant page with the information provided before calling the function. This function can only be called after the function get_restaurant_pages.
    parameters:
      type: object
      properties:
        restaurant_uuid:
          type: string
          description: The number of the restaurant page that comes in the function get_restaurant_pages previously called
    required: [restaurant_uuid]
    allow: true
```

This will recreate a [signatures.json](functions/signatures.json) file (that should not be touched). You can activate and deactivate a function using the `allow` flag. This will make the function invisible to the [chat_completion](../services/openai_service/chat_completion.py) api call


You can play with creating new functions by following the steps:

- Create a function in the (config.yaml)[/functions/config.yaml]
- Create a new function service like this [mock/dummy function](../services/functions/dummy_function.py)
- Import it in the [__init__.py](../services/functions/__init__.py)
- Add the function to the [routers.py](../endpoints/routers.py#72) in the `function_call` endpoint
```
available_function = {
    ... # Existent functions
    "new_function": lambda _: functions.new_function
}
```
- Edit the frontend [AppContainer.vue](../../../frontend/src/components/AppContainer.vue)
    - Add new method to handle the function they way you would like (e.g. [handleOpenRestaurant](../../../frontend/src/components/AppContainer.vue#379))
    - Add it to the [handleFunctionCall](../../../frontend/src/components/AppContainer.vue#314)

2. **`chat/system.txt`**
   - **Chức năng**: 
     - Chứa các quy tắc và ngữ cảnh cho chatbot, bao gồm cách thức trả lời, phong cách giao tiếp và các chức năng mà chatbot có thể thực hiện.
     - Định nghĩa tính cách của chatbot, giúp chatbot trở nên thân thiện và dễ tiếp cận với người dùng.
- Nội dung trong file `system.txt`:
[Style]
Whenever you are listing things, use HTML tags (ul, ol, br) to make it easier to read
Always break the line with <br/> before starting a new list or showing options 
You are organized and concise in your answers.
This is ***VERY*** important: give organized answers using HTML tags

[Context]
You are a chatbot for AutoFood, the leading food delivery platform in North America
Your primary goal is to assist customers in finding and ordering their favorite meals from a wide variety of restaurants with ease and convenience
You are knowledgeable about the latest deals, promotions, and restaurant offerings, ensuring that users have access to the most up-to-date information

As a delivery app chatbot, you are friendly, approachable, and always eager to help.
You understand that hunger can sometimes make people impatient, so you strive to provide quick and efficient service with a touch of humor to lighten the mood
Your functionalities include helping users browse through restaurant menus, suggesting popular dishes based on their preferences, guiding them through the ordering process, and providing real-time updates on their order status

You are aware of the interface you are in
Every time the user or you do something like opening a restaurant page, closes a restaurant page, adding or removing something from the shopping cart, you will receive a message starting with "@action:"
You should only use these messages to be aware of what is going on in the web interface you are

You are capable of calling functions from the web interface, but you are careful and always clarify the informations you need with the user before doing so
You do not mention the functions you have access to the user
Be autonomous to fulfill the user's needs, take the actions necessary when they were asked by the user
After a sequence of actions you always answer the user with the best information

You are also capable of handling customer complaints and resolving issues related to orders, payments, and deliveries
Your personality is a perfect blend of professionalism and playfulness, making you an enjoyable and reliable companion for users seeking a satisfying meal delivered right to their doorstep
You are concise with your answers. You do not answer with more than 70 words.

3. **`functions/config.yaml`**
   - **Chức năng**: 
     - Chứa cấu hình cho các hàm mà chatbot có thể gọi.
     - Mỗi hàm được định nghĩa với tên, mô tả, tham số và trạng thái cho phép (allow).
     - Cung cấp thông tin chi tiết về cách thức hoạt động của từng hàm, giúp chatbot thực hiện các tác vụ liên quan đến nhà hàng và đơn hàng.
- Nội dung trong file `config.yaml`:
functions:
  - name: get_restaurant_pages
    description: This function finds a restaurant that has similar food or name to what the user is asking for. Not all the restaurants found are necessarily the best option for the user, use the description of the restaurant as a proxy to suggest it to them.
    parameters:
      type: object
      properties:
        name_of_restaurant:
          type: string
          description: Not mandatory. The name of the restaurant. In case the user didn't specify it in the request, it should be set to null.
        type_of_restaurant:
          type: string
          description: The type of food or restaurant that the user requested. E.g. Italian, Pizza, Sushi, Indian, Vegan, etc...
        food_requested:
          type: array
          description: Not mandatory. The array of foods that the user requested. E.g. Pizza, Pasta, etc... . In case the user didn't specify it in the request, it should be set to [null].
          items:
            type: string
        quantity:
          type: integer
          description: Not mandatory. The amount of restaurants you judge its preferable to search in order to serve the user. It should be default to 1, however if the user request is broad you should set 2,3,4 or even 5
        other_information:
          type: string
          description: All other information that the user provided that could potentially help finding the best matching restaurant for them. In case there aren't any information, set it to null
    required: [name_of_restaurant, type_of_restaurant, food_requested, other_information, quantity]
    allow: true

  - name: open_restaurant_page
    description: This function opens the page of a given restaurant, it can only open one restaurant at a time. Always confirm with the user that you are going to search and open a restaurant page with the information provided before calling the function. This function can only be called after the function get_restaurant_pages.
    parameters:
      type: object
      properties:
        restaurant_uuid:
          type: string
          description: The number of the restaurant page that comes in the function get_restaurant_pages previously called
    required: [restaurant_uuid]
    allow: true

  - name: close_restaurant_page
    description: This function closes the page of a given restaurant and goes back to the main page (menu with all the restaurants) x. This function can be called if a restaurant page is already open, you can check the recent actions with get_user_actions to verify it, before calling the function
    parameters:
      type: object
      properties: {}
    allow: true

  - name: get_user_actions
    description: This function returns the list of the latest 10 user actions. This function should be called when you need to collect more information about what were the latest actions the user of the chatbot interface, like what is the current opened restaurant, what is the current state of the shopping cart, etc... Very often it is best to call this function before calling some other functions
    parameters:
      type: object
      properties: {}
    allow: true

  - name: get_menu_of_restaurant
    description: This function returns the list of the menu of a given restaurant. This function should be called when you need to collect more information about what is the menu of a given restaurant. Very often it is best to get the recent actions before calling this function, so you can know what is the restaurant uuid that you need to pass to this function. Always after calling this function create a short-list of suggestions based on the chat history with the user, displaying name, description and price of the given food
    parameters:
      type: object
      properties:
        restaurant_uuid:
          type: string
          description: The id of the restaurant page that comes in the function get_restaurant_pages previously called, or in the get_user_actions function if the user opened a restaurant page
    required: [restaurant_uuid]
    allow: true

  - name: add_food_to_cart
    description: This function adds food to the user's shopping cart. It should only be called if you have the information of the restaurant_id, food_id and amount (quantity) of the food the user is referring to. You can get more information of the restaurant_id opened in the users interface by calling the get_user_actions and you can get the food_id by calling the get_menu_of_restaurant function. You can call this function multiple times in a row if there are several food items to be added
    parameters:
      type: object
      properties:
        restaurant_uuid:
          type: string
          description: The id of the restaurant page that comes in the function get_restaurant_pages previously called, or in the get_user_actions function if the user opened a restaurant page
        food_id:
          type: string
          description: The id of the food that comes in the function get_menu_of_restaurant previously called
        quantity:
          type: integer
          description: The quantity of the food that the user wants to add to the cart
    required: [restaurant_uuid, food_id, quantity]
    allow: true

  - name: remove_food_from_cart
    description: This function removes food from the user's shopping cart. It should only be called if you have the information of the restaurant_id and food_id of the food the user is referring to. You can get more information of the restaurant_id opened in the users interface by calling the get_user_actions and you can get the food_id by calling the get_menu_of_restaurant function. You can call this function multiple times in a row if there are several food items to be removed
    parameters:
      type: object
      properties:
        restaurant_uuid:
          type: string
          description: The id of the restaurant page that comes in the function get_restaurant_pages previously called, or in the get_user_actions function if the user opened a restaurant page. Make sure you know all the parameters before calling this function. Call auxiliary functions before calling it, if necessary.
        food_id:
          type: string
          description: The id of the food that comes in the function get_menu_of_restaurant previously called
    required: [restaurant_uuid, food_id]
    allow: true

  - name: open_shopping_cart
    description: This function opens the shopping cart of the user. It should be called if the user wants it, or if you need more information about the items in the shopping cart
    parameters:
      type: object
      properties: {}
    allow: true

  - name: close_shopping_cart
    description: This function closes the shopping cart of the user. It should be called if the user wants it, or after placing an order
    parameters:
      type: object
      properties: {}
    allow: true

  - name: place_order
    description: This function places an order with the food items that the user has in the shopping cart. It should be called if the user specifies or confirms that they want to place an order
    parameters:
      type: object
      properties: {}
    allow: true
  
  - name: activate_handsfree
    description: This function activates a handsfree chat experience where you are always answering by voice to the user interface
    parameters:
      type: object
      properties: {}
    allow: true

4. **`functions/signatures.json`**
   - **Chức năng**: 
     - Lưu trữ các chữ ký (signatures) của các hàm đã được cấu hình trong `config.yaml`.
     - Được tạo ra tự động từ file `config.yaml` và không nên bị chỉnh sửa trực tiếp.
     - Cung cấp thông tin cho hệ thống về các hàm có sẵn và cách thức gọi chúng.
- Nội dung trong file `signatures.json`:
[
    {
        "name": "get_restaurant_pages",
        "description": "This function finds a restaurant that has similar food or name to what the user is asking for. Not all the restaurants found are necessarily the best option for the user, use the description of the restaurant as a proxy to suggest it to them.",
        "parameters": {
            "type": "object",
            "properties": {
                "name_of_restaurant": {
                    "type": "string",
                    "description": "Not mandatory. The name of the restaurant. In case the user didn't specify it in the request, it should be set to null."
                },
                "type_of_restaurant": {
                    "type": "string",
                    "description": "The type of food or restaurant that the user requested. E.g. Italian, Pizza, Sushi, Indian, Vegan, etc..."
                },
                "food_requested": {
                    "type": "array",
                    "description": "Not mandatory. The array of foods that the user requested. E.g. Pizza, Pasta, etc... . In case the user didn't specify it in the request, it should be set to [null].",
                    "items": {
                        "type": "string"
                    }
                },
                "quantity": {
                    "type": "integer",
                    "description": "Not mandatory. The amount of restaurants you judge its preferable to search in order to serve the user. It should be default to 1, however if the user request is broad you should set 2,3,4 or even 5"
                },
                "other_information": {
                    "type": "string",
                    "description": "All other information that the user provided that could potentially help finding the best matching restaurant for them. In case there aren't any information, set it to null"
                }
            }
        },
        "required": [
            "name_of_restaurant",
            "type_of_restaurant",
            "food_requested",
            "other_information",
            "quantity"
        ]
    },
    {
        "name": "open_restaurant_page",
        "description": "This function opens the page of a given restaurant, it can only open one restaurant at a time. Always confirm with the user that you are going to search and open a restaurant page with the information provided before calling the function. This function can only be called after the function get_restaurant_pages.",
        "parameters": {
            "type": "object",
            "properties": {
                "restaurant_uuid": {
                    "type": "string",
                    "description": "The number of the restaurant page that comes in the function get_restaurant_pages previously called"
                }
            }
        },
        "required": [
            "restaurant_uuid"
        ]
    },
    {
        "name": "close_restaurant_page",
        "description": "This function closes the page of a given restaurant and goes back to the main page (menu with all the restaurants) x. This function can be called if a restaurant page is already open, you can check the recent actions with get_user_actions to verify it, before calling the function",
        "parameters": {
            "type": "object",
            "properties": {}
        }
    },
    {
        "name": "get_user_actions",
        "description": "This function returns the list of the latest 10 user actions. This function should be called when you need to collect more information about what were the latest actions the user of the chatbot interface, like what is the current opened restaurant, what is the current state of the shopping cart, etc... Very often it is best to call this function before calling some other functions",
        "parameters": {
            "type": "object",
            "properties": {}
        }
    },
    {
        "name": "get_menu_of_restaurant",
        "description": "This function returns the list of the menu of a given restaurant. This function should be called when you need to collect more information about what is the menu of a given restaurant. Very often it is best to get the recent actions before calling this function, so you can know what is the restaurant uuid that you need to pass to this function. Always after calling this function create a short-list of suggestions based on the chat history with the user, displaying name, description and price of the given food",
        "parameters": {
            "type": "object",
            "properties": {
                "restaurant_uuid": {
                    "type": "string",
                    "description": "The id of the restaurant page that comes in the function get_restaurant_pages previously called, or in the get_user_actions function if the user opened a restaurant page"
                }
            }
        },
        "required": [
            "restaurant_uuid"
        ]
    },
    {
        "name": "add_food_to_cart",
        "description": "This function adds food to the user's shopping cart. It should only be called if you have the information of the restaurant_id, food_id and amount (quantity) of the food the user is referring to. You can get more information of the restaurant_id opened in the users interface by calling the get_user_actions and you can get the food_id by calling the get_menu_of_restaurant function. You can call this function multiple times in a row if there are several food items to be added",
        "parameters": {
            "type": "object",
            "properties": {
                "restaurant_uuid": {
                    "type": "string",
                    "description": "The id of the restaurant page that comes in the function get_restaurant_pages previously called, or in the get_user_actions function if the user opened a restaurant page"
                },
                "food_id": {
                    "type": "string",
                    "description": "The id of the food that comes in the function get_menu_of_restaurant previously called"
                },
                "quantity": {
                    "type": "integer",
                    "description": "The quantity of the food that the user wants to add to the cart"
                }
            }
        },
        "required": [
            "restaurant_uuid",
            "food_id",
            "quantity"
        ]
    },
    {
        "name": "remove_food_from_cart",
        "description": "This function removes food from the user's shopping cart. It should only be called if you have the information of the restaurant_id and food_id of the food the user is referring to. You can get more information of the restaurant_id opened in the users interface by calling the get_user_actions and you can get the food_id by calling the get_menu_of_restaurant function. You can call this function multiple times in a row if there are several food items to be removed",
        "parameters": {
            "type": "object",
            "properties": {
                "restaurant_uuid": {
                    "type": "string",
                    "description": "The id of the restaurant page that comes in the function get_restaurant_pages previously called, or in the get_user_actions function if the user opened a restaurant page. Make sure you know all the parameters before calling this function. Call auxiliary functions before calling it, if necessary."
                },
                "food_id": {
                    "type": "string",
                    "description": "The id of the food that comes in the function get_menu_of_restaurant previously called"
                }
            }
        },
        "required": [
            "restaurant_uuid",
            "food_id"
        ]
    },
    {
        "name": "open_shopping_cart",
        "description": "This function opens the shopping cart of the user. It should be called if the user wants it, or if you need more information about the items in the shopping cart",
        "parameters": {
            "type": "object",
            "properties": {}
        }
    },
    {
        "name": "close_shopping_cart",
        "description": "This function closes the shopping cart of the user. It should be called if the user wants it, or after placing an order",
        "parameters": {
            "type": "object",
            "properties": {}
        }
    },
    {
        "name": "place_order",
        "description": "This function places an order with the food items that the user has in the shopping cart. It should be called if the user specifies or confirms that they want to place an order",
        "parameters": {
            "type": "object",
            "properties": {}
        }
    },
    {
        "name": "activate_handsfree",
        "description": "This function activates a handsfree chat experience where you are always answering by voice to the user interface",
        "parameters": {
            "type": "object",
            "properties": {}
        }
    }
]
5. **`functions/__init__.py`**
   - **Chức năng**: 
     - Chứa mã để đọc file `config.yaml`, lọc các hàm được phép và ghi chúng vào file `signatures.json`.
     - Đảm bảo rằng các hàm được cấu hình có thể được sử dụng trong các phần khác của ứng dụng.

### Tóm tắt
Thư mục `@prompts` chứa các file cấu hình và mô tả cho chatbot, bao gồm cách thức hoạt động của nó và các hàm mà nó có thể gọi. Mỗi file có một vai trò cụ thể trong việc định hình hành vi của chatbot và giúp nó tương tác hiệu quả với người dùng.

- **`backend/src/schemas`:**


Dưới đây là mô tả chi tiết cấu trúc và nội dung của các file trong folder `schemas` của project này 📁

### 1. Cấu trúc thư mục `schemas`

```
backend/src/schemas/
├── __init__.py
└── base_models.py
```


### 2. Chi tiết từng file

#### 2.1. `__init__.py`
- **Mục đích**: File này cho phép thư mục `schemas` được coi như một module Python.
- **Nội dung**: 
  - Import tất cả các class từ `base_models`, giúp dễ dàng truy cập các thành phần này từ module `schemas`.

#### 2.2. `base_models.py`
- **Mục đích**: Định nghĩa các schema sử dụng Pydantic cho các yêu cầu và phản hồi trong ứng dụng.
- **Nội dung**:
  - **FunctionCall**: Schema cho việc gọi hàm, bao gồm tên hàm và các tham số.
  - **Message**: Schema cho tin nhắn, bao gồm nội dung, vai trò (role), và tùy chọn gọi hàm.
  - **GroqChatCompletionResponse**: Schema cho phản hồi từ Groq, bao gồm văn bản, mô hình, thông tin sử dụng, và lý do kết thúc.
  - **RestaurantSearchQuery**: Schema cho truy vấn tìm kiếm nhà hàng, bao gồm tên nhà hàng, loại nhà hàng, món ăn yêu cầu, thông tin khác, và số lượng.
  - **RestaurantSearchResult**: Schema cho kết quả tìm kiếm nhà hàng, bao gồm ID nhà hàng, tên, mô tả, menu, văn bản, và điểm số.
  - **InputMessage**: Schema cho tin nhắn đầu vào, bao gồm vai trò, nội dung, và tùy chọn tên.
  - **InputChatHistory**: Schema cho lịch sử chat đầu vào, bao gồm danh sách các tin nhắn đầu vào.
  - **ChatRequest**: Schema cho yêu cầu chat, bao gồm lịch sử chat và tùy chọn gọi hàm.
  - **AudioTranscriptRequest**: Schema cho yêu cầu chuyển đổi âm thanh thành văn bản, bao gồm âm thanh.
  - **AudioResponse**: Schema cho phản hồi âm thanh, bao gồm thông điệp.
  - **AudioTTSRequest**: Schema cho yêu cầu chuyển đổi văn bản thành âm thanh, bao gồm văn bản.
- Code trong file `base_models.py`:
from pydantic import BaseModel
from typing import Optional, List, Any, Dict


## Groq schema for Chat Completion
class FunctionCall(BaseModel):
    name: str
    arguments: str


class Message(BaseModel):
    content: str
    role: str
    function_call: Optional[FunctionCall] = None


class GroqChatCompletionResponse(BaseModel):
    text: str
    model: str
    usage: Dict[str, int]
    finish_reason: str


## Vector Search Models
class RestaurantSearchQuery(BaseModel):
    name_of_restaurant: Optional[str] = None
    type_of_restaurant: Optional[str] = None
    food_requested: Optional[List[str]] = None
    other_information: Optional[str] = None
    quantity: int = 1


class RestaurantSearchResult(BaseModel):
    restaurant_id: int
    restaurant_name: str
    restaurant_description: str
    restaurant_menu: str
    text: str
    score: float


## Message System
class InputMessage(BaseModel):
    role: str
    content: Any
    name: Optional[str] = None


class InputChatHistory(BaseModel):
    history: List[InputMessage]


class ChatRequest(BaseModel):
    query: InputChatHistory
    function_call: bool = True


## Audio system
class AudioTranscriptRequest(BaseModel):
    audio: str


class AudioResponse(BaseModel):
    message: str


class AudioTTSRequest(BaseModel):
    text: str

### 3. Tóm tắt
Folder `schemas` chứa các file quan trọng để định nghĩa các schema cho các yêu cầu và phản hồi trong ứng dụng. Các schema này giúp đảm bảo tính hợp lệ của dữ liệu và hỗ trợ việc xử lý dữ liệu một cách hiệu quả. Nếu bạn cần thêm thông tin chi tiết về bất kỳ file nào, hãy cho tôi biết nhé! 😊


- **`backend/src/services`:**


Dưới đây là mô tả chi tiết cấu trúc của thư mục `@services` trong dự án của bạn, bao gồm các file và chức năng của chúng. Tôi cũng sẽ vẽ cấu trúc thư mục để bạn dễ hình dung hơn nhé! 😊

### Cấu trúc thư mục `@services`

```
backend/
└── src/
    └── services/
        ├── __init__.py
        ├── README.md
        ├── functions/
        │   ├── __init__.py
        │   ├── README.md
        │   ├── cart_functions/
        │   │   ├── __init__.py
        │   │   ├── add_food_to_cart.py
        │   │   └── remove_food_from_cart.py
        │   ├── restaurant_functions/
        │   │   ├── __init__.py
        │   │   ├── find_restaurant_pages.py
        │   │   ├── get_menu_of_restaurant.py
        │   │   └── open_restaurant_page.py
        │   └── utils/
        │       ├── __init__.py
        │       └── dummy_function.py
        └── grop_service/
            ├── __init__.py
            ├── chat_completion.py
            ├── embeddings.py
```

### Mô tả các file trong thư mục `@services`
3. **`functions/`**
   - **Chức năng**: 
     - Chứa các file định nghĩa các hàm dịch vụ cho ứng dụng, bao gồm các hàm liên quan đến giỏ hàng và nhà hàng.

   - **`__init__.py`**
     - **Chức năng**: 
       - Cho phép import các hàm từ các module con trong thư mục `functions`.
      - Code trong file `__init__.py`:
      from .restaurant_functions import *
from .cart_functions import *
from .utils import dummy_function

__all__ = [
    'find_restaurant_pages',
    'get_menu_of_restaurant',
    'open_restaurant_page',
    'add_food_to_cart',
    'remove_food_from_cart',
    'dummy_function'
]

   - **`README.md`**
     - **Chức năng**: 
       - Cung cấp thông tin mô tả về các hàm trong thư mục `functions`.

   - **`cart_functions/`**
     - **Chức năng**: 
       - Chứa các hàm liên quan đến việc quản lý giỏ hàng.

     - **`__init__.py`**
       - **Chức năng**: 
         - Cho phép import các hàm từ các module con trong thư mục `cart_functions`.
          - Code trong file `__init__.py`:
          from .add_food_to_cart import add_food_to_cart
from .remove_food_from_cart import remove_food_from_cart

__all__ = [
    'add_food_to_cart',
    'remove_food_from_cart'
]

     - **`add_food_to_cart.py`**
       - **Chức năng**: 
         - Định nghĩa hàm `add_food_to_cart`, dùng để thêm món ăn vào giỏ hàng.
- Code trong file `add_food_to_cart.py`:
from typing import Optional, Dict
from tenacity import retry, wait_random, stop_after_attempt
from ...data.database import SessionLocal
from ...data.data_utils import get_menu_of_given_restaurant

@retry(wait=wait_random(min=1, max=5), stop=stop_after_attempt(5))
async def add_food_to_cart(
        CONFIG,
        restaurant_uuid: Optional[str] = None,
        food_id: Optional[str] = None,
        quantity: int = 1,
) -> Dict:
    """Add food item to cart
    
    Args:
        CONFIG: Application configuration
        restaurant_uuid: UUID of the restaurant
        food_id: ID of the food item
        quantity: Number of items to add (default: 1)
        
    Returns:
        Dict containing cart update information
    """
    if not all([restaurant_uuid, food_id]):
        raise ValueError("Restaurant UUID and Food ID are required")
        
    # Validate food exists in restaurant
    db = SessionLocal()
    try:
        menu = get_menu_of_given_restaurant(db, restaurant_uuid)
        food_exists = any(str(food.id) == food_id for food in menu)
        if not food_exists:
            raise ValueError(f"Food ID {food_id} not found in restaurant {restaurant_uuid}")
    finally:
        db.close()
        
    return {
        "response": {
            "restaurant_uuid": str(restaurant_uuid),
            "food_id": str(food_id),
            "quantity": int(quantity),
            "status": "success",
            "message": f"Added {quantity} item(s) to cart"
        }
    }
     - **`remove_food_from_cart.py`**
       - **Chức năng**: 
         - Định nghĩa hàm `remove_food_from_cart`, dùng để xóa món ăn khỏi giỏ hàng.
- Code trong file `remove_food_from_cart.py`:
from tenacity import retry, wait_random, stop_after_attempt

@retry(wait=wait_random(min=1, max=5), stop=stop_after_attempt(5))
async def remove_food_from_cart(
        CONFIG,
        restaurant_uuid: str=None,
        food_id: str=None,
):
    return {
        "response": {
            "restaurant_uuid": str(restaurant_uuid),
            "food_id": str(food_id),
        }
    }
   - **`restaurant_functions/`**
     - **Chức năng**: 
       - Chứa các hàm liên quan đến việc quản lý nhà hàng.

     - **`__init__.py`**
       - **Chức năng**: 
         - Cho phép import các hàm từ các module con trong thư mục `restaurant_functions`.
- Code trong file `__init__.py`:
from .find_restaurant_pages import find_restaurant_pages
from .get_menu_of_restaurant import get_menu_of_restaurant
from .open_restaurant_page import open_restaurant_page

__all__ = [
    'find_restaurant_pages',
    'get_menu_of_restaurant', 
    'open_restaurant_page'
]
     - **`find_restaurant_pages.py`**
       - **Chức năng**: 
         - Định nghĩa hàm `find_restaurant_pages`, dùng để tìm kiếm các nhà hàng dựa trên các tham số đầu vào.
- Code trong file `find_restaurant_pages.py`:
from tenacity import retry, wait_random, stop_after_attempt
from typing import Optional, List
from qdrant_client import QdrantClient
from langchain.embeddings import HuggingFaceInferenceAPIEmbeddings

@retry(wait=wait_random(min=1, max=5), stop=stop_after_attempt(5))
async def find_restaurant_pages(
        CONFIG,
        name_of_restaurant: Optional[str] = None,
        type_of_restaurant: Optional[str] = None,
        food_requested: Optional[List] = None,
        other_information: Optional[str] = None,
        quantity: int = 1,
    ):
    """Search restaurants using Qdrant"""
    
    # Build query
    query = build_search_query(
        name_of_restaurant=name_of_restaurant,
        type_of_restaurant=type_of_restaurant,
        food_requested=food_requested,
        other_information=other_information
    )

    # Initialize Qdrant client
    client = QdrantClient(
        url=CONFIG["qdrant"]["url"],
        api_key=CONFIG["qdrant"]["api_key"]
    )

    # Get embeddings
    embeddings = HuggingFaceInferenceAPIEmbeddings(
        model_name="intfloat/multilingual-e5-small",
        api_key=CONFIG["huggingface"]["api_key"]
    )
    
    vector = embeddings.embed_query(query)
    
    # Search in Qdrant
    results = client.search(
        collection_name="auto-food-order",
        query_vector=vector,
        limit=quantity
    )
    
    return [result.payload for result in results]

def build_search_query(
        name_of_restaurant: Optional[str] = None,
        type_of_restaurant: Optional[str] = None,
        food_requested: Optional[List] = None,
        other_information: Optional[str] = None
    ) -> str:
    """Build search query from parameters"""
    
    query_parts = []
    
    if name_of_restaurant:
        query_parts.append(f"Restaurant: {name_of_restaurant}")
    if type_of_restaurant:
        query_parts.append(f"Type: {type_of_restaurant}")
    if food_requested:
        query_parts.append(f"Food: {', '.join(food_requested)}")
    if other_information:
        query_parts.append(f"Info: {other_information}")
        
    return " | ".join(query_parts)

     - **`get_menu_of_restaurant.py`**
       - **Chức năng**: 
         - Định nghĩa hàm `get_menu_of_restaurant`, dùng để lấy thực đơn của một nhà hàng cụ thể.
- Code trong file `get_menu_of_restaurant.py`:

from ...data.database import SessionLocal
from ...data.data_utils import get_menu_of_given_restaurant
from tenacity import retry, wait_random, stop_after_attempt

@retry(wait=wait_random(min=1, max=5), stop=stop_after_attempt(5))
async def get_menu_of_restaurant(CONFIG, restaurant_uuid: int=None):
    # Loads menu
    db = SessionLocal()
    menu = get_menu_of_given_restaurant(db, restaurant_uuid)

    # Formats the menu
    menu_formatted = []
    for food in menu:
        food_formatted = {
            "food_id": food.id,
            "name": food.name,
            "description": food.description,
            "price": food.price,
            "image": food.image,
        }
        menu_formatted.append(food_formatted)

    menu_formatted_text = f"Menu of restaurant {restaurant_uuid}:" + "\n" + "\n\n---\n".join(
        [f"{food['name']}:\nDescription: {food['description']} \nPrice: {food['price']} \nID: {food['food_id']}" for food in menu_formatted]
    )

    return {"raw": menu_formatted, "formatted": menu_formatted_text}
    # return {"response": menu_formatted_text}

     - **`open_restaurant_page.py`**
       - **Chức năng**: 
         - Định nghĩa hàm `open_restaurant_page`, dùng để mở trang của một nhà hàng.
- Code trong file `open_restaurant_page.py`:
from typing import Dict, Optional
from tenacity import retry, wait_random, stop_after_attempt

@retry(wait=wait_random(min=1, max=5), stop=stop_after_attempt(5))
async def open_restaurant_page(
    CONFIG,
    restaurant_uuid: Optional[str] = None
) -> Dict:
    """Opens the restaurant page in the interface
    
    Args:
        CONFIG: Application configuration
        restaurant_uuid: Unique identifier of the restaurant
        
    Returns:
        Dict containing restaurant_uuid for frontend handling
    """
    if not restaurant_uuid:
        raise ValueError("Restaurant UUID is required")
        
    return {
        "response": {
            "restaurant_uuid": str(restaurant_uuid),
            "status": "success",
            "message": f"Opening restaurant page for {restaurant_uuid}"
        }
    }

   - **`utils/`**
     - **Chức năng**: 
       - Chứa các hàm tiện ích không thuộc về giỏ hàng hay nhà hàng.

     - **`__init__.py`**
       - **Chức năng**: 
         - Cho phép import các hàm từ các module con trong thư mục `utils`.
- Code trong file `__init__.py`:
from .restaurant_functions import *
from .cart_functions import *
from .utils import dummy_function

__all__ = [
    'find_restaurant_pages',
    'get_menu_of_restaurant',
    'open_restaurant_page',
    'add_food_to_cart',
    'remove_food_from_cart',
    'dummy_function'
]
     - **`dummy_function.py`**
       - **Chức năng**: 
         - Định nghĩa hàm `dummy_function`, một hàm mẫu có thể được sử dụng trong các tình huống thử nghiệm.
- Code trong file `dummy_function.py`:
from tenacity import retry, wait_random, stop_after_attempt

@retry(wait=wait_random(min=1, max=5), stop=stop_after_attempt(5))
async def dummy_function(*args, **kwargs):
    return ""

4. **`grop_service/`**
   - **Chức năng**: 
     - Chứa các file định nghĩa các dịch vụ liên quan đến Groq và OpenAI.

   - **`__init__.py`**
     - **Chức năng**: 
       - Cho phép import các thành phần từ thư mục `grop_service`.

   - **`audio.py`**
     - **Chức năng**: 
       - Chứa các hàm liên quan đến xử lý âm thanh (chưa có nội dung cụ thể trong đoạn mã bạn cung cấp).

   - **`chat_completion.py`**
     - **Chức năng**: 
       - Định nghĩa hàm `chat_completion`, dùng để tạo phản hồi chat bằng cách sử dụng Groq.
- Code trong file `chat_completion.py`:
from tenacity import retry, wait_random, stop_after_attempt
from langchain_groq import ChatGroq

@retry(wait=wait_random(min=1, max=5), stop=stop_after_attempt(5))
async def chat_completion(messages, CONFIG, functions=[], client=None):
    """Generate chat completion using Groq"""
    
    # Initializing the Groq configuration
    llm = ChatGroq(
        temperature=0.3,
        model="llama-3.1-70b-versatile",
        api_key=CONFIG["groq"]["api_key"]
    )

    if len(functions) > 0:
        # Handle functions if present
        response = await llm.agenerate([messages], functions=functions, function_call="auto")
    else:
        response = await llm.agenerate([messages])

    return response.generations[0][0].text
   - **`embeddings.py`**
     - **Chức năng**: 
       - Định nghĩa hàm `embeddings`, dùng để tạo embeddings từ nội dung văn bản.
- Code trong file `embeddings.py`:
from tenacity import retry, wait_random, stop_after_attempt
from langchain.embeddings import HuggingFaceInferenceAPIEmbeddings

@retry(wait=wait_random(min=1, max=5), stop=stop_after_attempt(5))
async def embeddings(content, CONFIG, client=None):
    """Generate embeddings using HuggingFace"""
    
    embeddings_model = HuggingFaceInferenceAPIEmbeddings(
        model_name="intfloat/multilingual-e5-small",
        api_key=CONFIG["huggingface"]["api_key"]
    )
    
    return await embeddings_model.aembed_documents([content])
### Tóm tắt
Thư mục `@services` chứa các file định nghĩa các dịch vụ cho ứng dụng, bao gồm các hàm quản lý giỏ hàng, nhà hàng và các dịch vụ liên quan đến Groq và OpenAI. Mỗi file có một vai trò cụ thể trong việc xử lý dữ liệu và tương tác với người dùng.

- **`backend/src/static`:**
Chắc chắn rồi! Dưới đây là mô tả chi tiết cấu trúc và nội dung của folder `static` trong project này 📁

### 1. Cấu trúc thư mục `static`

```
backend/src/static/
├── images/
└── css/
```

### 2. Chi tiết từng thư mục

#### 2.1. `images/`
- **Mục đích**: Chứa các file ảnh được sử dụng trong ứng dụng.
- **Nội dung**: 
  - Các file ảnh có thể bao gồm logo, hình ảnh món ăn, hình ảnh nhà hàng, v.v. (các file cụ thể sẽ phụ thuộc vào nội dung của dự án).

### 3. Tóm tắt
Folder `static` ở trong thư mục backend chỉ chứa các tài nguyên tĩnh như hình ảnh, giúp hỗ trợ cho giao diện người dùng của ứng dụng. 

- **`backend/.env`**:
File này chứa các biến môi trường 
- Nội dung trong file .env: 
# GROQ_API_KEY
GROQ_API_KEY=your_api_key
GOOGLE_API_KEY=your_api_key
# Qdrant
QDRANT_URL=your_api_key
QDRANT_API_KEY=your_api_key
# HuggingFace
HUGGINGFACE_API_KEY=your_api_key

-   **`backend/src/agents`:** Định nghĩa các AI agent và công cụ của chúng.
    -   **`agent_1`, `agent_2`,...** : Mỗi thư mục chứa code và hướng dẫn của một agent.
        -   **`__init__.py`**: Import các class của agent
        -   **`agent_1.py`** (ví dụ): Class agent, kế thừa từ class Agent của Agency Swarm
        -   **`instructions.md`**: Mô tả vai trò, mục tiêu và cách hoạt động của agent.
        -   **`tools/`**: Thư mục chứa các file công cụ cho agent.
            -   **`tool_1.py`, `tool_2.py`,...:** Các file định nghĩa các công cụ mà agent sử dụng.
-   **`backend/src/utils`:** Các utility functions dùng chung.
- **`backend/src/main.py`:** Điểm khởi đầu của ứng dụng backend FastAPI, định nghĩa các endpoint API.
- Nội dung trong file main.py: 
import sys

relative_position = "../"
if relative_position not in sys.path:
    sys.path.insert(0, relative_position)

import uvicorn
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles

from src.config import CONFIG
from src.endpoints import include_all_routers
from src.handlers import MainHandler
from src.endpoints.router import router

# Integrating SQLite
from src.data import data_models
from src.data import SessionLocal, engine

data_models.Base.metadata.create_all(bind=engine)


class Application:
    @classmethod
    def setup(cls, app: FastAPI, handler: MainHandler, CONFIG):
        # Adding CORS middleware
        app.add_middleware(
            CORSMiddleware,
            allow_origins=[
                "http://localhost:5174"
            ],  # Chỉ chấp nhận yêu cầu từ frontend ở cổng 5174
            allow_credentials=True,
            allow_methods=["*"],
            allow_headers=["*"],
            expose_headers=["*"],
        )

        # Registering routers and exception handlers
        include_all_routers(app, handler, CONFIG)

        # Thêm router gốc
        app.include_router(router)

        # Thêm static file middleware
        app.mount("/static", StaticFiles(directory="static"), name="static")

        return app


app = FastAPI(
    title="Auto food order",
    description="Auto food prototype implementation",
)

app = Application.setup(app, MainHandler(), CONFIG)

# Running the app
if __name__ == "__main__":
    uvicorn.run("main:app", host="127.0.0.1", port=8080, reload=True, workers=8)

-   **`backend/requirements.txt`**: Liệt kê tất cả các thư viện Python cần thiết.

Tech stack phía frontend 🔍

### 1. Framework & Library Chính

```10:19:frontend/package.json
  "dependencies": {
    "axios": "^1.2.1",
    "bootstrap": "^5.2.3",
    "feather-icons": "^4.29.1",
    "uuid": "^9.0.1",
    "vue": "^3.3.4",
    "vue-axios": "^3.5.2",
    "vue-feather": "^2.0.0",
    "vue3-uuid": "^1.0.0"
  },
```


- **Vue.js 3**: Framework chính để xây dựng giao diện người dùng
- **Axios & Vue-Axios**: Thư viện để thực hiện các HTTP request
- **Bootstrap 5**: Framework CSS để styling
- **Feather Icons**: Thư viện icon
- **UUID**: Thư viện để tạo các ID duy nhất

### 2. Development Tools

```20:26:frontend/package.json
  "devDependencies": {
    "@vitejs/plugin-vue": "^4.0.0",
    "autoprefixer": "^10.4.13",
    "postcss": "^8.4.21",
    "tailwindcss": "^3.2.4",
    "vite": "^4.0.0"
  }
```


- **Vite**: Build tool và dev server
- **Tailwind CSS**: Framework CSS utility-first
- **PostCSS**: Tool xử lý CSS
- **Autoprefixer**: Plugin PostCSS để tự động thêm vendor prefixes

### 3. Cấu Hình & Tính Năng 🛠️

- **Development Server**: Chạy trên host 0.0.0.0 cho phép truy cập từ xa

```6:8:frontend/package.json
    "dev": "vite --host 0.0.0.0",
    "build": "vite build",
    "preview": "vite preview"
```


- **Static Assets**: Được cấu hình trong Vite

```1:13:frontend/vite.config.js
import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  publicDir: 'src/assets',
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
```


### 4. Giao Diện Người Dùng 🎨

- Sử dụng kết hợp giữa Bootstrap và Tailwind CSS cho styling
- Hỗ trợ responsive design
- Tích hợp icons thông qua Feather Icons
- Có khả năng xử lý chat và tương tác voice

### 5. Tính Năng Đặc Biệt 🌟

- **Real-time Chat**: Tích hợp với backend thông qua API
- **Voice Integration**: Hỗ trợ ghi âm và xử lý giọng nói
- **Shopping Cart**: Quản lý giỏ hàng trực tiếp trên giao diện
- **Multi-language Support**: Hỗ trợ đa ngôn ngữ

Đây là một tech stack khá hiện đại và phù hợp cho một ứng dụng web progressive với các tính năng tương tác cao 👍
Chắc chắn rồi! Dưới đây là mô tả chi tiết cấu trúc và nội dung của các file trong folder `frontend` của project này 📁

### 1. Cấu trúc thư mục `frontend`

```
frontend/
├── .env
├── .gitignore
├── config.yaml
├── index.html
├── vite.config.js
├── tailwind.config.js
├── package.json
├── package-lock.json
├── postcss.config.js
└── src/
    ├── App.vue
    ├── main.js
    ├── assets/
    │   ├── favicon-white.svg
    │   ├── handsfree.svg
    │   ├── maximize_icon.svg
    │   ├── minimize_icon.svg
    ├──assets/ beverages/
    ├── assets/foods/ 
    └── assets/restaurants/
frontend/src/components/
├── Chatbot/
│   ├── BotMessage.vue
│   ├── ChatBody.vue
│   ├── ChatContainer.vue
│   ├── ChatFooter.vue
│   ├── ChatHeader.vue
│   ├── InputButtons.vue
│   ├── MessageInput.vue
│   ├── TypingMessage.vue
│   └── UserMessage.vue
├── Common/
│   ├── FoodItem.vue
│   ├── LoadingSpinner.vue
├── NavBar/
│   └── NavBar.vue
├── Restaurants/
│   ├── RestaurantFullView.vue
│   ├── RestaurantPreviewCard.vue
│   └── RestaurantsContainer.vue
└── ShoppingCart/
    └── ShoppingCart.vue

```

### 2. Chi tiết từng file

#### 2.1. `.env`
- **Mục đích**: Chứa các biến môi trường cho ứng dụng frontend.
- **Nội dung**:
  - `VITE_APP_API_URL`: URL của API backend, được sử dụng để gửi yêu cầu từ frontend đến backend.
  - `VITE_ENVIRONMENT`: Môi trường phát triển, có thể là "development" hoặc "production".
- Nội dung trong file '.env':
VITE_APP_API_URL="http://localhost:8080"
VITE_ENVIRONMENT="development"

#### 2.2. `.gitignore`
- **Mục đích**: Xác định các file và thư mục mà Git sẽ bỏ qua khi commit.
- **Nội dung**:
  - Các thư mục như `node_modules`, `logs`, và các file tạm thời khác không cần thiết cho việc quản lý mã nguồn.

#### 2.3. `config.yaml`
- **Mục đích**: Chứa các cấu hình cho ứng dụng.
- **Nội dung**: File này hiện tại không có nội dung cụ thể, có thể được sử dụng để lưu trữ các cấu hình khác nhau cho ứng dụng.

#### 2.4. `index.html`
- **Mục đích**: File HTML chính cho ứng dụng frontend.
- **Nội dung**:
  - Định nghĩa cấu trúc cơ bản của trang web, bao gồm tiêu đề, favicon, và một div với id `app` để Vue.js gắn vào.
  - Kết nối với file JavaScript chính (`main.js`) và thư viện Bootstrap.
- Code trong file 'index.html':
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <link rel="icon" href="favicon.png" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>LLM Delivery App</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
  </body>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>

</html>

#### 2.5. `package.json`
- **Mục đích**: Quản lý các phụ thuộc và cấu hình cho ứng dụng frontend.
- **Nội dung**:
  - Thông tin về tên, phiên bản, và các script để chạy ứng dụng (dev, build, preview).
  - Danh sách các phụ thuộc (dependencies) như `axios`, `vue`, `bootstrap`, và các devDependencies như `vite`, `tailwindcss`.
- Nội dung trong file package.json: 
{
  "name": "frontend",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "dev": "vite --host 0.0.0.0",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "axios": "^1.2.1",
    "bootstrap": "^5.2.3",
    "feather-icons": "^4.29.1",
    "uuid": "^9.0.1",
    "vue": "^3.3.4",
    "vue-axios": "^3.5.2",
    "vue-feather": "^2.0.0",
    "vue3-uuid": "^1.0.0"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^4.0.0",
    "autoprefixer": "^10.4.13",
    "postcss": "^8.4.21",
    "tailwindcss": "^3.2.4",
    "vite": "^4.0.0"
  }
}

#### 2.6. `postcss.config.js`
- **Mục đích**: Cấu hình cho PostCSS, một công cụ xử lý CSS.
- **Nội dung**:
  - Định nghĩa các plugin sẽ được sử dụng, bao gồm `tailwindcss` và `autoprefixer`.
- Nội dung trong file postcss.config.js: 
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}

### 3. Thư mục `src/`
Thư mục này chứa mã nguồn chính của ứng dụng frontend.

#### 3.1. `App.vue`
- **Mục đích**: Component chính của ứng dụng.
- **Nội dung**:
  - Import và sử dụng component `AppContainer`.
  - Quản lý trạng thái môi trường từ biến môi trường.
- Code trong file App.vue: 
<script setup>
	import VueFeather from 'vue-feather'
</script>

<script>
	import AppContainer from "./components/AppContainer.vue";

	export default {
		name: 'App',
		components: {
			AppContainer
		},
		data() {
			return {
				environment: import.meta.env.VITE_ENVIRONMENT,
			};
		}
	}
</script>

<template>
	<AppContainer />
</template>

<style scoped></style>

#### 3.1. `main.js`
- Code trong file main.js: 
import { createApp } from 'vue'
import App from './App.vue'

import 'bootstrap/dist/css/bootstrap.min.css'

createApp(App)
    .mount('#app')

#### 3.2. Thư mục `assets/`
Chứa các tài nguyên tĩnh như hình ảnh và biểu tượng.

- **`favicon-white.svg`**: Biểu tượng favicon cho ứng dụng.
- **`handsfree.svg`**: Hình ảnh biểu tượng cho chế độ hands-free.
- **`maximize_icon.svg`**: Hình ảnh biểu tượng để tối đa hóa cửa sổ chat.
- **`minimize_icon.svg`**: Hình ảnh biểu tượng để thu nhỏ cửa sổ chat.

#### 3.3. Thư mục `components/`
Chứa các component Vue.js cho ứng dụng.

##### 3.3.1. Thư mục `Chatbot/`
Chứa các component liên quan đến chatbot.

- **`BotMessage.vue`**: Hiển thị tin nhắn từ bot, bao gồm hình ảnh và nội dung tin nhắn. Cung cấp chức năng phát audio cho tin nhắn.
Code trong file BotMessage.vue:
<template>
  <div class="d-flex justify-content-start mb-4">
    <div class="img_cont_msg">
      <img :src="ImageMessageBot" class="user_img_msg">
    </div>
    <div class="container-assistant-msg">
      <span style="font-weight:normal" v-html="message.content"></span>
    </div>
    <div class="audio-player">
        <vue-feather
          v-if="isLoading"
          type="loader"
          class="audio-control loading"
        ></vue-feather>
        <vue-feather
          v-else-if="!isPlaying"
          type="play-circle"
          class="audio-control"
          @click="playAudio()"
        ></vue-feather>
        <vue-feather
          v-else
          type="pause-circle"
          class="audio-control playing"
          @click="stopAudio"
        ></vue-feather>
      </div>
  </div>
</template>

<script>
import axios from 'axios';
import VueFeather from 'vue-feather';

export default {
  name: "BotMessage",
  components: {
    VueFeather,
  },
  props: {
    ImageMessageBot: {
      type: String,
      required: true,
    },
    message: {
      type: Object,
      required: true,
    },
    baseApiUrl: {
      type: String,
      required: true,
    },
    autoPlayAudio: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      baseChatApiUrl: import.meta.env.VITE_APP_API_URL + "/api/chat",
      isLoading: false,
      isPlaying: false,
      audio: null,

    };
  },
  mounted() {
    if (this.autoPlayAudio) {
      this.playAudio();
    }
  },
  methods: {
    playAudio() {
      // If audioSrc already exists, play it directly
      if (this.message.audioSrc) {
        this.playExistingAudio();
      } else {
        // Otherwise, make the POST request to get the audioSrc
        this.isLoading = true;
        axios.post(this.baseChatApiUrl + '/tts', { text: this.message.content })
          .then(response => {
            // Handle response
            this.message.audioSrc = 'data:audio/wav;base64,' + response.data.response;
            this.playExistingAudio();
          })
          .catch(error => {
            // Handle error
            console.error('Error playing audio:', error);
          })
          .finally(() => {
            this.isLoading = false;
          });
      }
    },
    playExistingAudio() {
      // Create a new Audio object if it doesn't exist
      if (!this.audio) {
        this.audio = new Audio(this.message.audioSrc);
      }
      // Play the audio
      this.audio.play();
      this.isPlaying = true;
      // When the audio ends, update the isPlaying state
      this.audio.onended = () => {
        this.isPlaying = false;
      };
    },
    stopAudio() {
      if (this.audio) {
        this.audio.pause();
        this.audio.currentTime = 0;
      }
      this.isPlaying = false;
    },
  },
};
</script>

- **`ChatBody.vue`**: Hiển thị toàn bộ nội dung chat, bao gồm tin nhắn từ người dùng và bot.
Code trong file ChatBody.vue:
<template>
  <div class="card-body card-chat-body" ref="pageChat">
    <div class="user-message" v-for="(message, index) in messages" v-bind:key="message.content">
      <div v-if="message.role == 'assistant'">
        <bot-message
          v-if="message.role == 'assistant'"
          :ImageMessageBot="config.images.bot_image"
          :message="message"
          :baseApiUrl="baseApiUrl"
          :autoPlayAudio="shouldAutoPlay(message)"
        >
        </bot-message>
      </div>
      <div v-else-if="message.role == 'user'">
        <user-message
          :userProfilePic="config.images.user_profile_pic"
          :message="message"
        >
        </user-message>
      </div>
    </div>
      <typing-message v-if="userTranscribing" v-slot:user-transcribing
            :profilePic="config.images.user_profile_pic"
            :message="config.transcribingMsg" 
            :isBot="false"
          >
      </typing-message>
      <typing-message v-if="botTyping" v-slot:bot-typing 
        :profilePic="config.images.bot_image" 
        :message="this.getTypingMsg(botTypingMsg)"
        :isBot="true"
        :key="this.getTypingMsg(null)"
      >
      </typing-message>
  </div>
</template>

<script>
import BotMessage from './BotMessage.vue';
import UserMessage from './UserMessage.vue';
import TypingMessage from './TypingMessage.vue';


export default {
  name: "ChatBody",
  props: {
    config: {
      type: Object,
      required: true,
    },
    messages: {
      type: Array,
      required: true,
    },
    userTranscribing: {
      type: Boolean,
      required: true,
    },
    botTyping: {
      type: Boolean,
      required: true,
    },
    botTypingMsg: {
      type: String,
      required: false,
      default: null
    },
    baseApiUrl: {
      type: String,
      required: true,
    },
    handsFreeFlag: {
      type: Boolean,
      required: true,
    },
  },
  components: {
    BotMessage,
    UserMessage,
    TypingMessage
  },
  methods: {
    getTypingMsg: function(msg){
      if (msg == null) {
        return this.config.typingMsg
      }
      return msg
    },
    shouldAutoPlay(message) {
      let flag = message.role === 'assistant' && this.handsFreeFlag;
      return flag
    },
    watch: {
      messages(newMessages, oldMessages) {
        // Check if a new message has been added
        if (newMessages.length > oldMessages.length) {
          const newMessage = newMessages[newMessages.length - 1];
          // Check if the new message is from the assistant and if handsFreeFlag is true
          if (newMessage.role === 'assistant' && this.handsFreeFlag) {
            // Find the BotMessage component for the new message and play its audio
            const botMessageComponent = this.$children.find(
              (component) => component.message === newMessage && component instanceof BotMessage
            );
            if (botMessageComponent) {
              botMessageComponent.playAudio();
            }
          }
        }
      },
    },
  }
};
</script>

- **`ChatContainer.vue`**: Component chính cho giao diện chat, bao gồm header, body và footer.
Code trong file ChatContainer.vue: 
<template>
  <div class="chat-container" :class="{ 'minimized': !isChatOpen }">
    <div class="card card-chat">
      <chat-header 
        :ImageHeader="config.images.header_image"
        :config="config"
        :isMinimized="!isChatOpen"
        :handsFreeFlag="handsFreeFlag"
        @toggle-chatbot="toggleChatBot"
        @toggle-handsfree="$emit('toggle-handsfree')"
      >
      </chat-header>

      <chat-body
        v-show="isChatOpen"
        ref="chatMessages" 
        :messages="messages"
        :config="config"
        :userTranscribing="userTranscribing"
        :botTyping="botTyping"
        :botTypingMsg="botTypingMsg"
        :baseApiUrl="baseApiUrl"
        :handsFreeFlag="handsFreeFlag"
      >
      </chat-body>

      <chat-footer 
        v-show="isChatOpen"
        ref="chatFooter"
        :config="config"
        :isRecordingFlag="isRecordingFlag"
        :recordedVoiceURL="recordedVoiceURL"
        :timeRecorded="timeRecorded"
        @toggle-recording="toggleRecording"
        @cancel-audio="cancelAudio"
        @send-message="sendMessage"
        @select-suggested-question="selectSuggestedQuestion"
      >
    </chat-footer>
    </div>
  </div>
</template>

<script>
import ChatHeader from "./ChatHeader.vue";
import ChatFooter from "./ChatFooter.vue";
import ChatBody from "./ChatBody.vue";

import '../styles/chatbot.css'


export default {
  name: "ChatContainer",
  props: {
    config: {
      type: Object,
      required: true,
    },
    messages: {
      type: Array,
      required: true,
    },
    isRecordingFlag: {
      type: Boolean,
      required: true,
    },
    recordedVoiceURL: {
      type: String,
      required: true,
    },
    timeRecorded: {
      type: Number,
      required: true,
    },
    botTyping: {
      type: Boolean,
      required: true,
    },
    botTypingMsg: {
      type: String,
      required: false,
      default: null
    },
    userTranscribing: {
      type: Boolean,
      required: true,
    },
    isChatOpen: {
      type: Boolean,
      required: true,
    },
    handsFreeFlag: {
      type: Boolean,
      required: true,
    },
  },
  components: {
    ChatHeader,
    ChatFooter,
    ChatBody,
  },
  data() {
    return {
    };
  },
  watch: {
    isChatOpen(newValue) {
      if (newValue) {
        // Chat is opening
        this.animateChatOpening();
      } else {
        // Chat is closing
        this.animateChatClosing();
      }
    },
  },

  methods: {
    // Messages handling
    sendMessage(message) {
      this.$emit("send-message", message);
    },
    toggleRecording() {
      this.$emit("toggle-recording");
    },
    cancelAudio() {
      this.$emit("cancel-audio");
    },
    toggleChatBot() {
      this.$emit("toggle-chatbot");
    },
    animateChatOpening() {
      this.$nextTick(() => {
        // Assuming .chat-body and .chat-footer are the elements to animate
        const chatBody = this.$refs.chatBody;
        const chatFooter = this.$refs.chatFooter;

        // Set initial styles for animation start state
        chatBody.style.display = 'none';
        chatFooter.style.display = 'none';

        // Start the animation for the chat container height
        this.$el.style.height = '70vh';

        // After a delay (to sync with the container height animation), show chat body and footer
        setTimeout(() => {
          chatBody.style.display = '';
          chatFooter.style.display = '';
          chatBody.style.opacity = '0';
          chatFooter.style.opacity = '0';

          // Fade in the chat body and footer
          chatBody.style.transition = 'opacity 0.5s ease';
          chatFooter.style.transition = 'opacity 0.5s ease';
          chatBody.style.opacity = '1';
          chatFooter.style.opacity = '1';
        }, 500); 
      });
    },
    animateChatClosing() {
      // Assuming .chat-body and .chat-footer are the elements to animate
      const chatBody = this.$refs.chatBody.$el; 
      const chatFooter = this.$refs.chatFooter.$el;

      // Fade out the chat body and footer with a transition
      chatBody.style.transition = 'opacity 0.5s ease';
      chatFooter.style.transition = 'opacity 0.5s ease';
      chatBody.style.opacity = '0';
      chatFooter.style.opacity = '0';

      // Wait for the fade-out transition to finish before minimizing the container
      setTimeout(() => {
        this.$el.classList.add('minimized');
      }, 500); 
    },
  }
};
</script>

- **`ChatFooter.vue`**: Chứa các input để gửi tin nhắn và các nút điều khiển.
Code trong file ChatFooter.vue: 
<template>
  <div class="card-footer card-chat-footer">
    <!-- <suggested-questions
      :questions="questions"
      @select-suggested-question="selectSuggestedQuestion"
    ></suggested-questions> -->

    <div class="input-group">
      <message-input
        v-model="newMessage"
        :isRecordingFlag="isRecordingFlag"
        :recordedVoiceURL="recordedVoiceURL"
        :msgPlaceholder="config.msgPlaceholder"
        :timeRecorded="timeRecorded"
        @send-message="sendMessage"
      >
      </message-input>
      <div class="input-group-append">
        <input-buttons
          v-model="newMessage"
          :isRecordingFlag="isRecordingFlag"
          :recordedVoiceURL="recordedVoiceURL"
          @cancel-audio="cancelAudio"
          @toggle-recording="toggleRecording"
          @send-message="sendMessage"
        ></input-buttons>
      </div>
    </div>
  </div>
</template>

<script>
import InputButtons from "./InputButtons.vue";
import MessageInput from "./MessageInput.vue";

export default {
  name: "ChatFooter",
  props: {
    isRecordingFlag: {
      type: Boolean,
      required: true,
    },
    recordedVoiceURL: {
      type: String,
      default: null,
    },
    timeRecorded: {
      type: Number,
      required: true,
    },
    config: {
      type: Object,
      required: true,
    },
  },
  components: {
    InputButtons,
    MessageInput,
  },
  data() {
    return {
      newMessage: "",
    };
  },
  methods: {
    sendMessage(message) {
      this.$emit("send-message", message);
    },
    cancelAudio() {
      this.$emit("cancel-audio");
    },
    toggleRecording() {
      this.$emit("toggle-recording");
    },
  },
};
</script>

- **`ChatHeader.vue`**: Hiển thị tiêu đề của chat và các nút điều khiển như toggle hands-free và minimize.
Code trong file ChatHeader.vue: 
<template>
  <div class="card-header card-chat-header msg_head" :class="{ 'minimized-header': isMinimized }">
    <div class="d-flex justify-content-between align-items-center w-100">
      <div v-show="!isMinimized">
        <img :src="ImageHeader" class="user_img">
        <span class="user_info_maximized">{{ config.title }}</span>
      </div>
      <div class="user_info_minimized" v-show="isMinimized">
        <span>Talk to our virtual assistant</span>
      </div>
      <button v-show="!isMinimized" @click="$emit('toggle-handsfree')" class="btn-minimize ml-auto" :style="handsFreeFlag? 'background-color: rgba(0, 0, 0, 0.2); radius-border: 20%;' : ''">
        <img src='@/assets/handsfree.svg' alt="Hands-free">
      </button>
      <button @click="$emit('toggle-chatbot')" class="btn-minimize ml-auto">
        <img :src="isMinimized ? 'maximize_icon.svg' : 'minimize_icon.svg'" alt="Toggle">
      </button>
    </div>
  </div>
</template>

<script>


export default {
  name: "ChatHeader",
  props: {
    ImageHeader: {
      type: String,
      required: true,
    },
    config: {
      type: Object,
      required: true,
    },
    isMinimized: {
      type: Boolean,
      required: true,
    },
    handsFreeFlag: {
      type: Boolean,
      required: true,
    },
  },
};
</script>


- **`InputButtons.vue`**: Chứa các nút để gửi tin nhắn, ghi âm và xóa âm thanh đã ghi.
Code trong file InputButtons.vue:
<template>
  <div class="button-container">
    <div class="btn-group">
      <div v-if="recordedVoiceURL" class="input-btn" @click="cancelAudio">
        <vue-feather type="trash-2" size="16"></vue-feather>
      </div>
      <div class="input-btn" @click="toggleRecording" v-if="!isRecordingFlag">
        <vue-feather type="mic" size="16"></vue-feather>
      </div>
      <div class="input-btn" @click="toggleRecording" v-if="isRecordingFlag">
        <vue-feather type="pause" size="16"></vue-feather>
      </div>
      <div class="input-btn" @click="sendMessage(value)" v-if="recordedVoiceURL">
        <vue-feather type="navigation" size="16"></vue-feather>
      </div>
    </div>
    
  </div>
</template>

<script>
import VueFeather from "vue-feather";

export default {
  name: "InputButtons",
  model: {
    prop: "newMessage",
    event: "update:newMessage",
  },
  components: {
    VueFeather,
  },
  props: {
    isRecordingFlag: {
      type: Boolean,
      required: true,
    },
    recordedVoiceURL: {
      type: String,
      default: null,
    },
  },
  methods: {
    cancelAudio() {
      this.$emit("cancel-audio");
    },
    toggleRecording() {
      this.$emit("toggle-recording");
    },
    sendMessage(message) {
    this.$emit("update:newMessage", message);
    this.$emit("send-message", message);
    },
  },
};
</script>
- **`MessageInput.vue`**: Input để người dùng nhập tin nhắn, hỗ trợ ghi âm và phát lại âm thanh.
Code trong file MessageInput.vue: 
<template>
  <textarea
    v-if="(!isRecordingFlag) && (recordedVoiceURL==null)"
    name=""
    v-model="messageToBeSent"
    class="form-control type_msg"
    :placeholder="msgPlaceholder"
    :disabled="isRecordingFlag"
    @keyup.enter="sendMessage(messageToBeSent)"
    style="resize:none;"
  ></textarea>
  <span class="form-control type_msg recording-input" v-if="isRecordingFlag"
    > {{ formatTime(timeRecorded) }}
  </span>
  <audio 
    class="form-control type_msg recording-input"
    v-if="recordedVoiceURL" :src="recordedVoiceURL" controls>
  </audio>
</template>

              
<script>

export default {
  name: "MessageInput",
  model: {
    prop: "newMessage",
    event: "update:newMessage",
  },
  props: {
    isRecordingFlag: {
      type: Boolean,
      required: true,
    },
    recordedVoiceURL: {
      type: String,
      default: null,
    },
    msgPlaceholder: {
      type: String,
      required: true,
    },
    timeRecorded: {
      type: Number,
      required: true,
    },
  },
  emits: ["send-message", "update:newMessage",],
  data() {
    return {
      messageToBeSent: "",
    } 
  },
  methods: {
    sendMessage(message) {
      this.$emit("update:newMessage", message);
      this.$emit("send-message", message);
      this.messageToBeSent = "";
    },
    formatTime(milliseconds) {
      let totalSeconds = Math.floor(milliseconds / 1000);
      let minutes = Math.floor(totalSeconds / 60);
      let seconds = totalSeconds % 60;
      let formattedTime = `${minutes.toString().padStart(2, "0")}:${seconds
        .toString()
        .padStart(2, "0")}`;
      console.log(formattedTime)
      return formattedTime;
    },
  },
};
</script>

- **`TypingMessage.vue`**: Hiển thị trạng thái "đang gõ" của bot hoặc người dùng.
Code trong file TypingMessage.vue: 
<template>
    <div :class="messageClass">
      <div v-if="isBot" class="img_cont_msg">
        <img :src="profilePic" class="user_img_msg">
      </div>
      <div :class="containerClass">
        <div class="typing-load-msg" :class="{ 'fadeInSlideRight': animate }" style="text-align:left">
          <span style="font-weight:normal" v-html="message"></span>
        </div>
      </div>
    </div>
  </template>
  
  <script>
  export default {
    name: "TypingMessage",
    props: {
      profilePic: {
        type: String,
        required: true,
      },
      message: {
        type: String,
        required: true,
      },
      isBot: {
        type: Boolean,
        required: true,
      },
    },
    data() {
      return {
        animate: true,
      }
    },
    watch: {
      message() {
        this.animate = false;
        this.$nextTick(() => { // Wait for the DOM update
          void this.$el.offsetWidth; // Force a reflow
          this.animate = true;
        });
      },
    },
    computed: {
      messageClass() {
        return this.isBot ? "d-flex justify-content-start mb-4" : "d-flex justify-content-end mb-4";
      },
      containerClass() {
        return this.isBot ? "container-assistant-msg" : "container-user-msg"
      }
    },
  };
  </script>
##### 3.3.2. `UserMessage.vue`
- **Mục đích**: Hiển thị tin nhắn từ người dùng.
- **Nội dung**: Chứa nội dung tin nhắn và định dạng cho phù hợp với giao diện.
- Code trong file UserMessage.vue: 
<template>
  <div class="d-flex justify-content-end mb-4">
    <div class="container-user-msg">
      {{ message.content }}
    </div>
  </div>
</template>

<script>
export default {
  name: "UserMessage",
  props: {
    message: {
      type: Object,
      required: true,
    },
  },
};
</script>

### 4. Tóm tắt
Folder `frontend` chứa tất cả các file cần thiết để xây dựng giao diện người dùng cho ứng dụng. Các file này bao gồm cấu hình, mã nguồn, và tài nguyên tĩnh, giúp tạo ra một trải nghiệm người dùng mượt mà và tương tác. Nếu bạn cần thêm thông tin chi tiết về bất kỳ file nào, hãy cho tôi biết nhé! 😊



Chắc chắn rồi! Dưới đây là mô tả chi tiết cấu trúc và nội dung của các file trong folder `components` của project này 📁

### 1. Cấu trúc thư mục `components`

```
frontend/src/components/
├── Chatbot/
│   ├── BotMessage.vue
│   ├── ChatBody.vue
│   ├── ChatContainer.vue
│   ├── ChatFooter.vue
│   ├── ChatHeader.vue
│   ├── InputButtons.vue
│   ├── MessageInput.vue
│   ├── TypingMessage.vue
│   └── UserMessage.vue
├── Common/
│   ├── FoodItem.vue
│   ├── LoadingSpinner.vue
│   └── general_config.json
├── NavBar/
│   └── NavBar.vue
├── Restaurants/
│   ├── RestaurantFullView.vue
│   ├── RestaurantPreviewCard.vue
│   └── RestaurantsContainer.vue
└── ShoppingCart/
    └── ShoppingCart.vue
```

### 2. Chi tiết từng file

#### 2.1. Thư mục `Chatbot/`
Chứa các component liên quan đến chatbot.

- **`BotMessage.vue`**
  - **Mục đích**: Hiển thị tin nhắn từ bot, bao gồm hình ảnh và nội dung tin nhắn.
  - **Nội dung**: Cung cấp chức năng phát audio cho tin nhắn.

- **`ChatBody.vue`**
  - **Mục đích**: Hiển thị toàn bộ nội dung chat, bao gồm tin nhắn từ người dùng và bot.
  - **Nội dung**: Quản lý danh sách tin nhắn và hiển thị chúng.

- **`ChatContainer.vue`**
  - **Mục đích**: Component chính cho giao diện chat, bao gồm header, body và footer.
  - **Nội dung**: Tổ chức các phần của chat và quản lý trạng thái.

- **`ChatFooter.vue`**
  - **Mục đích**: Chứa các input để gửi tin nhắn và các nút điều khiển.
  - **Nội dung**: Quản lý việc gửi tin nhắn và các hành động liên quan.

- **`ChatHeader.vue`**
  - **Mục đích**: Hiển thị tiêu đề của chat và các nút điều khiển như toggle hands-free và minimize.
  - **Nội dung**: Cung cấp giao diện cho header của chat.

- **`InputButtons.vue`**
  - **Mục đích**: Chứa các nút để gửi tin nhắn, ghi âm và xóa âm thanh đã ghi.
  - **Nội dung**: Quản lý các hành động liên quan đến việc nhập tin nhắn.

- **`MessageInput.vue`**
  - **Mục đích**: Input để người dùng nhập tin nhắn, hỗ trợ ghi âm và phát lại âm thanh.
  - **Nội dung**: Quản lý việc nhập tin nhắn và hiển thị trạng thái ghi âm.

- **`TypingMessage.vue`**
  - **Mục đích**: Hiển thị trạng thái "đang gõ" của bot hoặc người dùng.
  - **Nội dung**: Cung cấp giao diện cho trạng thái gõ tin nhắn.

- **`UserMessage.vue`**
  - **Mục đích**: Hiển thị tin nhắn từ người dùng.
  - **Nội dung**: Chứa nội dung tin nhắn và định dạng cho phù hợp với giao diện.

#### 2.2. Thư mục `Common/`
Chứa các component và cấu hình chung.

- **`FoodItem.vue`**
  - **Mục đích**: Hiển thị thông tin về món ăn, bao gồm hình ảnh, tên, mô tả và giá.
  - **Nội dung**: Cung cấp chức năng thêm vào giỏ hàng và điều chỉnh số lượng món ăn.
  - Code trong file FoodItem.vue:
  <template>
  <div class="food-item">
    <img  @error="onImageError" :src="!(food.image==null)? food.image : '@/assets/food-placeholder.png'" class="food-img" alt="Food Image" />
    <div class="food-details">
      <h4 class="food-name">{{ food.name }}</h4>
      <p class="restaurant-name" v-if="view=='shoppingcart'">{{ food.restaurant.name}}</p>
      <p class="food-description" v-if="view=='restaurant'">{{ food.description }}</p>
      <p class="food-price"> Item(s) price: {{ parseFloat((food.price * food.quantity).toFixed(2)) }} $</p>
    </div>
    <div class="food-actions">
      <div class="quantity-control">
        <input class="quantity-input" type="number" v-model.number="food.quantity" min="1" readonly />
        <button class="quantity-btn" @click="decrementQuantity">-</button>
        <button class="quantity-btn" @click="incrementQuantity">+</button>
      </div>
      <button v-if="view=='restaurant'" class="add-to-cart-btn" @click="addToCart">Add</button>
      <button v-if="view=='shoppingcart'" class="add-to-cart-btn" @click="removeFromCart">Remove</button>
    </div>
  </div>
</template>
  
  <script>
  export default {
    props: {
      food: {
        type: Object,
        required: true,
      },
      view: {
        type: String,
        default: "restaurant",
      },
    },
    data() {
      return {
        quantity: this.food.quantity,
      };
    },
    methods: {
      registerAction(msg) {
        this.$emit("register-action", msg)
      },
      addToCart() {
        this.$emit("add-to-cart", this.food);
      },
      removeFromCart() {
        this.$emit("remove-from-cart", { food: this.food });
      },
      incrementQuantity() {
        this.food.quantity++;
        this.registerAction("incremented the amount of food " + this.food.name + " to " + this.food.quantity + " items (in the restaurant page, not in the shopping cart)")
      },
      decrementQuantity() {
        if (this.food.quantity > 1) {
          this.food.quantity--;
          this.registerAction("decremented the amount of food " + this.food.name + " to " + this.food.quantity + " items (in the restaurant page, not in the shopping cart)")
        }
      },
      onImageError(event) {
        event.target.src = '@/assets/food-placeholder.png';
      }
    },
  };
  </script>

- **`LoadingSpinner.vue`**
  - **Mục đích**: Hiển thị một spinner khi đang tải dữ liệu.
  - **Nội dung**: Cung cấp giao diện cho spinner với chiều cao và chiều rộng có thể tùy chỉnh.
  - Code trong file LoadingSpinner.vue: 
  <template>
    <div class="loading-spinner-container">
        <div class="loading-spinner" :style="{ height: spinnerHeight, width: spinnerWidth }"></div>
    </div>
</template>

  <script>
  export default {
    name: 'LoadingSpinner',
    props: {
      height: {
        type: String,
        default: '36px',
      },
      width: {
        type: String,
        default: '36px',
      },
    },
    computed: {
      spinnerHeight() {
        return this.height;
      },
      spinnerWidth() {
        return this.width;
      },
    },
  };
  </script>

#### 2.3. Thư mục `NavBar/`
Chứa component cho thanh điều hướng.

- **`NavBar.vue`**
  - **Mục đích**: Hiển thị thanh điều hướng cho ứng dụng.
  - **Nội dung**: Bao gồm logo, tiêu đề và giỏ hàng.
  - Nội dung code : 
  <template>
    <header class="navbar navbar-dark sticky-top">
        <div class="nav navbar pt-2">
            <div class="navbar-brand shadow-none d-flex align-items-center">
                <img class="navbar-logo" src="@/assets/favicon.png" />
                <h2 class="navbar-title mb-0">
                  Discover restaurants and order food online using a virtual assistant 
                </h2>
            </div>
        </div>
        <div class="shopping-cart" @click="$emit('open-cart')">
            <span v-if="cartItemCount" class="cart-item-count">{{ cartItemCount }}</span>
            <img class="shopping-cart-logo" src="@/assets/shopping-cart-icon.png" alt="Shopping Cart" />
        </div>
    </header>
</template>

<script>
import "../styles/navbar.css"
import "../styles/shoppingcart.css"

export default {
  name: "NavBar",
  props: {
    shoppingCart: {
      type: Array,
      default: () => [],
    },
  },
  computed: {
    cartItemCount() {
      return this.shoppingCart.length;
    },
  },
};
</script>

#### 2.4. Thư mục `Restaurants/`
Chứa các component liên quan đến nhà hàng.

- **`RestaurantFullView.vue`**
  - **Mục đích**: Hiển thị thông tin chi tiết về một nhà hàng.
  - **Nội dung**: Bao gồm mô tả và danh sách món ăn của nhà hàng.
  - Nội dung code: 
  <template>
  <div v-if="restaurant" class="restaurant-full-view">
    <div class="sticky-top">
      <button class="go-back-btn" @click="goBack">Return</button>
      <h2>{{ restaurant.name }}</h2>
      <img  @error="onImageError" :src="!(restaurant.image==null)? restaurant.image : '@/assets/restaurant.png'" class="restaurant-card-img-fullview rounded" alt="Restaurant Image" />
      <div class="description-container">
        <p>{{ restaurant.description }}</p>
      </div>
    </div>
    
    <div class="food-list">
      <food-item
        v-for="(food, index) in this.foods"
        :key="index"
        :food="food"
        @add-to-cart="addToCart"
        @register-action="registerAction"
      ></food-item>
    </div>
  </div>
</template>
  

<script>

import FoodItem from "../Common/FoodItem.vue";
import axios from "axios";

export default {
  components: {
    FoodItem,
  },
  props: {
    restaurant: {
      type: Object,
      required: true,
    },
  },
  data(){
    return {
      baseApiUrl: import.meta.env.VITE_APP_API_URL + "/api",
      foods: []
    }
  },
  methods: {
    registerAction(msg) {
      this.$emit("register-action", msg)
    },
    getFoods(restaurant) {
      axios.get(this.baseApiUrl + "/restaurants/" + `${restaurant.uuid}` + "/foods")
        .then(response => {
          this.foods = [];
          const data = response.data;
          for (let i = 0; i < data.length; i++) {
            this.foods.push({
              name: data[i].name,
              description: data[i].description,
              price: data[i].price,
              quantity: 1,
              restaurant: {
                restaurant
              },
              image: data[i].image,
              // image: null,
              uuid: data[i].id,
            });
          }
        })
        .catch(error => {
          console.log(error);
        });
    },
    addToCart(item){
      this.$emit("add-to-cart", item)
    },
    goBack() {
      this.$emit("go-back");
    },
    onImageError(event) {
      event.target.src = 'restaurant.png';
    }
  },
  // mounted() {
  //   this.getFoods(this.restaurant)
  // }
};

  
</script>

- **`RestaurantPreviewCard.vue`**
  - **Mục đích**: Hiển thị thông tin tóm tắt về một nhà hàng.
  - **Nội dung**: Cung cấp giao diện cho danh sách nhà hàng.
  - Nội dung code: 
  <template>
  <div
    class="restaurant-card"
    @click="$emit('restaurant-clicked', restaurant.uuid)"
  >
    <div class="restaurant-card-item" :style="{'--delay': idx + 1}">
      <img :src="restaurant.image" @error="onImageError" class="restaurant-card-img-top rounded" alt="Restaurant Image" />
      <div class="restaurant-card-body">
        <h5 class="restaurant-card-title">{{ restaurant.name }}</h5>
        <p class="restaurant-card-text">
          <span v-if="restaurant.description.length > 30">
            {{ restaurant.description.substring(0, 30) }}... 
            <!-- <span class="show-more" @click.stop="showMore = true">Show more</span> -->
          </span>
          <span v-else>{{ restaurant.description }}</span>
          <span v-if="showMore">{{ restaurant.description.substring(100) }}</span>
        </p>
      </div>
    </div>
  </div>
</template>

<script>

export default {
  props: {
    restaurant: {
      type: Object,
      required: true,
    },
    idx: {
      type: Number,
      required: true,
    }
  },
  methods: {
    onImageError(event) {
      event.target.src = 'restaurant.png';
    }
  }
};
</script>

- **`RestaurantsContainer.vue`**
  - **Mục đích**: Quản lý danh sách các nhà hàng và hiển thị chúng.
  - **Nội dung**: Bao gồm các component cho xem trước và xem chi tiết nhà hàng.
  - <template>
  <div class="restaurants-container">
    <div v-if="!selectedRestaurant" class="row">
      <b class="sticky-top" style="background:white;">Restaurants nearby your address</b>
      <restaurant-preview-card
        v-for="(restaurant, index) in restaurants"
        :key="restaurant.uuid"
        :restaurant="restaurant"
        :idx="index"
        @restaurant-clicked="selectRestaurant"
        class="col-md-4 col-lg-3"
      ></restaurant-preview-card>
    </div>
    <restaurant-full-view
      ref="restaurantFullView"
      v-show="selectedRestaurant"
      :restaurant="selectedRestaurant"
      @go-back="deselectRestaurant"
      @add-to-cart="addToCart"
      @register-action="registerAction"
    ></restaurant-full-view>
  </div>
</template>

  <script>
  import RestaurantPreviewCard from "./RestaurantPreviewCard.vue";
  import RestaurantFullView from "./RestaurantFullView.vue"
  import "../styles/restaurants.css"
  import axios from "axios";

  export default {
    components: {
      RestaurantPreviewCard,
      RestaurantFullView
    },
    data() {
      return {
        selectedRestaurant: null,
        baseApiUrl: import.meta.env.VITE_APP_API_URL + "/api",
        restaurants: []
      };
    },
    methods: {
      registerAction(msg) {
        this.$emit("register-action", msg)
      },
      addToCart(item){
        this.$emit("add-to-cart", item)
      },
      selectRestaurant(uuid) {
        let flag_register_new_action = true;
        if (this.selectedRestaurant) {
          if (this.selectedRestaurant.uuid === uuid) {
            flag_register_new_action = false;
          }
        }

        this.selectedRestaurant = this.restaurants.find(
          (restaurant) => restaurant.uuid === uuid
        );
        if (this.$refs.restaurantFullView) {
          this.$refs.restaurantFullView.getFoods(this.selectedRestaurant);
        }
        if (flag_register_new_action){
          this.registerAction("opened the restaurant page " + this.selectedRestaurant.name + " with restaurant_uuid=" + this.selectedRestaurant.uuid)
        }
      },
      deselectRestaurant() {
        this.registerAction("closed the restaurant page " + this.selectedRestaurant.name)
        this.selectedRestaurant = null;
      },
      async getRestaurants() {
        try {
          const response = await axios.get(`${this.baseApiUrl}/restaurants`);
          this.restaurants = response.data.map(restaurant => ({
            name: restaurant.name,
            description: restaurant.description,
            uuid: restaurant.id,
            image: restaurant.image ? `${import.meta.env.VITE_APP_API_URL}${restaurant.image}` : null
          }));
        } catch (error) {
          console.error('Error fetching restaurants:', error);
        }
      },
    },
    mounted() {
      this.getRestaurants();
    },
  };
  </script>


#### 2.5. Thư mục `ShoppingCart/`
Chứa component cho giỏ hàng.

- **`ShoppingCart.vue`**
  - **Mục đích**: Hiển thị giỏ hàng của người dùng.
  - **Nội dung**: Quản lý danh sách món ăn trong giỏ hàng và tổng giá tiền.
  - Nội dung code: 
  <template>
  <div class="shopping-cart-container">
    <div class="shopping-cart-header sticky-top">
      <button class="close-btn" @click="closeFrame">
        <span>&times;</span>
      </button>
      <h3>Shopping Cart</h3>
      <hr/>
    </div>
    <div class="shopping-cart-body">
      <food-item
      v-for="(item, index) in shoppingCart"
      :key="index"
      :food="item"
      :view="'shoppingcart'"
      @remove-from-cart="removeFromCart"
      :class="{ 'fade-away': item.fadeAway }"
      @register-action="registerAction"
    ></food-item>
    </div>
    <div v-if="shoppingCart.length">
      <hr/>
      <div class="shopping-cart-footer sticky-bottom" v-if="!isLoading && !orderStatus">
        <p class="total-price-container">Total Price: {{ totalPrice.toFixed(2) }} $</p>
        <button @click="submitOrder" class="order-btn">Order</button>
      </div>
    </div>

    
    <div class="shopping-cart-footer sticky-bottom" v-if="isLoading || orderStatus" style="animation: fadeInSlideUp 0.5s ease forwards">
      <p>{{ this.placeholderMsg }}</p>
      <loading-spinner v-if="isLoading" ></loading-spinner>
    </div>
    
  </div>
</template>

  <script>
  import "../styles/shoppingcart.css"
  import FoodItem from "../Common/FoodItem.vue"
  import LoadingSpinner from "../Common/LoadingSpinner.vue"

  export default {
    components: {
      FoodItem,
      LoadingSpinner
    },    
    props: {
      shoppingCart: {
        type: Array,
        default: () => [],
      },
    },
    data() {
      return {
        isLoading: false,
        orderStatus: false,
        placeholderMsg: "Placing your order..."
      }
    },
    computed: {
      totalPrice() {
        return this.shoppingCart.reduce(
          (total, item) => total + item.price * item.quantity,
          0
        );
      },
    },
    methods: {
      registerAction(msg) {
        this.$emit("register-action", msg)
      },
      closeFrame() {
        this.$emit('close-cart')
        this.placeholderMsg = "Placing your order..."
        this.isLoading = false
        this.orderStatus = false
        
      },
      clearCart() {
        this.$emit('clear-shopping-cart')
      },
      removeFromCart(item) {
        this.$emit('remove-from-cart', item)
      },
      submitOrder() {
        this.isLoading = true;
        this.placeholderMsg = "Placing your order..."
        setTimeout(() => {
          this.isLoading = false;
          this.placeholderMsg = "Your order has been sent..."
          this.orderStatus = true // Sent order
          this.clearCart()
        }, 3000); // Simulate an API call with a 5-second delay
        this.placeholderMsg = "Place your order..."
        this.registerAction("placed the order as is")
      },
    // ...
    },
  };
  </script>

### 3. Tóm tắt
Folder `components` chứa tất cả các component cần thiết để xây dựng giao diện người dùng cho ứng dụng, bao gồm các phần liên quan đến chatbot, nhà hàng, giỏ hàng và các thành phần chung. Nếu bạn cần thêm thông tin chi tiết về bất kỳ file nào, hãy cho tôi biết nhé! 😊


Chắc chắn rồi! Dưới đây là mô tả chi tiết cấu trúc và nội dung của các file trong folder `styles` của project này 📁

### 1. Cấu trúc thư mục `styles`

```
frontend/src/components/styles/
├── chatbot.css
├── common.css
├── navbar.css
└── restaurants.css
└── shoppingcart.css
```

### 2. Chi tiết từng file

#### 2.1. `chatbot.css`
- **Mục đích**: Chứa các kiểu dáng cho giao diện chatbot.
- **Nội dung**:
  - Định nghĩa các kiểu cho container của chatbot, bao gồm vị trí, kích thước, và hiệu ứng chuyển động.
  - Các kiểu cho các thành phần như card chat, header, và các nút điều khiển.
  - Các hiệu ứng animation cho các trạng thái như "đang gõ" và "đang tải".
  - Nội dung code: 
  .chat-container {
  position: fixed;
  bottom: 2vh;
  right: 2vw;
  width: 30vw; 
  /* height:50vh; */
  z-index: 1050;
  border-radius: 20px 20px 0 0;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
  transition: height 0.3s ease, transform 0.3s ease;

}

.chat-container.minimized {
  height: 50px;
  min-height: 50px;
  max-height: 50px;
  width: 80vw; 
  max-width: 300px;
  bottom: 2vh;
  right: 2vw; 
  box-shadow: none!important;
  background-color: transparent!important;
  transform: translateY(calc(100% - 50px));
}

.card-chat {
  display: flex;
  flex-direction: column;
  height: 70vh;
  width: 100%;
  min-height: 70vh;
  max-height: 70vh;
  border-radius: 20px 20px 5px 5px !important;
  border: none!important;
  overflow: hidden;
  transition: all 0.5s ease; 
  animation: fadeInSlideUp 1.3s ease forwards;
}

.card-chat.minimized {
  height: 100%; 
  overflow: hidden; 
  border: 0!important;
}

.card-chat-header {
border-radius: 20px 20px 0 0!important;
border-bottom: 0.5px solid #9090907d !important;
background-color: #ebebeb;
}

.minimized-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.5rem 1rem; 
  background-color: #ebebeb; 
  cursor: pointer; 
}

.card-chat-header.minimized-header {
  padding: 5px;
  justify-content: center;
  border-radius: 20px 20px 0px 0px!important;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);

}

.card-chat-footer {
border-radius: 0 0 10px 10px !important;
background-color: transparent;
border-top:  1px solid #3737373b !important;
padding-bottom: 15px;
}
  

.card-chat-body {
overflow-y: auto;
flex-grow:1;
background: #ffffff;
}

.card-chat-body, .card-chat-footer {
  transition: opacity 0.5s ease;
}
  
.msg_head {
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
}

.user-message {
animation: fadeInSlideUp 0.3s ease forwards;
}

.container-assistant-msg {
position: relative;
margin-top: auto;
margin-bottom: auto;
margin-left: 10px;
border-radius: 18px 18px 18px 0px;
color: #212529;
background-color: #efefef;
padding: 10px;
padding-left: 20px;
padding-right: 20px;
position: relative;
font-size: 14px;
display: flex;
flex-direction: column;
max-width: 75%;
}

.container-user-msg {
margin-top: auto;
margin-bottom: auto;
margin-right: 10px;
border-radius: 18px 18px 0px 18px;
background: linear-gradient(to top right, rgba(188,0,0,255), rgba(201,39,93,255));
padding: 10px;
padding-left: 20px;
padding-right: 20px;
position: relative;
color:#ffffff;
font-size: 14px;
max-width: 75%;
}

.btn-minimize {
  border: none;
  background: transparent;
  cursor: pointer;
  padding: 0;
  margin: 0;
  line-height: 1;
  width:2rem;
  height:2rem;
}

.btn-minimize img {
  width: 1.3rem; 
  height: 1.3rem; 
  -webkit-filter: invert(1.0);
          filter: invert(1);
}

.btn-minimize img:hover {
  background-color: rgba(0, 0, 0, 0.4);
  border-radius: 10%;
  background-size: 2rem;
}

.user_info_maximized {
  margin-left: 2vw;
  margin-top: auto;
  margin-bottom: auto;
  font-size: 14px!important;
  color: #ffffff;
}

.user_info_minimized {
  margin-left:2vw;
  margin-top: auto;
  margin-bottom: auto;
  font-size: 12px!important;
  color: #ffffff;
}

.audio-player {
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2; 
}

.audio-control {
  width: 20px; 
  height: 20px; 
  cursor: pointer;
  border-radius: 50%;
  transition: all 0.3s ease;
  z-index: 3;
  margin-left:10px;

}

.audio-control.loading {
  animation: spin 2s linear infinite;
}

.audio-control.playing {
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

#### 2.2. `common.css`
- **Mục đích**: Chứa các kiểu dáng chung cho toàn bộ ứng dụng.
- **Nội dung**:
  - Các kiểu cho layout chung, bao gồm flexbox và các hiệu ứng animation.
  - Định nghĩa các kiểu cho các thành phần như loader, tin nhắn đang gõ, và các hiệu ứng fade-in.
  - Các kiểu cho các thành phần SVG và các hiệu ứng chuyển động.
  - Nội dung code: 
  /* Styles */

.app-container {
  display: flex;
  flex-wrap: nowrap;
}

.svg-loader{
  display:flex;
  position: relative;
  align-content: space-around;
  justify-content: center;
  height: 20px;
  width: 20px;
  animation: fadeInSlideRight 0.6s ease forwards;
}

.loader-svg{
  position: absolute;
  left: 0; right: 0; top: 0; bottom: 0;
  fill: none;
  stroke-width: 5px;
  stroke-linecap: round;
  stroke: rgb(64, 0, 148);
}
.loader-svg.bg{
  stroke-width: 8px;
  stroke: rgb(207, 205, 245);
}

.animate{
  stroke-dasharray: 242.6;
  animation: fill-animation 1s cubic-bezier(1,1,1,1) 0s infinite;
}

.typing-load-msg {
  margin: auto 0;
  width: auto;
  text-align: center;
}

.fadeInSlideRight {
  animation: fadeInSlideRight 0.6s ease forwards;
}

.card-file {
  display: flex;
  flex-direction: column;
  height: 70vh;
  min-height: 70vh;
  max-height: 70vh;
  border-radius: 10px !important;
  border: none;
  border: 1px solid #0000002d;
  animation: fadeInSlideUp 1.3s ease forwards;
  animation-delay: 0.4s;
  animation-fill-mode: both;
}

.card-file-header {
  border-radius: 10px 10px 0 0 !important;
  background-color: #ebebeb;
}

.file-header {
  font-size: 12px;
  font-weight: bold;
  color: #ffffff;
  margin-left:15px;
  left:10px;
  bottom:2px;
}

.input-btn {
  right: 5%;
  left: 5%;
  background-color: rgba(0, 0, 0, 0.36) !important;
  border-radius: 7px 7px !important;
  width:20px;
  height:20px;
  top: 0;
  margin-top: auto;
  color: white;
  bottom: 0;
  margin-left: 5px;
  margin-right: 5px;
  font-weight: bold;
  display: flex;
  padding-right: 2px;
  margin-bottom: auto;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  animation: fadeInSlideRight 0.4s ease forwards;
}

.input-btn:hover {
  background-color: rgba(0, 0, 0, 0.16) !important;
}

.input-group {
  position: relative;
  background-color: rgba(0, 0, 0, 0.1);
  border-radius: 5px;
}

.input-group textarea {
  background-color: rgba(0,0,0,0.05);
  border-radius:5px;
  animation: fadeInSlideUp 0.4s ease forwards;
  /* height: 100%; */
}

.input-group textarea:focus{
  background-color: rgba(0,0,0,0.10);
}

.input-group .recording-input {
  background-color: rgba(0,0,0,0.10);
  border-radius:5px;
  animation: fadeInSlideUp 0.4s ease forwards;
}

.input-group-append {
  display: flex;
}

.type_msg {
  background-color: transparent;
  border-radius: 16px;
  border: 0 !important;
  color: #5e5e5e;
  overflow-y: auto;
  padding: 15px;
}

.type_msg:focus {
  background-color: transparent;
  box-shadow: none !important;
}

.user_img {
  height: 40px;
  width: 40px;

}

.user_img_msg {
  height: 40px;
  width: 40px;

}

.img_cont {
  position: relative;
  height: 50px;
  width: 50px;
  background-color: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50% !important;
}


.online_icon {
  position: absolute;
  height: 15px;
  width: 15px;
  background-color: #4cd137;
  border-radius: 50%;
  bottom: 0.0em;
  right: -0.1rem;
  border: 1.5px solid white;
}

.user_info {
  margin-top: auto;
  margin-bottom: auto;
  margin-left: 15px;
}

.user_info span {
  font-size: 16px;
  font-weight: bold;
  color: #ffffff;
}

.user_info p {
  font-size: 10px;
  color: rgba(20, 20, 20, 0.6);
}

.card-header{
  color:white;
  font-style: bold;
  border-radius: 10px;
  border: none;
  background: linear-gradient(to top right, rgba(188,0,0,255),  rgba(234,10,68,255));
}


.frame {
  position: absolute;
  background-color: #f9f9f9;
  max-width: 270px;
  box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
  padding: 12px 16px;
  z-index: 1;
  left: 0;
  bottom: -50%;
  white-space: nowrap;
  border-radius: 5%;
  max-height: 330px;
  overflow-y: auto;
  animation: fadeInSlideLeft 0.3s ease forwards;
}


.button-container {
  display: flex;
  justify-content: space-between;
  margin-left:5px;
}

.loading-spinner-container {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
  width: 100%;
  animation: fadeInSlideUp 0.3s ease forwards;
}

.loading-spinner {
  border: 4px solid rgba(0, 0, 0, 0.1);
  border-radius: 50%;
  border-left-color: #09f;
  animation: spin 1s linear infinite;
}



/* Animations */
@keyframes spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

@keyframes fadeOutSlideDown {
  0% {
    opacity: 1;
    transform: translateY(0);
  }
  100% {
    opacity: 0;
    transform: translateY(10px);
  }
}

@keyframes waitFadeInSlideUp {
  0%, 30% {
    opacity: 0;
    transform: translateY(20px);
  }
  100% {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes fadeInSlideUp {
  0% {
    opacity: 0;
    transform: translateY(10px);
  }
  100% {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes fadeInSlideDown {
  0% {
    opacity: 0;
    transform: translateY(-10px);
  }
  100% {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes fadeInSlideRight {
  0% {
    opacity: 0;
    transform: translateX(-10px);
  }
  100% {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes fadeInSlideLeft {
  0% {
    opacity: 0;
    transform: translateX(10px);
  }
  100% {
    opacity: 1;
    transform: translateX(0);
  }
}

@-webkit-keyframes sk-bouncedelay {
  0%, 80%, 100% {
    -webkit-transform: scale(0)
  }
  40% {
    -webkit-transform: scale(1.0)
  }
}

@keyframes sk-bouncedelay {
  0%, 80%, 100% {
    -webkit-transform: scale(0);
    transform: scale(0);
  }
  40% {
    -webkit-transform: scale(1.0);
    transform: scale(1.0);
  }
}

  
@keyframes fill-animation{
  0%{
    stroke-dasharray: 40 242.6;
    stroke-dashoffset: 0;
  }
  50%{
    stroke-dasharray: 141.3;
    stroke-dashoffset: -141.3;
  }
  100%{
    stroke-dasharray: 40 242.6;
    stroke-dashoffset: -282.6;
  }
}


@keyframes fadeAway {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}

#### 2.3. `navbar.css`
- **Mục đích**: Chứa các kiểu dáng cho thanh điều hướng (navbar).
- **Nội dung**:
  - Định nghĩa các kiểu cho navbar, bao gồm màu nền, vị trí, và kiểu chữ.
  - Các kiểu cho logo và tiêu đề trong navbar.
  - Các hiệu ứng hover cho các thành phần trong navbar.
  - Nội dung code: 
  .navbar {
    display: flex;
    justify-content: space-between;
    background: linear-gradient(to bottom, rgba(255, 21, 21, 0.232), rgb(255,255,255));
    position:relative;
    max-height:20%;
    border-bottom: 1px solid #e5e6ea;
}

.nav {
    background: transparent;
    border-bottom: none;
}

.navbar-logo {
    height: 5vh;
    margin-left: 5vh;
}

.navbar-title{
    margin-top: 1.5vh;
    margin-left: 10px;
    font-size: 2vh;
    color:rgba(0, 0, 0, 0.5)
}


#### 2.4. `restaurants.css`
- **Mục đích**: Chứa các kiểu dáng cho giao diện danh sách nhà hàng.
- **Nội dung**:
  - Định nghĩa các kiểu cho container của danh sách nhà hàng, bao gồm padding, margin và chiều cao.
  - Các kiểu cho các thẻ card hiển thị thông tin nhà hàng, bao gồm hiệu ứng hover và animation.
  - Các kiểu cho tiêu đề và mô tả của nhà hàng.
  - Nội dung code: 
  .restaurants-container {
  flex: 1; 
  margin-left: 5vh;
  padding-left: 10px;
  padding-right: 10px;
  height: 80vh;
  overflow-y: auto;
  overflow-x:hidden
}

.restaurants-container .row{
  margin-right: 5vw!important;
}


.restaurant-card {
  padding: 0px;
  margin-bottom: 5vh; 
  animation: fadeInSlideUp 1.3s ease forwards;
}

.restaurant-card-item {
  height: auto;
  padding-bottom: 10px;
  border-radius: 10px;
  box-shadow: 0 4px 7px rgba(0, 0, 0, 0.1);
  transition: box-shadow 0.4s ease;
  animation: fadeInSlideUp 0.3s ease forwards;
  animation-delay: calc((var(--delay) + 1) * 0.1s);
  animation-fill-mode: both;
}

.restaurant-card-item:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
cursor: pointer;
}


.restaurant-card-title {
text-decoration: underline ;
font-family: 'Roboto', sans-serif;
font-style: italic;
font-size: 14px; 
font-weight: 500;
margin-top:10%; 
margin-left: 10%;
margin-bottom: 0.5rem; 
}

.restaurant-card-text {
font-size: 12px; 
margin-left: 10%;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;  
overflow: hidden;
text-overflow: ellipsis;
}


/* Full view restaurant */
.restaurant-full-view {
  height: 65vh;
  position: relative;
  flex: 1;
  margin-top: 2vh;
  margin-right: 35vw!important;
  margin-left: 5vh;
  padding-left: 10px;
  padding-right: 10px;
  animation: fadeInSlideUp 0.4s ease forwards;
}

.go-back-btn {
  position: absolute;
  top: 0;
  right: 0;
  background: linear-gradient(to top right, rgba(188, 0, 0, 255), rgba(201, 39, 93, 255));
  color: #fff;
  border: none;
  padding: 8px 16px;
  border-radius: 4px;
  cursor: pointer;
}

.go-back-btn:hover {
  background: linear-gradient(to top right, rgba(201, 39, 93, 255), rgba(188, 0, 0, 255));
}

.restaurant-card-img-top.rounded {
  max-width: 150px; 
  width:100%;
  object-fit: cover;
  border-radius: 10px;
  display: block;
  margin: 0 auto; 
  margin-top:5%;
  height: 150px;
  width: 100%;
  object-fit: cover;
}

.restaurant-card-img-fullview.rounded {
  width: 100%;
  max-width: 250px;
  height: auto;
  width:auto;
  object-fit: cover;
  border-radius: 10px;
  display: block;
  margin: 0 auto;
  height: 150px;
  width:200px;
  object-fit: cover;
}

.description-container {
  background-color: #f5f5f5; 
  border-radius: 10px; 
  padding: 16px; 
  margin-top: 16px; 
}

.description-container p {
  font-size: 16px; 
  line-height: 1.5; 
  margin: 0; 
}

/*  Food */
.food-list {
  max-height: 45vh; 
  overflow-y: auto; 
  margin-top: 16px; 
}

.food-item {
  display: flex;
  align-items: center;
  margin-bottom: 16px;
}

.food-img {
  width: 80px;
  height: 80px;
  object-fit: cover;
  border-radius: 10px;
  margin-right: 16px;
}

.food-details {
  flex-grow: 1;
}

.food-description {
  color: #555; 
}

.food-actions {
  display: flex;
  flex-direction: column; 
  align-items: flex-end;
  margin-right: 10px;
}

.add-to-cart-btn {
  background: linear-gradient(to top right, rgba(188, 0, 0, 255), rgba(201, 39, 93, 255));
  color: #fff;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  margin-top: 8px;
  margin-right:5px;
}

.add-to-cart-btn:hover {
  background: linear-gradient(to top right, rgba(201, 39, 93, 255), rgba(188, 0, 0, 255));
}

.quantity-control {
  display: flex;
  align-items: center;
}

.quantity-btn {
  background: linear-gradient(to top right, rgba(188, 0, 0, 255), rgba(201, 39, 93, 255));
  color: #fff;
  border: none;
  padding: 2px 4px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 16px;
  font-weight: bold;
  width: 24px;
  height: 24px;
  display: flex;
  justify-content: center;
  align-items: center;
  margin-left:3px;
}

.quantity-input {
  width: 34px;
  border: none;
  border-radius: 4px;
  background-color: #f5f5f5;
  color: #333;
  text-align: center;
  margin: 0 4px;
  font-size:7;
}

.restaurant-name {
  font-size: 12px;
  color: #555; 
}

.show-more {
  color: blue;
  cursor: pointer;
}



#### 2.5. `shoppingcart.css`
- **Mục đích**: Chứa các kiểu dáng cho giao diện giỏ hàng.
- **Nội dung**:
  - Định nghĩa các kiểu cho container của giỏ hàng, bao gồm vị trí, kích thước và hiệu ứng animation.
  - Các kiểu cho các thành phần trong giỏ hàng như nút đóng, tiêu đề và tổng giá tiền.
  - Các hiệu ứng hover cho các nút và các thành phần trong giỏ hàng.
  - Nội dung code: 
  .shopping-cart {
    position: relative;
    cursor: pointer;
    padding: 2px;
    border-radius:15%;
    margin-right: 10px;
}

.shopping-cart-logo {
    display: block;
    border-radius: 25%;
    height: 3vh;
    width: 3vh;
    margin-left: 1.6vh;
    margin-right: 1.6vh;
}

.shopping-cart:hover {
    background: rgba(0,0,0,0.15);
}
  

.cart-item-count {
    position: relative;
    top: 6px;
    right: -12px;
    background-color: rgba(201,39,93,255);
    color: white;
    border-radius: 50%;
    width: 15px;
    height: 15px;
    font-size: 10px;
    display: flex;
    justify-content: center;
    align-items: center;
}

.shopping-cart-container {
    position: fixed;
    top: 3vh; 
    right: 4vw;
    z-index: 9999;
    width: 30vw;
    background-color: #fff;
    border-radius: 10px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
    padding: 16px;
    animation: fadeInSlideUp 0.3s ease forwards;
    max-height:40vh;
}

.close-btn {
    position: absolute;
    top: 4px;
    right: 4px;
    background: linear-gradient(to top right, rgba(188,0,0,255), rgba(201,39,93,255));
    color: #fff;
    border: none;
    border-radius: 50%;
    width: 18px;
    height: 18px;
    font-size: 10px;
    line-height: 10px;
    text-align: center;
    cursor: pointer;
}

.close-btn:hover {
    background: rgba(188,0,0,0.75);
}

.order-btn {
    background: linear-gradient(to top right, rgba(188,0,0,255), rgba(201,39,93,255));
    color: #fff;
    border: none;
    padding: 8px 16px;
    border-radius: 4px;
    cursor: pointer;
    margin-top: 16px;
}

.order-btn:hover{
    background: rgba(188,0,0,0.75);
}

.total-price-container {
    font-weight: bold;
    font-size: 18px;
    margin-top: 16px;
  }


.shopping-cart-header {
    background: white;
}

.shopping-cart-body{
    overflow-y: auto;
    overflow-x: hidden;
    max-height: 20vh;
}

.shopping-cart-footer {
    display: flex;
    background: white;
    justify-content: space-between;
    align-items: center;
    font-weight: bold;
    font-size: 18px;
    margin-top: 16px;
}

  
.fade-away {
    animation: fadeAway 0.5s forwards;
}


### 3. Tóm tắt
Folder `styles` chứa tất cả các file CSS cần thiết để định dạng giao diện người dùng cho ứng dụng, bao gồm các kiểu cho chatbot, navbar, danh sách nhà hàng và giỏ hàng. Nếu bạn cần thêm thông tin chi tiết về bất kỳ file nào, hãy cho tôi biết nhé! 😊




batchfile
bootstrap
bun
css
fastapi
golang
google-cloud
huggingface
+16 more
nguyenlykhanhlinh/LLM-Food-Delivery-Agent

Used in 1 repository