update client for server #142
Binary file not shown.
|
|
@ -1,3 +1,5 @@
|
|||
Run client: npm run dev or npm run build && npm run preview
|
||||
|
||||
==> Build client xong => coppy file asset và index vào folder static của server => thêm prefix static vào link của assets trong file index VD: /static/assets
|
||||
|
||||
Run server uvicorn main:app --reload
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,2 @@
|
|||
VITE_API_BASE_URL = "/"
|
||||
# VITE_API_BASE_URL = "http://127.0.0.1:8000"
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import ax from "axios";
|
||||
|
||||
const axios = ax.create({
|
||||
baseURL: "http://127.0.0.1:8000",
|
||||
baseURL: import.meta.env.VITE_API_BASE_URL || "/",
|
||||
});
|
||||
|
||||
export default axios;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ export default function TabFeatures() {
|
|||
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const [checkPoinLoading, setCheckPoinLoading] = useState(false);
|
||||
|
||||
const toggleAutoCheck = () => {
|
||||
if (isAutoChecking) {
|
||||
if (autoCheckIntervalRef.current) {
|
||||
|
|
@ -41,8 +43,13 @@ export default function TabFeatures() {
|
|||
};
|
||||
|
||||
const createCheckpoint = async () => {
|
||||
if (!currentUser) return;
|
||||
if (!currentUser) {
|
||||
toast.warning("Vui lòng chọn user để tạo checkpoint");
|
||||
|
||||
return;
|
||||
}
|
||||
try {
|
||||
setCheckPoinLoading(true);
|
||||
const file = await capture(videoRef, canvasRef);
|
||||
|
||||
const { data } = await checkingApi.register({ user: currentUser, file });
|
||||
|
|
@ -64,6 +71,8 @@ export default function TabFeatures() {
|
|||
(data.response?.data as any)?.message ||
|
||||
"Error In Checkpoint: " + JSON.stringify(data)
|
||||
);
|
||||
} finally {
|
||||
setCheckPoinLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -126,6 +135,8 @@ export default function TabFeatures() {
|
|||
// ← cách đúng nhất để detect phím cách
|
||||
e.preventDefault(); // nếu không muốn scroll
|
||||
|
||||
if (!loading) return;
|
||||
|
||||
captureAndCheck();
|
||||
}
|
||||
};
|
||||
|
|
@ -135,10 +146,10 @@ export default function TabFeatures() {
|
|||
return () => {
|
||||
window.removeEventListener("keydown", down);
|
||||
};
|
||||
}, [captureAndCheck]);
|
||||
}, [captureAndCheck, loading]);
|
||||
|
||||
return (
|
||||
<div className="absolute bottom-10 px-4 right-0 left-0 grid grid-cols-4 gap-4">
|
||||
<div className="absolute bottom-10 px-4 right-0 left-0 grid grid-cols-3 gap-4">
|
||||
<Button
|
||||
onClick={captureAndCheck}
|
||||
disabled={isAutoChecking}
|
||||
|
|
@ -162,7 +173,7 @@ export default function TabFeatures() {
|
|||
isAutoChecking && "animate-pulse"
|
||||
)}
|
||||
>
|
||||
{isAutoChecking ? (
|
||||
{!loading && isAutoChecking ? (
|
||||
<>
|
||||
<Square className="mr-2 size-4" />
|
||||
Dừng Tự Động
|
||||
|
|
@ -173,16 +184,26 @@ export default function TabFeatures() {
|
|||
Tự Động Điểm Danh
|
||||
</>
|
||||
)}
|
||||
|
||||
{loading && <Loader className="size-4 animate-spin" />}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
disabled={isAutoChecking}
|
||||
onClick={createCheckpoint}
|
||||
className={cn("w-full font-semibold")}
|
||||
>
|
||||
<Image />
|
||||
Tạo Check Point
|
||||
</Button>
|
||||
{currentUser && (
|
||||
<Button
|
||||
disabled={isAutoChecking}
|
||||
onClick={createCheckpoint}
|
||||
className={cn("w-full font-semibold")}
|
||||
>
|
||||
{!checkPoinLoading && (
|
||||
<>
|
||||
<Image />
|
||||
Tạo Check Point
|
||||
</>
|
||||
)}
|
||||
|
||||
{checkPoinLoading && <Loader className="size-4 animate-spin" />}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{!currentUser && <Register />}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -249,23 +249,23 @@ async def checkin(file: UploadFile = File(...), camera_id: str = Form("cam1"), d
|
|||
|
||||
|
||||
|
||||
# thêm dô đây
|
||||
id_log = 0
|
||||
ms_response = create_history({"name": encoding.name.split('\n')[0], "time_string": f"{datetime.datetime.now()}", "status": "check in"})
|
||||
id_log = ms_response.get('data').get('id')
|
||||
status = ms_response.get('data').get('status')
|
||||
# thêm dô đây------------
|
||||
# id_log = 0
|
||||
# ms_response = create_history({"name": encoding.name.split('\n')[0], "time_string": f"{datetime.datetime.now()}", "status": "check in"})
|
||||
# id_log = ms_response.get('data').get('id')
|
||||
# status = ms_response.get('data').get('status')
|
||||
|
||||
# reset pointer
|
||||
file.file.seek(0)
|
||||
# # reset pointer
|
||||
# file.file.seek(0)
|
||||
|
||||
send_image_res = send_image(
|
||||
id=id_log,
|
||||
file=file,
|
||||
student_name=encoding.name,
|
||||
status=status
|
||||
)
|
||||
# send_image_res = send_image(
|
||||
# id=id_log,
|
||||
# file=file,
|
||||
# student_name=encoding.name,
|
||||
# status=status
|
||||
# )
|
||||
|
||||
print(id_log, send_image_res)
|
||||
# print(id_log, send_image_res)
|
||||
|
||||
# Insert new checkin
|
||||
db.execute(
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,22 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>client</title>
|
||||
<script
|
||||
type="module"
|
||||
crossorigin
|
||||
src="/static/assets/index-Cbkb3kfK.js"
|
||||
></script>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
crossorigin
|
||||
href="/static/assets/index-CvR5W1c8.css"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,432 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Face Check-In / Register</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background-color: #f5f7fa;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #2c3e50;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 2px solid #3498db;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
video {
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
input[type="text"], input[type="email"] {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
margin: 8px 0;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
font-size: 16px;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
input[type="text"]:focus, input[type="email"]:focus {
|
||||
border-color: #3498db;
|
||||
outline: none;
|
||||
box-shadow: 0 0 5px rgba(52, 152, 219, 0.5);
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 20px;
|
||||
margin: 10px 5px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
#register {
|
||||
background-color: #2ecc71;
|
||||
}
|
||||
|
||||
#register:hover {
|
||||
background-color: #27ae60;
|
||||
}
|
||||
|
||||
.container {
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 20px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
#auto-checkin {
|
||||
background-color: #f39c12;
|
||||
}
|
||||
|
||||
#auto-checkin:hover {
|
||||
background-color: #d35400;
|
||||
}
|
||||
|
||||
#auto-checkin.active {
|
||||
background-color: #e74c3c;
|
||||
}
|
||||
|
||||
.logs-container {
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 12px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #f2f2f2;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
background-color: #9b59b6;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.refresh-btn:hover {
|
||||
background-color: #8e44ad;
|
||||
}
|
||||
|
||||
.empty-logs {
|
||||
text-align: center;
|
||||
color: #7f8c8d;
|
||||
padding: 20px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.main-layout {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
max-width: 1600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.left-panel {
|
||||
flex: 6;
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
flex: 5;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.main-layout {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.newest-log {
|
||||
background-color: #e8f8f5;
|
||||
font-weight: bold;
|
||||
animation: highlight 2s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes highlight {
|
||||
0% { background-color: #d4efdf; }
|
||||
50% { background-color: #a9dfbf; }
|
||||
100% { background-color: #e8f8f5; }
|
||||
}
|
||||
|
||||
.shortcut-hint {
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
color: #7f8c8d;
|
||||
font-style: italic;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.custom-alert {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
color: white;
|
||||
padding: 12px 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
font-family: sans-serif;
|
||||
font-size: 16px;
|
||||
z-index: 9999;
|
||||
animation: slideIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background-color: #34d399; /* green-400 */
|
||||
}
|
||||
|
||||
.alert-error {
|
||||
background-color: #f87171; /* red-400 */
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main-layout">
|
||||
<div class="left-panel">
|
||||
<div class="container">
|
||||
<h2>📸 Face Camera</h2>
|
||||
<div class="video-container" style="position: relative; display: flex; justify-content: center; align-items: center;">
|
||||
<video id="video" autoplay style="width: 100%;"></video>
|
||||
<img src="http://127.0.0.1:8000/static/face-removebg-preview.png" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 60%; height: auto; pointer-events: none; opacity: 0.5;" alt="Hướng dẫn nhận diện khuôn mặt">
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<div class="form-group">
|
||||
<input type="text" id="name" placeholder="Tên học sinh (khi đăng ký)">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="email" id="email" placeholder="Email học sinh (bắt buộc)">
|
||||
</div>
|
||||
|
||||
<div class="button-group">
|
||||
<button id="register">📥 Đăng ký khuôn mặt</button>
|
||||
<button id="checkin">✅ Điểm danh</button>
|
||||
<button id="auto-checkin">🔄 Tự động điểm danh</button>
|
||||
</div>
|
||||
<div class="shortcut-hint">
|
||||
Nhấn phím Space để điểm danh nhanh
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right-panel">
|
||||
<div class="logs-container">
|
||||
<h2>📋 Lịch sử điểm danh</h2>
|
||||
<button id="refresh-logs" class="refresh-btn">🔄 Làm mới dữ liệu</button>
|
||||
<div id="logs-table-container">
|
||||
<table id="logs-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Tên học sinh</th>
|
||||
<th>Thời gian</th>
|
||||
<th>Camera ID</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="logs-body">
|
||||
<!-- Dữ liệu logs sẽ được thêm vào đây -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<canvas id="canvas" width="1200" height="900" style="display:none;"></canvas>
|
||||
|
||||
<script>
|
||||
const video = document.getElementById('video');
|
||||
const canvas = document.getElementById('canvas');
|
||||
const context = canvas.getContext('2d');
|
||||
|
||||
// Mở camera
|
||||
navigator.mediaDevices.getUserMedia({ video: true })
|
||||
.then((stream) => {
|
||||
video.srcObject = stream;
|
||||
})
|
||||
.catch((err) => {
|
||||
showAlert("Không mở được camera: " + err);
|
||||
});
|
||||
|
||||
function showAlert(text, status = 'error') {
|
||||
const alertDiv = document.createElement('div');
|
||||
alertDiv.className = `custom-alert alert-${status}`;
|
||||
alertDiv.innerText = text;
|
||||
document.body.appendChild(alertDiv);
|
||||
|
||||
setTimeout(() => {
|
||||
alertDiv.style.opacity = '0';
|
||||
alertDiv.style.transition = 'opacity 0.5s ease-out';
|
||||
setTimeout(() => {
|
||||
alertDiv.remove();
|
||||
}, 500);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
// Hàm chụp ảnh từ video và gửi đến API
|
||||
function sendImage(url, extraData = {}) {
|
||||
context.drawImage(video, 0, 0, 1200, 900);
|
||||
canvas.toBlob((blob) => {
|
||||
const formData = new FormData();
|
||||
formData.append("file", blob, "frame.jpg");
|
||||
|
||||
for (const [key, value] of Object.entries(extraData)) {
|
||||
formData.append(key, value);
|
||||
}
|
||||
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
body: formData
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
const status = data.message && data.message.includes("successful") ? 'success' : 'error';
|
||||
showAlert(data.message || JSON.stringify(data), status);
|
||||
if (url === "/checkin" && data.message && data.message.includes("successful")) {
|
||||
loadLogs(); // Tải lại logs sau khi check-in thành công
|
||||
}
|
||||
})
|
||||
.catch(err => showAlert("Lỗi gửi ảnh: " + err, 'error'));
|
||||
}, "image/jpeg");
|
||||
}
|
||||
|
||||
// Sự kiện: Đăng ký
|
||||
document.getElementById('register').addEventListener('click', () => {
|
||||
const name = document.getElementById('name').value.trim();
|
||||
const email = document.getElementById('email').value.trim();
|
||||
if (!name || !email) {
|
||||
showAlert("Vui lòng nhập cả tên và email.", 'error');
|
||||
return;
|
||||
}
|
||||
sendImage("/register", { name, email });
|
||||
});
|
||||
|
||||
let autoCheckinInterval = null;
|
||||
const autoCheckinButton = document.getElementById('auto-checkin');
|
||||
|
||||
// Hàm bật/tắt tự động điểm danh
|
||||
function toggleAutoCheckin() {
|
||||
if (autoCheckinInterval) {
|
||||
// Tắt tự động
|
||||
clearInterval(autoCheckinInterval);
|
||||
autoCheckinInterval = null;
|
||||
autoCheckinButton.classList.remove('active');
|
||||
autoCheckinButton.textContent = '🔄 Tự động điểm danh';
|
||||
} else {
|
||||
// Bật tự động
|
||||
autoCheckinInterval = setInterval(() => {
|
||||
sendImage("/checkin", { camera_id: "webcam" });
|
||||
}, 1000); // Gửi mỗi giây
|
||||
autoCheckinButton.classList.add('active');
|
||||
autoCheckinButton.textContent = '⏹️ Dừng tự động';
|
||||
}
|
||||
}
|
||||
|
||||
// Sự kiện: Bật/tắt tự động điểm danh
|
||||
autoCheckinButton.addEventListener('click', toggleAutoCheckin);
|
||||
|
||||
// Sự kiện: Check-in
|
||||
document.getElementById('checkin').addEventListener('click', () => {
|
||||
if (autoCheckinInterval) {
|
||||
toggleAutoCheckin(); // Tắt tự động nếu đang bật
|
||||
}
|
||||
sendImage("/checkin", { camera_id: "webcam" });
|
||||
});
|
||||
|
||||
// Sự kiện: Nhấn phím Space để điểm danh
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (event.code === 'Space' || event.keyCode === 32) {
|
||||
event.preventDefault();
|
||||
if (autoCheckinInterval) {
|
||||
toggleAutoCheckin(); // Tắt tự động nếu đang bật
|
||||
}
|
||||
sendImage("/checkin", { camera_id: "webcam" });
|
||||
}
|
||||
});
|
||||
|
||||
// Hàm tải logs từ server
|
||||
function loadLogs() {
|
||||
fetch("/logs")
|
||||
.then(res => res.json())
|
||||
.then(logs => {
|
||||
const logsBody = document.getElementById('logs-body');
|
||||
logsBody.innerHTML = '';
|
||||
|
||||
if (logs.length === 0) {
|
||||
logsBody.innerHTML = '<tr><td colspan="3" class="empty-logs">Chưa có dữ liệu điểm danh</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Sắp xếp logs từ mới đến cũ
|
||||
logs.sort((a, b) => new Date(b.time) - new Date(a.time));
|
||||
|
||||
logs.forEach((log, index) => {
|
||||
// Highlight log mới nhất
|
||||
const isNewest = index === 0;
|
||||
const row = document.createElement('tr');
|
||||
if (isNewest) {
|
||||
row.classList.add('newest-log');
|
||||
}
|
||||
row.innerHTML = `
|
||||
<td>${log.name}</td>
|
||||
<td>${log.time}</td>
|
||||
<td>${log.camera_id}</td>
|
||||
`;
|
||||
logsBody.appendChild(row);
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
console.error("Lỗi tải logs:", err);
|
||||
document.getElementById('logs-body').innerHTML =
|
||||
'<tr><td colspan="3" class="empty-logs">Lỗi khi tải dữ liệu</td></tr>';
|
||||
});
|
||||
}
|
||||
|
||||
// Sự kiện: Làm mới logs
|
||||
document.getElementById('refresh-logs').addEventListener('click', loadLogs);
|
||||
|
||||
// Tải logs khi trang được tải
|
||||
document.addEventListener('DOMContentLoaded', loadLogs);
|
||||
</script>
|
||||
</body>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>client</title>
|
||||
<script
|
||||
type="module"
|
||||
crossorigin
|
||||
src="/static/assets/index-BvxJK8_6.js"
|
||||
></script>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
crossorigin
|
||||
href="/static/assets/index-CDZdzCu6.css"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 232 KiB After Width: | Height: | Size: 219 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 234 KiB After Width: | Height: | Size: 222 KiB |
Loading…
Reference in New Issue