Spaces:
Sleeping
Sleeping
fix: priority 4,3,2
Browse files
resources/js/components/auth/register-role-form.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
import { Head, Link } from '@inertiajs/react';
|
| 2 |
import type { FormEvent } from 'react';
|
| 3 |
import { useState } from 'react';
|
| 4 |
|
|
@@ -9,7 +9,7 @@ import { Input } from '@/components/ui/input';
|
|
| 9 |
import { Label } from '@/components/ui/label';
|
| 10 |
import { Spinner } from '@/components/ui/spinner';
|
| 11 |
import { RagApiError, registerUser } from '@/lib/rag-client';
|
| 12 |
-
import { register } from '@/routes';
|
| 13 |
|
| 14 |
const roleContent = {
|
| 15 |
mahasiswa: {
|
|
@@ -91,9 +91,13 @@ function validateRegisterForm(
|
|
| 91 |
}
|
| 92 |
|
| 93 |
if (!normalizedIdentityNumber) {
|
| 94 |
-
errors.identity_number = `${identifierLabel} wajib diisi
|
| 95 |
-
} else if (!
|
| 96 |
-
errors.identity_number = `${identifierLabel} hanya boleh angka
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
}
|
| 98 |
|
| 99 |
if (!normalizedEmail) {
|
|
@@ -311,7 +315,8 @@ export default function RegisterRoleForm({ role }: RegisterRoleFormProps) {
|
|
| 311 |
setFieldErrors({});
|
| 312 |
setFormValues(emptyFormValues);
|
| 313 |
setTouchedFields({});
|
| 314 |
-
setSuccessMessage('Registrasi berhasil
|
|
|
|
| 315 |
} catch (err) {
|
| 316 |
if (err instanceof RagApiError) {
|
| 317 |
const parsed = parseRegisterApiError(err);
|
|
|
|
| 1 |
+
import { Head, Link, router } from '@inertiajs/react';
|
| 2 |
import type { FormEvent } from 'react';
|
| 3 |
import { useState } from 'react';
|
| 4 |
|
|
|
|
| 9 |
import { Label } from '@/components/ui/label';
|
| 10 |
import { Spinner } from '@/components/ui/spinner';
|
| 11 |
import { RagApiError, registerUser } from '@/lib/rag-client';
|
| 12 |
+
import { login, register } from '@/routes';
|
| 13 |
|
| 14 |
const roleContent = {
|
| 15 |
mahasiswa: {
|
|
|
|
| 91 |
}
|
| 92 |
|
| 93 |
if (!normalizedIdentityNumber) {
|
| 94 |
+
errors.identity_number = `${identifierLabel} wajib diisi`;
|
| 95 |
+
} else if (!/^\d+$/.test(normalizedIdentityNumber)) {
|
| 96 |
+
errors.identity_number = `${identifierLabel} hanya boleh angka`;
|
| 97 |
+
} else if (normalizedIdentityNumber.length < 5) {
|
| 98 |
+
errors.identity_number = `${identifierLabel} minimal 5 digit`;
|
| 99 |
+
} else if (normalizedIdentityNumber.length > 30) {
|
| 100 |
+
errors.identity_number = `${identifierLabel} maksimal 30 digit`;
|
| 101 |
}
|
| 102 |
|
| 103 |
if (!normalizedEmail) {
|
|
|
|
| 315 |
setFieldErrors({});
|
| 316 |
setFormValues(emptyFormValues);
|
| 317 |
setTouchedFields({});
|
| 318 |
+
setSuccessMessage('Registrasi berhasil! Mengalihkan ke halaman login...');
|
| 319 |
+
setTimeout(() => router.visit(login()), 2000);
|
| 320 |
} catch (err) {
|
| 321 |
if (err instanceof RagApiError) {
|
| 322 |
const parsed = parseRegisterApiError(err);
|
resources/js/components/student/student-shell.tsx
CHANGED
|
@@ -157,7 +157,7 @@ function StudentSidebar({
|
|
| 157 |
const [isLogoutDialogOpen, setIsLogoutDialogOpen] = useState(false);
|
| 158 |
const [sessionToDelete, setSessionToDelete] =
|
| 159 |
useState<ChatSessionResponse>();
|
| 160 |
-
const { state } = useSidebar();
|
| 161 |
const isSidebarCollapsed = state === 'collapsed';
|
| 162 |
|
| 163 |
function handleDeleteDialogOpenChange(open: boolean): void {
|
|
@@ -211,6 +211,10 @@ function StudentSidebar({
|
|
| 211 |
<SidebarMenuButton
|
| 212 |
className="student-history-menu-button"
|
| 213 |
tooltip="Riwayat Chat"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 214 |
>
|
| 215 |
<Clock3 className="student-history-menu-icon size-4" />
|
| 216 |
<span className="student-history-copy">
|
|
@@ -508,6 +512,7 @@ export function StudentShell({
|
|
| 508 |
|
| 509 |
<SidebarInset className="student-workspace">
|
| 510 |
<header className="student-topbar">
|
|
|
|
| 511 |
<h1 className="student-brand-title">RAG Hub</h1>
|
| 512 |
|
| 513 |
<div className="student-topbar-actions">
|
|
|
|
| 157 |
const [isLogoutDialogOpen, setIsLogoutDialogOpen] = useState(false);
|
| 158 |
const [sessionToDelete, setSessionToDelete] =
|
| 159 |
useState<ChatSessionResponse>();
|
| 160 |
+
const { state, toggleSidebar } = useSidebar();
|
| 161 |
const isSidebarCollapsed = state === 'collapsed';
|
| 162 |
|
| 163 |
function handleDeleteDialogOpenChange(open: boolean): void {
|
|
|
|
| 211 |
<SidebarMenuButton
|
| 212 |
className="student-history-menu-button"
|
| 213 |
tooltip="Riwayat Chat"
|
| 214 |
+
onClick={() => {
|
| 215 |
+
toggleSidebar();
|
| 216 |
+
setIsHistoryOpen(true);
|
| 217 |
+
}}
|
| 218 |
>
|
| 219 |
<Clock3 className="student-history-menu-icon size-4" />
|
| 220 |
<span className="student-history-copy">
|
|
|
|
| 512 |
|
| 513 |
<SidebarInset className="student-workspace">
|
| 514 |
<header className="student-topbar">
|
| 515 |
+
<SidebarTrigger className="md:hidden" />
|
| 516 |
<h1 className="student-brand-title">RAG Hub</h1>
|
| 517 |
|
| 518 |
<div className="student-topbar-actions">
|
resources/js/pages/dosen.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
import { Head, router } from '@inertiajs/react';
|
| 2 |
import type { LucideIcon } from 'lucide-react';
|
| 3 |
-
import { AlertTriangle, Database, FileText, Plus, Search } from 'lucide-react';
|
| 4 |
import type { FormEvent, ReactNode } from 'react';
|
| 5 |
import { useEffect, useMemo, useState } from 'react';
|
| 6 |
|
|
@@ -19,6 +19,12 @@ import {
|
|
| 19 |
import { Input } from '@/components/ui/input';
|
| 20 |
import { Label } from '@/components/ui/label';
|
| 21 |
import { LoadingIndicator } from '@/components/ui/loading-indicator';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
import {
|
| 23 |
createCourse,
|
| 24 |
listCourseDocuments,
|
|
@@ -41,6 +47,7 @@ type Metric = {
|
|
| 41 |
label: string;
|
| 42 |
value: ReactNode;
|
| 43 |
description?: string;
|
|
|
|
| 44 |
icon: LucideIcon;
|
| 45 |
tone: 'primary' | 'danger';
|
| 46 |
};
|
|
@@ -103,7 +110,21 @@ function MetricCard({ metric }: { metric: Metric }) {
|
|
| 103 |
</span>
|
| 104 |
|
| 105 |
<div className="lecturer-metric-copy">
|
| 106 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
<div className="lecturer-metric-value-row">
|
| 108 |
<strong>{metric.value}</strong>
|
| 109 |
{metric.description && (
|
|
@@ -145,7 +166,13 @@ export default function Dosen() {
|
|
| 145 |
}),
|
| 146 |
);
|
| 147 |
|
| 148 |
-
setKnowledgeBases(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
})
|
| 150 |
.catch((e: Error) => setStatusMessage(e.message))
|
| 151 |
.finally(() => setIsLoading(false));
|
|
@@ -166,6 +193,7 @@ export default function Dosen() {
|
|
| 166 |
icon: Database,
|
| 167 |
label: 'Total Knowledge Base',
|
| 168 |
tone: 'primary',
|
|
|
|
| 169 |
value: String(knowledgeBases.length),
|
| 170 |
},
|
| 171 |
{
|
|
@@ -173,6 +201,7 @@ export default function Dosen() {
|
|
| 173 |
label: 'Total Dokumen',
|
| 174 |
description: 'Chunked',
|
| 175 |
tone: 'primary',
|
|
|
|
| 176 |
value: String(documentCount),
|
| 177 |
},
|
| 178 |
{
|
|
@@ -180,6 +209,7 @@ export default function Dosen() {
|
|
| 180 |
label: 'Processing Error',
|
| 181 |
description: 'dokumen',
|
| 182 |
tone: 'danger',
|
|
|
|
| 183 |
value: String(failedDocumentCount),
|
| 184 |
},
|
| 185 |
];
|
|
|
|
| 1 |
import { Head, router } from '@inertiajs/react';
|
| 2 |
import type { LucideIcon } from 'lucide-react';
|
| 3 |
+
import { AlertTriangle, Database, FileText, Info, Plus, Search } from 'lucide-react';
|
| 4 |
import type { FormEvent, ReactNode } from 'react';
|
| 5 |
import { useEffect, useMemo, useState } from 'react';
|
| 6 |
|
|
|
|
| 19 |
import { Input } from '@/components/ui/input';
|
| 20 |
import { Label } from '@/components/ui/label';
|
| 21 |
import { LoadingIndicator } from '@/components/ui/loading-indicator';
|
| 22 |
+
import {
|
| 23 |
+
Tooltip,
|
| 24 |
+
TooltipContent,
|
| 25 |
+
TooltipProvider,
|
| 26 |
+
TooltipTrigger,
|
| 27 |
+
} from '@/components/ui/tooltip';
|
| 28 |
import {
|
| 29 |
createCourse,
|
| 30 |
listCourseDocuments,
|
|
|
|
| 47 |
label: string;
|
| 48 |
value: ReactNode;
|
| 49 |
description?: string;
|
| 50 |
+
tooltip?: string;
|
| 51 |
icon: LucideIcon;
|
| 52 |
tone: 'primary' | 'danger';
|
| 53 |
};
|
|
|
|
| 110 |
</span>
|
| 111 |
|
| 112 |
<div className="lecturer-metric-copy">
|
| 113 |
+
<div className="flex items-center gap-1.5">
|
| 114 |
+
<p>{metric.label}</p>
|
| 115 |
+
{metric.tooltip && (
|
| 116 |
+
<TooltipProvider>
|
| 117 |
+
<Tooltip>
|
| 118 |
+
<TooltipTrigger asChild>
|
| 119 |
+
<Info className="size-3.5 cursor-help text-muted-foreground" />
|
| 120 |
+
</TooltipTrigger>
|
| 121 |
+
<TooltipContent side="top">
|
| 122 |
+
{metric.tooltip}
|
| 123 |
+
</TooltipContent>
|
| 124 |
+
</Tooltip>
|
| 125 |
+
</TooltipProvider>
|
| 126 |
+
)}
|
| 127 |
+
</div>
|
| 128 |
<div className="lecturer-metric-value-row">
|
| 129 |
<strong>{metric.value}</strong>
|
| 130 |
{metric.description && (
|
|
|
|
| 166 |
}),
|
| 167 |
);
|
| 168 |
|
| 169 |
+
setKnowledgeBases(
|
| 170 |
+
[...rows].sort((a, b) => {
|
| 171 |
+
if (a.updatedAt === '-') return 1;
|
| 172 |
+
if (b.updatedAt === '-') return -1;
|
| 173 |
+
return b.updatedAt.localeCompare(a.updatedAt);
|
| 174 |
+
}),
|
| 175 |
+
);
|
| 176 |
})
|
| 177 |
.catch((e: Error) => setStatusMessage(e.message))
|
| 178 |
.finally(() => setIsLoading(false));
|
|
|
|
| 193 |
icon: Database,
|
| 194 |
label: 'Total Knowledge Base',
|
| 195 |
tone: 'primary',
|
| 196 |
+
tooltip: 'Jumlah mata kuliah / topik yang sudah Anda buat sebagai sumber pengetahuan AI.',
|
| 197 |
value: String(knowledgeBases.length),
|
| 198 |
},
|
| 199 |
{
|
|
|
|
| 201 |
label: 'Total Dokumen',
|
| 202 |
description: 'Chunked',
|
| 203 |
tone: 'primary',
|
| 204 |
+
tooltip: 'Jumlah dokumen yang sudah berhasil diproses dan dipotong-potong (chunked) oleh sistem agar bisa dibaca AI.',
|
| 205 |
value: String(documentCount),
|
| 206 |
},
|
| 207 |
{
|
|
|
|
| 209 |
label: 'Processing Error',
|
| 210 |
description: 'dokumen',
|
| 211 |
tone: 'danger',
|
| 212 |
+
tooltip: 'Jumlah dokumen yang gagal diproses. Dokumen ini tidak akan bisa digunakan AI untuk menjawab pertanyaan.',
|
| 213 |
value: String(failedDocumentCount),
|
| 214 |
},
|
| 215 |
];
|
resources/js/pages/mahasiswa-chat.tsx
CHANGED
|
@@ -1117,7 +1117,7 @@ export default function MahasiswaChat({ sessionId }: MahasiswaChatProps) {
|
|
| 1117 |
</span>
|
| 1118 |
<div className="student-message-bubble student-message-bubble--typing">
|
| 1119 |
<LoadingIndicator
|
| 1120 |
-
label="
|
| 1121 |
showSpinner={false}
|
| 1122 |
/>
|
| 1123 |
</div>
|
|
|
|
| 1117 |
</span>
|
| 1118 |
<div className="student-message-bubble student-message-bubble--typing">
|
| 1119 |
<LoadingIndicator
|
| 1120 |
+
label="RAG Hub sedang berpikir"
|
| 1121 |
showSpinner={false}
|
| 1122 |
/>
|
| 1123 |
</div>
|
resources/js/pages/mahasiswa.tsx
CHANGED
|
@@ -129,13 +129,13 @@ export default function Mahasiswa() {
|
|
| 129 |
<div className="student-greeting">
|
| 130 |
<h2>
|
| 131 |
{isProcessing
|
| 132 |
-
? '
|
| 133 |
: `Good Morning, ${studentName}`}
|
| 134 |
</h2>
|
| 135 |
{isProcessing ? (
|
| 136 |
<p>
|
| 137 |
<LoadingIndicator
|
| 138 |
-
label="
|
| 139 |
showSpinner={false}
|
| 140 |
/>
|
| 141 |
</p>
|
|
|
|
| 129 |
<div className="student-greeting">
|
| 130 |
<h2>
|
| 131 |
{isProcessing
|
| 132 |
+
? 'RAG Hub sedang berpikir...'
|
| 133 |
: `Good Morning, ${studentName}`}
|
| 134 |
</h2>
|
| 135 |
{isProcessing ? (
|
| 136 |
<p>
|
| 137 |
<LoadingIndicator
|
| 138 |
+
label="RAG Hub sedang memikirkan jawaban"
|
| 139 |
showSpinner={false}
|
| 140 |
/>
|
| 141 |
</p>
|