Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b5839ebd5 | ||
|
|
e2742a7a60 | ||
|
|
ae22adee40 | ||
|
|
ee02bd4f9a | ||
|
|
310d8adc8d | ||
|
|
ccbcd99053 | ||
|
|
52e60b99dd | ||
|
|
e19321a2b7 |
@@ -80,7 +80,9 @@ function renderTauriDeepLinkPage(targetUrl: string): Response {
|
||||
const escapedPath = pathAndQuery.replace(/"/g, '"');
|
||||
// intent:// scheme: Chrome on Android opens the target app by package name
|
||||
// browser_fallback_url: shown if the app isn't installed
|
||||
const intentUrl = `intent:${escapedPath}#Intent;scheme=zeavisedu;package=com.zeavis.edu;S.browser_fallback_url=${encodeURIComponent(targetUrl)};end`;
|
||||
// Use intent://login/... to produce data URI zeavisedu://login/login?token=xxx
|
||||
// which new URL() can parse (single-slash non-hierarchical URLs break WebView)
|
||||
const intentUrl = `intent://login${escapedPath}#Intent;scheme=zeavisedu;package=com.zeavis.edu;S.browser_fallback_url=${encodeURIComponent(targetUrl)};end`;
|
||||
|
||||
const html = `<!DOCTYPE html>
|
||||
<html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 19 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 103 KiB |
@@ -35,20 +35,25 @@ export async function setupDeepLinkHandler(): Promise<void> {
|
||||
const { onOpenUrl } = await import('@tauri-apps/plugin-deep-link');
|
||||
onOpenUrl((urls) => {
|
||||
for (const url of urls) {
|
||||
// url looks like: zeavisedu://zeavisedu.asepharyana.my.id/login?token=xxx
|
||||
// url looks like: zeavisedu://login/login?token=xxx
|
||||
// Extract path + query after the host
|
||||
try {
|
||||
const u = new URL(url);
|
||||
const target = u.pathname + u.search + u.hash;
|
||||
if (target && target !== '/') {
|
||||
window.location.href = target;
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
// If URL parsing fails, try to extract everything after the scheme
|
||||
const match = url.match(/^[^:]+:\/\/(?:[^/]+)?(\/.*)?$/);
|
||||
if (match?.[1]) {
|
||||
window.location.href = match[1];
|
||||
}
|
||||
} catch { /* try fallback below */ }
|
||||
|
||||
// Fallback: extract everything after scheme, handling both // and /
|
||||
let match = url.match(/^[^:]+:\/\/(?:[^/]+)?(\/.*)?$/);
|
||||
if (!match) {
|
||||
// Also handle single-slash non-hierarchical URLs (e.g. zeavisedu:/path)
|
||||
match = url.match(/^[^:]+:\/(\/.*)?$/);
|
||||
}
|
||||
if (match?.[1]) {
|
||||
window.location.href = match[1];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -54,12 +54,12 @@ export function CatalogPage() {
|
||||
<div className="space-y-6 max-w-5xl mx-auto pb-10">
|
||||
{/* Page Title */}
|
||||
<div>
|
||||
<h1 className="text-2xl md:text-3xl font-extrabold text-[#214B11]">
|
||||
<h1 className="text-2xl font-bold text-emerald-800">
|
||||
Pustaka Penyakit
|
||||
</h1>
|
||||
<p className="mt-1 text-muted-foreground text-sm md:text-base">
|
||||
<p className="text-gray-500 mt-1 text-md">
|
||||
Referensi lengkap penyakit dan kondisi daun jagung yang dapat
|
||||
dideteksi oleh sistem AI ZeaVis Edu.
|
||||
dideteksi oleh sistem AI ZeaVis Edu
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { useUiStore } from "@/store/ui-store";
|
||||
import { apiClient } from "@/lib/api-client";
|
||||
import bg from "@/assets/images/dashboard-bg.webp";
|
||||
|
||||
export function DashboardPage() {
|
||||
const { dashboardCompact } = useUiStore();
|
||||
@@ -23,16 +22,17 @@ export function DashboardPage() {
|
||||
const summary = summaryQuery.data;
|
||||
const diseases = diseasesQuery.data ?? [];
|
||||
|
||||
const diseasesQuick = useMemo(() =>
|
||||
diseases
|
||||
.sort((a, b) => a.displayOrder - b.displayOrder)
|
||||
.map((d) => ({
|
||||
name: d.commonName,
|
||||
sci: d.label,
|
||||
color: d.accentColor,
|
||||
slug: d.slug,
|
||||
})),
|
||||
[diseases]
|
||||
const diseasesQuick = useMemo(
|
||||
() =>
|
||||
diseases
|
||||
.sort((a, b) => a.displayOrder - b.displayOrder)
|
||||
.map((d) => ({
|
||||
name: d.commonName,
|
||||
sci: d.label,
|
||||
color: d.accentColor,
|
||||
slug: d.slug,
|
||||
})),
|
||||
[diseases],
|
||||
);
|
||||
|
||||
const missionCards = [
|
||||
@@ -74,7 +74,11 @@ export function DashboardPage() {
|
||||
{/* Hero header */}
|
||||
<header
|
||||
className="relative overflow-hidden rounded-3xl bg-cover bg-center bg-no-repeat shadow-sm"
|
||||
style={{ backgroundImage: `url(${bg})` }}
|
||||
style={{
|
||||
backgroundImage: `url(https://cdn.pixabay.com/photo/2014/09/09/19/07/corn-field-440338_1280.jpg)`,
|
||||
backgroundPosition: "bottom",
|
||||
backgroundSize: "cover",
|
||||
}}
|
||||
>
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-[#2F6E1A]/60 to-black/30" />
|
||||
<div className="relative z-10 flex flex-col md:flex-row items-center justify-between gap-6 p-6 md:p-10">
|
||||
@@ -82,7 +86,9 @@ export function DashboardPage() {
|
||||
<span className="inline-block rounded-full bg-[#1E8A2A]/80 px-4 py-2 text-xs font-semibold">
|
||||
AI FOR SMART EDUCATION
|
||||
</span>
|
||||
<h1 className="text-2xl md:text-4xl font-extrabold">Selamat Datang di</h1>
|
||||
<h1 className="text-2xl md:text-4xl font-extrabold">
|
||||
Selamat Datang di
|
||||
</h1>
|
||||
<h2 className="text-2xl md:text-4xl font-extrabold tracking-tight text-[#9AD872]">
|
||||
ZeaVis Edu
|
||||
</h2>
|
||||
@@ -107,10 +113,7 @@ export function DashboardPage() {
|
||||
variant="outline"
|
||||
className="px-4 md:px-6 py-3 md:py-6 text-sm md:text-lg font-semibold text-white hover:bg-[#1E8A2A]"
|
||||
>
|
||||
<Link
|
||||
to="/catalog"
|
||||
className="inline-flex items-center gap-2"
|
||||
>
|
||||
<Link to="/catalog" className="inline-flex items-center gap-2">
|
||||
Pustaka Penyakit
|
||||
<ChevronRight className="h-5 w-6" />
|
||||
</Link>
|
||||
@@ -169,7 +172,10 @@ export function DashboardPage() {
|
||||
<div className="text-3xl font-bold">
|
||||
{summary.imageClassificationCount}
|
||||
</div>
|
||||
<Link to="/diagnoses" className="text-emerald-600 ml-auto hover:underline">
|
||||
<Link
|
||||
to="/diagnoses"
|
||||
className="text-emerald-600 ml-auto hover:underline"
|
||||
>
|
||||
Lihat daftar
|
||||
</Link>
|
||||
</CardContent>
|
||||
@@ -186,7 +192,10 @@ export function DashboardPage() {
|
||||
<div className="text-3xl font-bold text-amber-600">
|
||||
{summary.needsReviewCount}
|
||||
</div>
|
||||
<Link to="/diagnoses?status=needs_review" className="text-amber-600 ml-auto hover:underline">
|
||||
<Link
|
||||
to="/diagnoses?status=needs_review"
|
||||
className="text-amber-600 ml-auto hover:underline"
|
||||
>
|
||||
Lihat daftar
|
||||
</Link>
|
||||
</CardContent>
|
||||
@@ -200,10 +209,11 @@ export function DashboardPage() {
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="h-full flex flex-col justify-start pt-2">
|
||||
<div className="text-3xl font-bold text-red-600">
|
||||
—
|
||||
</div>
|
||||
<Link to="/diagnoses?status=failed" className="text-red-600 ml-auto hover:underline">
|
||||
<div className="text-3xl font-bold text-red-600">—</div>
|
||||
<Link
|
||||
to="/diagnoses?status=failed"
|
||||
className="text-red-600 ml-auto hover:underline"
|
||||
>
|
||||
Lihat daftar
|
||||
</Link>
|
||||
</CardContent>
|
||||
@@ -220,7 +230,10 @@ export function DashboardPage() {
|
||||
<div className="text-3xl font-bold text-red-600">
|
||||
{summary.riskDistribution.high}
|
||||
</div>
|
||||
<Link to="/catalog?risk=high" className="text-red-600 ml-auto hover:underline">
|
||||
<Link
|
||||
to="/catalog?risk=high"
|
||||
className="text-red-600 ml-auto hover:underline"
|
||||
>
|
||||
Lihat pustaka
|
||||
</Link>
|
||||
</CardContent>
|
||||
@@ -276,7 +289,8 @@ export function DashboardPage() {
|
||||
Penyakit yang Dapat Dideteksi
|
||||
</h3>
|
||||
<p className="text-[15px] font-normal text-muted-foreground">
|
||||
{diseases.length} kelas penyakit dan kondisi daun jagung dalam sistem kami
|
||||
{diseases.length} kelas penyakit dan kondisi daun jagung dalam
|
||||
sistem kami
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
@@ -295,9 +309,7 @@ export function DashboardPage() {
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4">
|
||||
{diseasesQuick.map((d) => (
|
||||
<Link key={d.slug} to={`/catalog/${d.slug}`}>
|
||||
<Card
|
||||
className="rounded-2xl bg-white p-4 shadow-sm h-full transition-transform hover:scale-[1.03] hover:shadow-md cursor-pointer"
|
||||
>
|
||||
<Card className="rounded-2xl bg-white p-4 shadow-sm h-full transition-transform hover:scale-[1.03] hover:shadow-md cursor-pointer">
|
||||
<CardContent className="h-full p-4 flex flex-col justify-between">
|
||||
<div className="flex items-start gap-3">
|
||||
<div
|
||||
@@ -324,7 +336,9 @@ export function DashboardPage() {
|
||||
{/* Scan quick access */}
|
||||
<div className="mt-12 flex flex-col sm:flex-row items-center gap-4 sm:gap-6 bg-[#1E8A2A] rounded-3xl p-5 sm:p-6">
|
||||
<div className="flex-1 text-white">
|
||||
<h3 className="text-2xl font-bold">Siap Mendeteksi Penyakit Daun?</h3>
|
||||
<h3 className="text-2xl font-bold">
|
||||
Siap Mendeteksi Penyakit Daun?
|
||||
</h3>
|
||||
<p className="text-sm text-[#9AD872] font-normal mt-2">
|
||||
Unggah foto daun jagung Anda dan dapatkan hasil analisis AI
|
||||
dalam hitungan detik.
|
||||
|
||||
@@ -75,7 +75,7 @@ export function DiagnosesPage() {
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-emerald-800">Diagnosa Tanaman</h1>
|
||||
<h1 className="text-2xl font-bold text-emerald-800">Diagnosa Penyakit</h1>
|
||||
<p className="text-gray-500 mt-1 text-md">
|
||||
Lihat hasil diagnosa dari scan yang telah dilakukan
|
||||
</p>
|
||||
|
||||
@@ -71,7 +71,7 @@ export function ExpertReviewsPage() {
|
||||
<div className="space-y-6">
|
||||
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-emerald-800">Review Pakar</h1>
|
||||
<h1 className="text-2xl font-bold text-emerald-800">Tinjauan Pakar</h1>
|
||||
<p className="text-gray-500 mt-1 text-md">
|
||||
Tinjau hasil diagnosa dari scan yang telah dilakukan dan berikan
|
||||
feedback untuk meningkatkan akurasi sistem AI ZeaVis Edu
|
||||
|
||||
@@ -36,21 +36,18 @@ export function ScanPage() {
|
||||
|
||||
// Camera mode state
|
||||
const [useCamera, setUseCamera] = useState(false);
|
||||
const handleCameraCapture = useCallback(
|
||||
(file: File) => {
|
||||
setFileName(file.name);
|
||||
const url = URL.createObjectURL(file);
|
||||
setPreviewUrl(url);
|
||||
const handleCameraCapture = useCallback((file: File) => {
|
||||
setFileName(file.name);
|
||||
const url = URL.createObjectURL(file);
|
||||
setPreviewUrl(url);
|
||||
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
setImageDimensions({ width: img.width, height: img.height });
|
||||
};
|
||||
img.src = url;
|
||||
setUseCamera(false);
|
||||
},
|
||||
[],
|
||||
);
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
setImageDimensions({ width: img.width, height: img.height });
|
||||
};
|
||||
img.src = url;
|
||||
setUseCamera(false);
|
||||
}, []);
|
||||
|
||||
const mutation = useMutation({
|
||||
mutationFn: (file: File) => apiClient.createDiagnosis(file),
|
||||
@@ -107,10 +104,10 @@ export function ScanPage() {
|
||||
{/* Main Header */}
|
||||
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 sm:gap-4">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-emerald-800">Scan Tanaman</h1>
|
||||
<h1 className="text-2xl font-bold text-emerald-800">Pindai Daun</h1>
|
||||
<p className="text-gray-500 mt-1 text-md">
|
||||
Unggah foto daun jagung untuk dianalisis oleh sistem AI kami secara
|
||||
real-time.
|
||||
real-time
|
||||
</p>
|
||||
</div>
|
||||
<Button asChild variant="outline">
|
||||
@@ -170,12 +167,16 @@ export function ScanPage() {
|
||||
className="w-full border-2 border-dashed border-green-300 rounded-md p-10 h-60 text-center cursor-pointer"
|
||||
onClick={() => inputRef.current?.click()}
|
||||
>
|
||||
<Upload className="mx-auto text-green-500 mb-3" size={48} />
|
||||
<Upload
|
||||
className="mx-auto text-green-500 mb-3"
|
||||
size={48}
|
||||
/>
|
||||
<h3 className="font-semibold text-base text-gray-800 mb-1">
|
||||
Seret & Lepas Foto Daun
|
||||
</h3>
|
||||
<p className="text-xs text-gray-500 mb-3">
|
||||
atau klik untuk memilih file berkas dari perangkat Anda
|
||||
atau klik untuk memilih file berkas dari perangkat
|
||||
Anda
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-2 justify-center">
|
||||
<div className="bg-green-100 text-green-700 px-3 py-1 rounded-full inline-flex items-center gap-1 text-xs font-medium">
|
||||
|
||||
@@ -286,12 +286,11 @@ export function TelemetryPage() {
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-2xl md:text-[28px] font-extrabold text-[#214B11] flex items-center gap-3">
|
||||
<Activity className="h-7 w-7 text-[#48A111]" />
|
||||
<h1 className="text-2xl font-bold text-emerald-800">
|
||||
Telemetry Dashboard
|
||||
</h1>
|
||||
<p className="text-sm text-muted-foreground mt-0.5">
|
||||
Real-time metrics from Prometheus
|
||||
<p className="text-gray-500 mt-1 text-md">
|
||||
Real-time monitoring dari performa sistem dan aplikasi ZeaVis Edu
|
||||
{error && <span className="text-amber-600 ml-2">(partial — {error})</span>}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user