@@ -98,22 +92,8 @@ export function Header({ activeTab, wsStatus, voiceStatus, themeMode, isDark, on
- {/* Right: status badges + theme toggle */}
+ {/* Right: status badges */}
- {/* Theme toggle */}
-
-
{/* WS Badge */}
= [
- { id: "messages", label: "Messages & Moderation", icon: MessageSquare },
- { id: "live", label: "Voice & Media", icon: Radio },
- { id: "dashboard", label: "Dashboard", icon: LayoutDashboard },
- { id: "settings" as const, label: "Admin", icon: Settings },
-];
+const navItems: Array<{ id: DashboardTab; label: string; icon: typeof Radio }> =
+ [
+ { id: "messages", label: "Messages & Moderation", icon: MessageSquare },
+ { id: "live", label: "Voice & Media", icon: Radio },
+ { id: "dashboard", label: "Dashboard", icon: LayoutDashboard },
+ ];
interface SidebarProps {
activeTab: DashboardTab;
@@ -31,7 +21,6 @@ interface SidebarProps {
recentMessages?: MessageRecord[];
guildId?: string;
channelId?: string;
- notificationCount?: number;
}
export function Sidebar({
@@ -41,7 +30,6 @@ export function Sidebar({
recentMessages = [],
guildId,
channelId,
- notificationCount = 0,
}: SidebarProps) {
const mascotChat = useMascotChat({
messageCount: recentMessages.length,
@@ -58,7 +46,7 @@ export function Sidebar({
{
- const target = e.currentTarget;
- target.style.display = "none";
- }}
/>
{/* Mascot image — only when expanded */}
@@ -86,10 +70,6 @@ export function Sidebar({
src="https://raw.githubusercontent.com/IMPHNEN/imphnen-frontend-service/develop/apps/dimentorin/public/image/mascot-1.png"
alt="Mascot"
className="mt-4 h-auto w-[140px] object-contain drop-shadow-md"
- onError={(e) => {
- const target = e.currentTarget;
- target.style.display = "none";
- }}
/>
)}
@@ -115,21 +95,6 @@ export function Sidebar({
>
{!collapsed &&
{item.label}}
- {!collapsed && item.id === "messages" &&
- notificationCount !== undefined &&
- notificationCount > 0 && (
-
- {notificationCount > 99 ? "99+" : notificationCount}
-
- )}
- {/* Collapsed badge — top-right dot */}
- {collapsed && item.id === "messages" &&
- notificationCount !== undefined &&
- notificationCount > 0 && (
-
- {notificationCount > 9 ? "N" : notificationCount}
-
- )}
);
})}
@@ -153,7 +118,7 @@ export function Sidebar({
onClose={() => mascotChat.setIsOpen(false)}
onSendMessage={mascotChat.handleSendMessage}
mascotName="IMPHNEN Mascot"
- className="fixed bottom-[170px] left-[80px] z-[9999] md:bottom-4 md:left-4 md:right-auto"
+ className="fixed bottom-[170px] left-[80px] z-[9999]"
/>
>
);
diff --git a/services/frontend/src/widgets/mascot/MascotChatbot.tsx b/services/frontend/src/widgets/mascot/MascotChatbot.tsx
index 6fe1faa..26c22ca 100644
--- a/services/frontend/src/widgets/mascot/MascotChatbot.tsx
+++ b/services/frontend/src/widgets/mascot/MascotChatbot.tsx
@@ -117,14 +117,14 @@ export function MascotChatbot({
)}
>
{/* Header */}
-
+
-
+
{mascotName}
-
+
{loading ? "Mengetik..." : "Online"}
@@ -134,7 +134,7 @@ export function MascotChatbot({
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.95 }}
onClick={() => setIsMinimized(!isMinimized)}
- className="p-1.5 hover:bg-primary-foreground/20 rounded-lg transition-colors"
+ className="p-1.5 hover:bg-white/20 rounded-lg transition-colors"
title={isMinimized ? "Maximize" : "Minimize"}
>
{isMinimized ? (
@@ -149,7 +149,7 @@ export function MascotChatbot({
onClick={() => {
onClose?.();
}}
- className="p-1.5 hover:bg-primary-foreground/20 rounded-lg transition-colors"
+ className="p-1.5 hover:bg-white/20 rounded-lg transition-colors"
title="Close"
>
@@ -176,10 +176,6 @@ export function MascotChatbot({
src={mascotAvatar}
alt={mascotName}
className="w-6 h-6 rounded-full object-cover"
- onError={(e) => {
- const target = e.currentTarget;
- target.style.display = "none";
- }}
/>
)}
{
- const target = e.currentTarget;
- target.style.display = "none";
- }}
/>
diff --git a/services/frontend/src/widgets/mascot/MascotImage.tsx b/services/frontend/src/widgets/mascot/MascotImage.tsx
index 2f1eed4..af53a3a 100644
--- a/services/frontend/src/widgets/mascot/MascotImage.tsx
+++ b/services/frontend/src/widgets/mascot/MascotImage.tsx
@@ -38,7 +38,6 @@ export function MascotImage({
const sizeClass = sizeMap[size];
const chatSizeClass = chatSizeMap[size];
const [isVisible, setIsVisible] = useState(false);
- const [imgError, setImgError] = useState(false);
useEffect(() => {
if (showChat && chatMessage) {
@@ -52,20 +51,13 @@ export function MascotImage({
return (
- {imgError ? (
-
-
-
- ) : (
-
setImgError(true)}
- />
- )}
+
{/* Floating Chat Bubble */}
{isVisible && chatMessage && (
@@ -80,7 +72,7 @@ export function MascotImage({
{/* Chat bubble */}
-
+
{chatMessage}
diff --git a/services/frontend/src/widgets/particles/ParticleBackground.tsx b/services/frontend/src/widgets/particles/ParticleBackground.tsx
index 21f6638..c6743c0 100644
--- a/services/frontend/src/widgets/particles/ParticleBackground.tsx
+++ b/services/frontend/src/widgets/particles/ParticleBackground.tsx
@@ -20,16 +20,11 @@ export function ParticleBackground() {
style={{ zIndex: -1 }}
>
{/* Top-right glow orb */}
-
+
{/* Bottom-left glow orb */}
);
diff --git a/services/frontend/tailwind.config.js b/services/frontend/tailwind.config.js
index 1785d4a..44977b3 100644
--- a/services/frontend/tailwind.config.js
+++ b/services/frontend/tailwind.config.js
@@ -1,6 +1,5 @@
/** @type {import('tailwindcss').Config} */
export default {
- darkMode: "class",
content: ["./index.html", "./src/**/*.{ts,tsx}"],
theme: {
extend: {
diff --git a/services/frontend/tsconfig.json b/services/frontend/tsconfig.json
index 9cd8dd5..deefd85 100644
--- a/services/frontend/tsconfig.json
+++ b/services/frontend/tsconfig.json
@@ -6,15 +6,11 @@
"strict": true,
"jsx": "react-jsx",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
- "types": ["astro/client"],
+ "types": ["vite/client"],
"noEmit": true,
"skipLibCheck": true,
"esModuleInterop": true,
- "allowSyntheticDefaultImports": true,
- "baseUrl": ".",
- "paths": {
- "~/*": ["src/*"]
- }
+ "allowSyntheticDefaultImports": true
},
- "include": ["src/**/*", "astro.config.mjs"]
+ "include": ["src/**/*", "vite.config.ts"]
}
diff --git a/services/frontend/vite.config.ts b/services/frontend/vite.config.ts
new file mode 100644
index 0000000..0699450
--- /dev/null
+++ b/services/frontend/vite.config.ts
@@ -0,0 +1,25 @@
+import react from "@vitejs/plugin-react";
+import { defineConfig } from "vite";
+
+export default defineConfig({
+ plugins: [react()],
+ build: {
+ rolldownOptions: {
+ checks: {
+ pluginTimings: false,
+ },
+ },
+ },
+ server: {
+ middlewareMode: false,
+ },
+ preview: {
+ port: 3000,
+ host: true,
+ allowedHosts: [
+ "imphnen.asepharyana.my.id",
+ "imphnen.asepharyana.tech",
+ "imphnen.asepharyana.web.id",
+ ],
+ },
+});