corrected FRONTEND, docker compose, mcp server
This commit is contained in:
parent
f2123a5779
commit
d400215596
@ -40,10 +40,10 @@ async def chat(request: Request):
|
|||||||
|
|
||||||
async def stream():
|
async def stream():
|
||||||
async for token in stream_response(agent, messages):
|
async for token in stream_response(agent, messages):
|
||||||
chunk = json.dumps({"type": "text", "delta": token})
|
chunk = json.dumps({"type": "text-delta", "textDelta": token})
|
||||||
yield f"data: {chunk}\n\n"
|
yield f"data: {chunk}\n\n"
|
||||||
yield "data: [DONE]\n\n"
|
yield "data: [DONE]\n\n"
|
||||||
|
|
||||||
return StreamingResponse(
|
return StreamingResponse(
|
||||||
stream(),
|
stream(),
|
||||||
media_type="text/event-stream",
|
media_type="text/event-stream",
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import httpx
|
|||||||
import json
|
import json
|
||||||
from fastmcp import FastMCP
|
from fastmcp import FastMCP
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from pydantic import Field
|
|
||||||
|
|
||||||
JUSTICE_API = "https://obcan.justice.sk/pilot/api/ress-isu-service"
|
JUSTICE_API = "https://obcan.justice.sk/pilot/api/ress-isu-service"
|
||||||
HEADERS = {
|
HEADERS = {
|
||||||
@ -29,7 +28,7 @@ def register_judge_tools(mcp: FastMCP):
|
|||||||
async def judge_autocomplete(
|
async def judge_autocomplete(
|
||||||
query: str,
|
query: str,
|
||||||
court_id: Optional[str] = None,
|
court_id: Optional[str] = None,
|
||||||
limit: int = Field(default=10, description="Maximálny počet výsledkov"),
|
limit: int = 10,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
Autocomplete pre mená sudcov — použiť AKO PRVÝ krok
|
Autocomplete pre mená sudcov — použiť AKO PRVÝ krok
|
||||||
@ -48,8 +47,8 @@ def register_judge_tools(mcp: FastMCP):
|
|||||||
kraj: Optional[str] = None,
|
kraj: Optional[str] = None,
|
||||||
court_id: Optional[str] = None,
|
court_id: Optional[str] = None,
|
||||||
status: Optional[str] = None,
|
status: Optional[str] = None,
|
||||||
page: int = Field(default=0, description="Číslo stránky (začína od 0)"),
|
page: int = 0,
|
||||||
size: int = Field(default=20, description="Počet výsledkov na stránku"),
|
size: int = 20,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
Vyhľadávanie sudcov s filtrami.
|
Vyhľadávanie sudcov s filtrami.
|
||||||
|
|||||||
@ -16,7 +16,7 @@ services:
|
|||||||
backend:
|
backend:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: /backend/Dockerfile
|
dockerfile: backend/Dockerfile
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "8000:8000"
|
- "8000:8000"
|
||||||
@ -40,9 +40,6 @@ services:
|
|||||||
- "4000:4000"
|
- "4000:4000"
|
||||||
env_file:
|
env_file:
|
||||||
- backend/.env
|
- backend/.env
|
||||||
environment:
|
|
||||||
- GROQ_API_KEY=${GROQ_API_KEY}
|
|
||||||
- GEMINI_API_KEY=${GEMINI_API_KEY}
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./backend/config.yaml:/app/config.yaml:ro
|
- ./backend/config.yaml:/app/config.yaml:ro
|
||||||
command: ['--config', '/app/config.yaml', '--port', '4000']
|
command: ['--config', '/app/config.yaml', '--port', '4000']
|
||||||
@ -50,7 +47,7 @@ services:
|
|||||||
mcp:
|
mcp:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: /backend/mcp_server/Dockerfile
|
dockerfile: backend/mcp_server/Dockerfile
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "8001:8001"
|
- "8001:8001"
|
||||||
|
|||||||
@ -1,19 +0,0 @@
|
|||||||
export async function POST(req: Request) {
|
|
||||||
const body = await req.json();
|
|
||||||
|
|
||||||
const response = await fetch("http://backend:8000/api/chat", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify(body),
|
|
||||||
});
|
|
||||||
|
|
||||||
return new Response(response.body, {
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "text/event-stream",
|
|
||||||
"Cache-Control": "no-cache",
|
|
||||||
"X-Accel-Buffering": "no",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@ -67,31 +67,7 @@
|
|||||||
--ring: oklch(0.5 0.15 240);
|
--ring: oklch(0.5 0.15 240);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
|
||||||
--background: oklch(0.12 0.04 240);
|
|
||||||
--foreground: oklch(0.95 0.01 240);
|
|
||||||
--card: oklch(0.17 0.05 240);
|
|
||||||
--card-foreground: oklch(0.95 0.01 240);
|
|
||||||
--popover: oklch(0.17 0.05 240);
|
|
||||||
--popover-foreground: oklch(0.95 0.01 240);
|
|
||||||
--primary: oklch(0.6 0.18 240);
|
|
||||||
--primary-foreground: oklch(0.98 0 0);
|
|
||||||
--secondary: oklch(0.2 0.05 240);
|
|
||||||
--secondary-foreground: oklch(0.95 0.01 240);
|
|
||||||
--muted: oklch(0.2 0.05 240);
|
|
||||||
--muted-foreground: oklch(0.65 0.05 240);
|
|
||||||
--accent: oklch(0.22 0.06 240);
|
|
||||||
--accent-foreground: oklch(0.95 0.01 240);
|
|
||||||
--destructive: oklch(0.704 0.191 22.216);
|
|
||||||
--border: oklch(1 0 0 / 8%);
|
|
||||||
--input: oklch(1 0 0 / 12%);
|
|
||||||
--ring: oklch(0.5 0.15 240);
|
|
||||||
--chart-1: oklch(0.488 0.243 264.376);
|
|
||||||
--chart-2: oklch(0.696 0.17 162.48);
|
|
||||||
--chart-3: oklch(0.769 0.188 70.08);
|
|
||||||
--chart-4: oklch(0.627 0.265 303.9);
|
|
||||||
--chart-5: oklch(0.645 0.246 16.439);
|
|
||||||
}
|
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
* {
|
* {
|
||||||
|
|||||||
@ -14,8 +14,7 @@ const geistMono = Geist_Mono({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "assistant-ui starter app",
|
title: "Legal AI Assistant",
|
||||||
description: "Generated by create-assistant-ui",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
@ -24,7 +23,7 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" className="dark">
|
<html lang="en" className="root">
|
||||||
<body
|
<body
|
||||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -16,10 +16,6 @@ export const ThreadHeader: FC = () => {
|
|||||||
</span>
|
</span>
|
||||||
<ChevronDownIcon className="size-4 text-muted-foreground" />
|
<ChevronDownIcon className="size-4 text-muted-foreground" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="ml-auto p-1.5">
|
|
||||||
<ScaleIcon className="size-5 text-blue-400" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -57,9 +57,6 @@ export const Thread: FC = () => {
|
|||||||
<ThreadPrimitive.ViewportFooter className="sticky bottom-0 mx-auto mt-auto flex w-full max-w-(--thread-max-width) flex-col gap-4 overflow-visible rounded-t-2xl bg-background pb-4 md:pb-6 pt-2">
|
<ThreadPrimitive.ViewportFooter className="sticky bottom-0 mx-auto mt-auto flex w-full max-w-(--thread-max-width) flex-col gap-4 overflow-visible rounded-t-2xl bg-background pb-4 md:pb-6 pt-2">
|
||||||
<ThreadScrollToBottom />
|
<ThreadScrollToBottom />
|
||||||
<ThreadComposer />
|
<ThreadComposer />
|
||||||
<p className="text-center text-xs text-muted-foreground/50 pb-1">
|
|
||||||
Právny AI Asistent môže robiť chyby. Overte dôležité informácie.
|
|
||||||
</p>
|
|
||||||
</ThreadPrimitive.ViewportFooter>
|
</ThreadPrimitive.ViewportFooter>
|
||||||
</ThreadPrimitive.Viewport>
|
</ThreadPrimitive.Viewport>
|
||||||
</ThreadPrimitive.Root>
|
</ThreadPrimitive.Root>
|
||||||
|
|||||||
@ -2,30 +2,22 @@
|
|||||||
|
|
||||||
import { type FC } from "react";
|
import { type FC } from "react";
|
||||||
|
|
||||||
const SUGGESTIONS = [
|
const STARTERS = [
|
||||||
{
|
{
|
||||||
icon: "🔍",
|
icon: "/icons/magnifying-glass.svg",
|
||||||
title: "Aké právne dáta",
|
description: "What legal data can the agent find?",
|
||||||
description: "môže agent nájsť?",
|
|
||||||
prompt: "Aké právne dáta môžeš vyhľadať?",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "🚫",
|
icon: "/icons/ban.svg",
|
||||||
title: "Čo agent nesmie",
|
description: "What are the agent's limitations?",
|
||||||
description: "robiť alebo používať?",
|
|
||||||
prompt: "Čo nie si oprávnený robiť alebo použiť?",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "🤖",
|
icon: "/icons/hexagon.svg",
|
||||||
title: "Detaily AI modelu",
|
description: "What are the details of your AI model?",
|
||||||
description: "aký model používaš?",
|
|
||||||
prompt: "Aké sú detaily tvojho AI modelu?",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "📊",
|
icon: "/icons/database.svg",
|
||||||
title: "Zdroje dát",
|
description: "What are your data sources?",
|
||||||
description: "odkiaľ čerpáš informácie?",
|
|
||||||
prompt: "Aké dátové zdroje agent využíva?",
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -47,16 +39,22 @@ const ThreadSuggestions: FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid w-full grid-cols-2 @lg:grid-cols-4 gap-2 pb-4 px-2">
|
<div className="grid w-full grid-cols-2 @lg:grid-cols-4 gap-2 pb-4 px-2">
|
||||||
{SUGGESTIONS.map((s) => (
|
{STARTERS.map((s) => (
|
||||||
<button
|
<button
|
||||||
key={s.title}
|
key={s.description}
|
||||||
onClick={() => handleClick(s.prompt)}
|
onClick={() => handleClick(s.description)}
|
||||||
className="flex flex-col items-start gap-1 rounded-xl border border-blue-500/20 bg-card px-4 py-3 text-left text-sm transition-colors hover:bg-blue-600/10 hover:border-blue-500/40 cursor-pointer"
|
className="flex flex-row items-center gap-1 rounded-xl border border-blue-500/20 bg-card px-4 py-3 text-left text-sm transition-colors hover:bg-blue-600/10 hover:border-blue-500/40 cursor-pointer"
|
||||||
>
|
>
|
||||||
<span className="font-medium text-foreground">
|
<div className="size-5 flex-shrink-0">
|
||||||
{s.icon} {s.title}
|
<img
|
||||||
|
src={s.icon}
|
||||||
|
alt=""
|
||||||
|
className="size-full object-contain group-hover:scale-110 transition-transform"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span className="text-muted-foreground group-hover:text-foreground transition-colors leading-tight">
|
||||||
|
{s.description}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-muted-foreground text-xs">{s.description}</span>
|
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@ -65,17 +63,13 @@ const ThreadSuggestions: FC = () => {
|
|||||||
|
|
||||||
export const ThreadWelcome: FC = () => {
|
export const ThreadWelcome: FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto my-auto flex w-full max-w-(--thread-max-width) grow flex-col items-center justify-center gap-6">
|
<div className="mx-auto my-auto flex w-full max-w-(--thread-max-width) grow flex-col items-center justify-end gap-6">
|
||||||
<div className="flex flex-col items-center gap-4">
|
<div className="flex flex-col items-center gap-4">
|
||||||
<div className="size-24 rounded-full overflow-hidden ring-2 ring-blue-500/30 shadow-lg shadow-blue-500/20">
|
<div className=" rounded-full overflow-hidden ring-2 ring-blue-500/30 shadow-lg shadow-blue-500/20">
|
||||||
<img
|
<img
|
||||||
src="/logo.png"
|
src="/logo.png"
|
||||||
alt="Legal AI"
|
alt="Legal AI"
|
||||||
className="size-full object-cover"
|
className="w-50 h-50 object-cover"
|
||||||
onError={(e) => {
|
|
||||||
e.currentTarget.style.display = "none";
|
|
||||||
e.currentTarget.parentElement!.innerHTML = `<div class="size-full bg-blue-600/20 flex items-center justify-center text-4xl">⚖️</div>`;
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-center gap-1 text-center">
|
<div className="flex flex-col items-center gap-1 text-center">
|
||||||
|
|||||||
25
frontend/package-lock.json
generated
25
frontend/package-lock.json
generated
@ -9,8 +9,9 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/openai": "^3.0.52",
|
"@ai-sdk/openai": "^3.0.52",
|
||||||
"@assistant-ui/react": "latest",
|
"@assistant-ui/react": "^0.12.25",
|
||||||
"@assistant-ui/react-ai-sdk": "latest",
|
"@assistant-ui/react-ai-sdk": "latest",
|
||||||
|
"@assistant-ui/react-data-stream": "^0.12.11",
|
||||||
"@assistant-ui/react-markdown": "latest",
|
"@assistant-ui/react-markdown": "latest",
|
||||||
"@radix-ui/react-avatar": "^1.1.11",
|
"@radix-ui/react-avatar": "^1.1.11",
|
||||||
"@radix-ui/react-collapsible": "^1.1.12",
|
"@radix-ui/react-collapsible": "^1.1.12",
|
||||||
@ -227,6 +228,27 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@assistant-ui/react-data-stream": {
|
||||||
|
"version": "0.12.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@assistant-ui/react-data-stream/-/react-data-stream-0.12.11.tgz",
|
||||||
|
"integrity": "sha512-DuCV1ILVJsJjl746Ly50jsyt1gUVp43E02Fbq3uau4LU9p3MhUP5CeCV0LLkdS80Hlu9X6xXc34tsL5BCyVzow==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@ai-sdk/provider": "^3.0.8",
|
||||||
|
"@assistant-ui/core": "^0.1.14",
|
||||||
|
"assistant-cloud": "*",
|
||||||
|
"assistant-stream": "^0.3.11"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^18 || ^19"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@assistant-ui/react-markdown": {
|
"node_modules/@assistant-ui/react-markdown": {
|
||||||
"version": "0.12.9",
|
"version": "0.12.9",
|
||||||
"resolved": "https://registry.npmjs.org/@assistant-ui/react-markdown/-/react-markdown-0.12.9.tgz",
|
"resolved": "https://registry.npmjs.org/@assistant-ui/react-markdown/-/react-markdown-0.12.9.tgz",
|
||||||
@ -7716,7 +7738,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.12.tgz",
|
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.12.tgz",
|
||||||
"integrity": "sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==",
|
"integrity": "sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.20.0"
|
"node": ">=12.20.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -13,8 +13,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/openai": "^3.0.52",
|
"@ai-sdk/openai": "^3.0.52",
|
||||||
"@assistant-ui/react": "latest",
|
"@assistant-ui/react": "^0.12.25",
|
||||||
"@assistant-ui/react-ai-sdk": "latest",
|
"@assistant-ui/react-ai-sdk": "latest",
|
||||||
|
"@assistant-ui/react-data-stream": "^0.12.11",
|
||||||
"@assistant-ui/react-markdown": "latest",
|
"@assistant-ui/react-markdown": "latest",
|
||||||
"@radix-ui/react-avatar": "^1.1.11",
|
"@radix-ui/react-avatar": "^1.1.11",
|
||||||
"@radix-ui/react-collapsible": "^1.1.12",
|
"@radix-ui/react-collapsible": "^1.1.12",
|
||||||
|
|||||||
1
frontend/public/icons/ban.svg
Normal file
1
frontend/public/icons/ban.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path fill="rgb(255, 5, 30)" d="M431.2 476.5L163.5 208.8C141.1 240.2 128 278.6 128 320C128 426 214 512 320 512C361.5 512 399.9 498.9 431.2 476.5zM476.5 431.2C498.9 399.8 512 361.4 512 320C512 214 426 128 320 128C278.5 128 240.1 141.1 208.8 163.5L476.5 431.2zM64 320C64 178.6 178.6 64 320 64C461.4 64 576 178.6 576 320C576 461.4 461.4 576 320 576C178.6 576 64 461.4 64 320z"/></svg>
|
||||||
|
After Width: | Height: | Size: 594 B |
1
frontend/public/icons/database.svg
Normal file
1
frontend/public/icons/database.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path fill="rgb(45, 53, 196)" d="M544 269.8C529.2 279.6 512.2 287.5 494.5 293.8C447.5 310.6 385.8 320 320 320C254.2 320 192.4 310.5 145.5 293.8C127.9 287.5 110.8 279.6 96 269.8L96 352C96 396.2 196.3 432 320 432C443.7 432 544 396.2 544 352L544 269.8zM544 192L544 144C544 99.8 443.7 64 320 64C196.3 64 96 99.8 96 144L96 192C96 236.2 196.3 272 320 272C443.7 272 544 236.2 544 192zM494.5 453.8C447.6 470.5 385.9 480 320 480C254.1 480 192.4 470.5 145.5 453.8C127.9 447.5 110.8 439.6 96 429.8L96 496C96 540.2 196.3 576 320 576C443.7 576 544 540.2 544 496L544 429.8C529.2 439.6 512.2 447.5 494.5 453.8z"/></svg>
|
||||||
|
After Width: | Height: | Size: 817 B |
1
frontend/public/icons/hexagon.svg
Normal file
1
frontend/public/icons/hexagon.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path fill="rgb(177, 151, 252)" d="M344 170.6C362.9 161.6 376 142.3 376 120C376 89.1 350.9 64 320 64C289.1 64 264 89.1 264 120C264 142.3 277.1 161.6 296 170.6L296 269.4C293.2 270.7 290.5 272.3 288 274.1L207.9 228.3C209.5 207.5 199.3 186.7 180 175.5C153.2 160 119 169.2 103.5 196C88 222.8 97.2 257 124 272.5C125.3 273.3 126.6 274 128 274.6L128 365.4C126.7 366 125.3 366.7 124 367.5C97.2 383 88 417.2 103.5 444C119 470.8 153.2 480 180 464.5C199.3 453.4 209.4 432.5 207.8 411.7L258.3 382.8C246.8 371.6 238.4 357.2 234.5 341.1L184 370.1C181.4 368.3 178.8 366.8 176 365.4L176 274.6C178.8 273.3 181.5 271.7 184 269.9L264.1 315.7C264 317.1 263.9 318.5 263.9 320C263.9 342.3 277 361.6 295.9 370.6L295.9 469.4C277 478.4 263.9 497.7 263.9 520C263.9 550.9 289 576 319.9 576C350.8 576 375.9 550.9 375.9 520C375.9 497.7 362.8 478.4 343.9 469.4L343.9 370.6C346.7 369.3 349.4 367.7 351.9 365.9L432 411.7C430.4 432.5 440.6 453.3 459.8 464.5C486.6 480 520.8 470.8 536.3 444C551.8 417.2 542.6 383 515.8 367.5C514.5 366.7 513.1 366 511.8 365.4L511.8 274.6C513.2 274 514.5 273.3 515.8 272.5C542.6 257 551.8 222.8 536.3 196C520.8 169.2 486.8 160 460 175.5C440.7 186.6 430.6 207.5 432.2 228.3L381.6 257.2C393.1 268.4 401.5 282.8 405.4 298.9L456 269.9C458.6 271.7 461.2 273.2 464 274.6L464 365.4C461.2 366.7 458.5 368.3 456 370L375.9 324.2C376 322.8 376.1 321.4 376.1 319.9C376.1 297.6 363 278.3 344.1 269.3L344.1 170.5z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
1
frontend/public/icons/magnifying-glass.svg
Normal file
1
frontend/public/icons/magnifying-glass.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path fill="rgb(116, 192, 252)" d="M480 272C480 317.9 465.1 360.3 440 394.7L566.6 521.4C579.1 533.9 579.1 554.2 566.6 566.7C554.1 579.2 533.8 579.2 521.3 566.7L394.7 440C360.3 465.1 317.9 480 272 480C157.1 480 64 386.9 64 272C64 157.1 157.1 64 272 64C386.9 64 480 157.1 480 272zM272 416C351.5 416 416 351.5 416 272C416 192.5 351.5 128 272 128C192.5 128 128 192.5 128 272C128 351.5 192.5 416 272 416z"/></svg>
|
||||||
|
After Width: | Height: | Size: 621 B |
Loading…
Reference in New Issue
Block a user