Skip to content
Merged

V2 #80

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/PatientPortal/FloatingChatbot.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const FloatingChatbot = ({ isFullScreen = false, hideFloatingButton = false }) =
setLoading(true);

try {
const res = await fetch(`${import.meta.env.VITE_CHATBOT_URL}/api/chat`, {
const res = await fetch(`/chatbot/api/chat`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message: inputMessage }),
Expand Down
2 changes: 1 addition & 1 deletion src/components/PatientPortal/MessagingDashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ const MessagingDashboard = () => {
const connectWebSocket = () => {
try {
if (stompRef.current?.active) return;
const sock = new SockJS(`${WS_BASE}/ws`);
const sock = new SockJS(`/ws`);
const stomp = new Client({
webSocketFactory: () => sock,
reconnectDelay: 5000,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ import SockJS from "sockjs-client";

/* ===================== Helpers ===================== */
const API_BASE = import.meta.env.VITE_API_URL;
const WS_BASE = import.meta.env.VITE_WS_URL || import.meta.env.VITE_API_URL;
// WebSocket connects directly to mainservice (not via API gateway)
// SockJS doesn't support custom request headers, so token is passed as query param
const WS_BASE = import.meta.env.VITE_WS_URL || "http://localhost:8080";

// Allow 0 as valid ID
function safeNumber(v) {
Expand Down Expand Up @@ -310,7 +312,8 @@ const DoctorMessagingDashboard = () => {
const connectWebSocket = () => {
if (stompRef.current?.active) return;
const stomp = new Client({
webSocketFactory: () => new SockJS(`${WS_BASE}/ws`),
// Pass token as query param — SockJS cannot send custom HTTP headers
webSocketFactory: () => new SockJS(`/ws`),
reconnectDelay: 5000,
});

Expand Down Expand Up @@ -493,6 +496,12 @@ const DoctorMessagingDashboard = () => {
if (!messageText.trim()) return;
if (userId === null || !selectedChat.participantId) return;

// Block send if WebSocket is not connected
if (!isConnected) {
alert("Not connected. Please wait a moment and try again.");
return;
}

const message = {
conversationId: selectedChat.id,
senderId: userId,
Expand All @@ -506,6 +515,7 @@ const DoctorMessagingDashboard = () => {

try {
stompRef.current.send(message);
// Only show message locally after confirming send was attempted
setMessages((prev) => [
...prev,
{ id: Date.now(), sender: "self", text: messageText, time: "Just now", read: false, attachments: [] },
Expand All @@ -514,7 +524,7 @@ const DoctorMessagingDashboard = () => {
if (textareaRef.current) textareaRef.current.style.height = "auto";
} catch (err) {
console.error("Error sending message:", err);
alert("Failed to send message.");
alert("Failed to send message. Please check your connection.");
}
};

Expand Down
39 changes: 2 additions & 37 deletions src/pages/DoctorViewPatient/DoctorViewComponents/Header.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useState, useEffect } from "react";
import { Heart, Bell, ArrowLeft } from "lucide-react";
import AlertsCard from "../../../components/PatientPortal/AlertsCard";
import { Heart, ArrowLeft } from "lucide-react";
import { useNavigate, useLocation } from "react-router-dom";

const Header = ({
Expand All @@ -10,8 +9,7 @@ const Header = ({
const navigate = useNavigate();
const location = useLocation();

const [isDarkMode, setIsDarkMode] = useState(false);
const [showAlerts, setShowAlerts] = useState(false);

const [profileName, setProfileName] = useState("Patient");

// where to return (from navigate state)
Expand Down Expand Up @@ -74,40 +72,7 @@ const Header = ({
</button>
)}

{/* DARK MODE UI toggle only */}
<button
onClick={() => setIsDarkMode(!isDarkMode)}
className={`relative w-12 h-6 sm:w-14 sm:h-7 rounded-full transition-colors ${
isDarkMode ? "bg-gray-700" : "bg-gray-300"
}`}
aria-label="Toggle dark mode"
type="button"
>
<div
className={`absolute top-1 w-4 h-4 sm:w-5 sm:h-5 bg-white rounded-full shadow-md transition-transform ${
isDarkMode ? "translate-x-6 sm:translate-x-8" : "translate-x-1"
}`}
/>
</button>

{/* ALERTS */}
<div className="relative">
<button
className="relative p-2 hover:bg-gray-100 rounded-full transition-colors"
onClick={() => setShowAlerts(!showAlerts)}
aria-label="Alerts"
type="button"
>
<Bell className="w-5 h-5 text-gray-700" />
<span className="absolute top-1 right-1 w-2 h-2 bg-red-500 rounded-full" />
</button>

{showAlerts && (
<div className="absolute right-0 mt-3 z-50 w-[260px] sm:w-auto">
<AlertsCard />
</div>
)}
</div>
</div>
</div>
</header>
Expand Down
23 changes: 2 additions & 21 deletions src/pages/DoctorsPage/DocDashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import AppointmentsCard from "../../components/PatientPortal/AppointmentsCard";
import { useNavigate } from "react-router-dom";
import {
Heart,
Bell,
Settings,
Activity,
AlertTriangle,
FileText,
Expand All @@ -21,8 +19,8 @@ import {
AlertCircle,
TrendingUp,
Wifi,
MessageSquare, // NEW ICON for Messaging
Calendar, // NEW ICON for Appointments
MessageSquare,
Calendar,
} from "lucide-react";

/* ===================== Config ===================== */
Expand Down Expand Up @@ -114,7 +112,6 @@ const fetchMyPatients = async () => {
};

function DocDashboard() {
const [hasNotification, setHasNotification] = useState(true);
const [activeTab, setActiveTab] = useState("patient-overview");
const [searchQuery, setSearchQuery] = useState("");
const navigate = useNavigate();
Expand Down Expand Up @@ -309,23 +306,7 @@ function DocDashboard() {

{/* Right */}
<div className="flex items-center gap-2 sm:gap-3 md:gap-4">
<button
className="p-2 hover:bg-gray-100 rounded-lg transition-colors relative"
aria-label="Notifications"
onClick={() => setHasNotification(false)}
>
<Bell className="w-4 h-4 sm:w-5 sm:h-5 md:w-6 md:h-6 text-black" />
{hasNotification && (
<span className="absolute top-1 right-1 w-2 h-2 sm:w-2.5 sm:h-2.5 bg-red-500 rounded-full" />
)}
</button>

<button
className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
aria-label="Settings"
>
<Settings className="w-4 h-4 sm:w-5 sm:h-5 md:w-6 md:h-6 text-black" />
</button>

<button
onClick={handleLogout}
Expand Down
12 changes: 12 additions & 0 deletions vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ export default defineConfig({
target: 'http://localhost:8088',
changeOrigin: true,
secure: false,
},
'/ws': {
target: 'http://localhost:8080',
changeOrigin: true,
secure: false,
ws: true, // enable WebSocket proxying
},
'/chatbot': {
target: 'http://localhost:8081',
changeOrigin: true,
secure: false,
rewrite: (path) => path.replace(/^\/chatbot/, ''),
}
}
}
Expand Down
Loading