Skip to content
This repository was archived by the owner on Sep 30, 2025. It is now read-only.
Merged
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
3 changes: 2 additions & 1 deletion src/Interfaces/IProps_NotificationContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export interface SoundSettings {

export interface Props_NotificationContext {
showNotification: (title: string, message: string, icon: string, config?: SoundSettings) => void;
showBannerNotification: (title: string, message: string, permanent: boolean) => void; // New method
showBannerNotification: (title: string, message: string, permanent: boolean) => void;
showPopupNotification: (title: string, description: string, buttons: { text: string; onClick: () => void; type: "primary" | "secondary" | "danger" }[]) => void;
hideNotification: (id: number) => void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,13 @@ const FeatureButtonComponent: React.FC<FeatureButtonComponentProps> = ({
>
<IconComponent className="text-white" color={icon_color} size={30} />
</div>
{/* Tooltip */}
<motion.div
initial={{ opacity: 0, x: -30 }}
animate={{
opacity: showTooltip ? 1 : 0,
x: showTooltip ? 0 : -30,
pointerEvents: showTooltip ? "auto" : "none", // Enable or disable pointer events
pointerEvents: showTooltip ? "auto" : "none",
}}
className="absolute bg-red-500 z-20 min-h-12 w-auto max-w-[350px] rounded-md ml-14 pl-4 pr-4 pt-3 pb-3 flex flex-col select-none"
style={{ backgroundColor: bg_color }}>
Expand Down
33 changes: 33 additions & 0 deletions src/components/NotificationManager/Buttons.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.popup-btn-primary {
@apply h-[40px];
@apply text-[15px];
@apply font-medium;
@apply rounded-md;
@apply text-comment-color;
@apply px-5;
@apply bg-background-card;
@apply hover:bg-background-card-selected;
@apply hover:text-white;
}

.popup-btn-secondary {
@apply h-[40px];
@apply text-[15px];
@apply font-medium;
@apply rounded-md;
@apply text-comment-color;
@apply px-5;
@apply hover:text-white;
}

.popup-btn-danger {
@apply h-[40px];
@apply text-white;
@apply font-medium;
@apply rounded-md;
@apply px-3;
@apply text-white;
@apply bg-jenkins-job-red;
@apply hover:brightness-[0.9];
@apply active:brightness-[0.7]
}
89 changes: 81 additions & 8 deletions src/components/NotificationManager/NotificationContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import { BannerNotificcation, Notification } from "../../Interfaces/INotificatio
import { NOTIFICATION_CLOSE_TIME } from "../../config/constants";
import Logger from "../../helpers/Logger";

import "./Buttons.css";

// Audio
import notification_info from "../../assets/sounds/notification_info.mp3";
import notification_pop from "../../assets/sounds/notification_pop.mp3";
import notification_success from "../../assets/sounds/notification_success.mp3";
import notification_error from "../../assets/sounds/notification_error.mp3";
import { IcoCross } from "@/Icons/pack_1";
import { IcoCross, IcoError, IcoQuestionmark, IcoSuccess } from "@/Icons/pack_1";
import { Props_NotificationContext, SoundSettings } from "@/Interfaces/IProps_NotificationContext";


Expand All @@ -24,9 +26,6 @@ const notificationSounds = {
error: new Audio(notification_error),
};




const NotificationContext = createContext<Props_NotificationContext | undefined>(undefined);

export function playAudio(soundType: "success" | "error" | "pop" | "info") {
Expand All @@ -45,7 +44,7 @@ export function useNotification() {
return context;
} catch (error) {
Logger.error("NotificationContext.tsx", "Error using notification:", error);
return { showNotification: () => { }, showBannerNotification: () => { }, hideNotification: () => { } };
return { showNotification: () => { }, showBannerNotification: () => { }, showPopupNotification: () => { }, hideNotification: () => { } };
}
}

Expand All @@ -55,11 +54,21 @@ interface Props_NotificationProvider {
children: ReactNode;
}

interface PopupNotification {
title: string;
description: string;
buttons: {
text: string;
onClick: () => void;
type: "primary" | "secondary" | "danger";
}[];
}


export const NotificationProvider: React.FC<Props_NotificationProvider> = ({ children }) => {
const [notifications, setNotifications] = useState<Notification[]>([]);
const [bannerNotification, setBannerNotification] = useState<BannerNotificcation | null>(null);
const [popupNotification, setPopupNotification] = useState<PopupNotification | null>(null);

const showNotification = (title: string, message: string, icon: string, config: SoundSettings = {
soundOn: true,
Expand Down Expand Up @@ -99,12 +108,24 @@ export const NotificationProvider: React.FC<Props_NotificationProvider> = ({ chi
setBannerNotification(newNotification);
};

// function using PopupNotification for props
const showPopupNotification = (title: string, description: string, buttons: { text: string; onClick: () => void; type: "primary" | "secondary" | "danger" }[]) => {
const newNotification: PopupNotification = {
title,
description,
buttons,
};

setPopupNotification(newNotification);
};


const hideNotification = (id: number) => {
setNotifications((prevNotifications) => prevNotifications.filter((n) => n.id !== id));
};

return (
<NotificationContext.Provider value={{ showNotification, showBannerNotification, hideNotification }}>
<NotificationContext.Provider value={{ showNotification, showBannerNotification, showPopupNotification, hideNotification }}>
<div className="absolute bottom-[30px] right-[30px] space-y-2 w-[300px] z-50 select-none cursor-pointer">
{notifications.map((notification) => (
<motion.div
Expand Down Expand Up @@ -137,20 +158,72 @@ export const NotificationProvider: React.FC<Props_NotificationProvider> = ({ chi
))}
</div>
{bannerNotification && (
<motion.div
<motion.div
initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }}
className="bg-jenkins-job-red absolute bottom-0 left-0 w-screen min-h-[50px] z-50 flex flex-row items-center justify-between p-4 select-none">
<p><b>{bannerNotification.title}</b> {String(bannerNotification.message)}</p>
{!bannerNotification.permanent && (
<span
<span
className="mx-8"
onClick={() => setBannerNotification(null)}>
<IcoCross className="hover:scale-[1.1] active:scale-[0.95] transition cursor-pointer" />
</span>
)}
</motion.div>
)}

{popupNotification && (
<>
<motion.div
className="fixed top-0 left-0 w-full h-full z-50 bg-black bg-opacity-80 flex items-center justify-center"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={() => setPopupNotification(null)}
>
<motion.div
initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }}
className="bg-background-sidebar p-4 rounded-md w-[500px] min-h-[200px] flex flex-col justify-between"
>
{/* Text Section */}
<div className="pt-4 px-4 grid grid-cols-[48px_auto] gap-4">
{/* if buttons contain danger type, show danger icon if not show icosuccess */}
{popupNotification.buttons.some((button) => button.type === "danger") ? (
<div className="h-[48px] w-[48px] flex justify-center items-center bg-red-500 bg-opacity-30 rounded-full">
<IcoError size={30} color="#f22c3d"/>
</div>
) : (
<div className="h-[48px] w-[48px] flex justify-center items-center bg-gray-500 bg-opacity-30 rounded-full">
<IcoQuestionmark size={30}/>
</div>
)}
<div className="w-auto">
<p className="font-bold text-xl word-break">{popupNotification.title}</p>
<p className="text-md word-break">{popupNotification.description}</p>
</div>
</div>
{/* Button Section */}
<div className="flex justify-end space-x-4">
{popupNotification.buttons.map((button, index) => (
<button
key={index}
className={button.type === "primary" ? "popup-btn-primary" : button.type === "secondary" ? "popup-btn-secondary" : "popup-btn-danger"}
onClick={() => {
button.onClick();
setPopupNotification(null);
}}
>
{button.text}
</button>
))}
</div>
</motion.div>
</motion.div>

</>
)}
{children}
</NotificationContext.Provider>
);
Expand Down
18 changes: 17 additions & 1 deletion src/screens/Jarvis/Utils/JarivsUtils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,23 @@ export class JarvisUtils {
}
case "stop_build": {
if (!selectedBuildData) throw new Error("selectedBuildData is undefined");
this.stopBuild(activeJobBuild);
this.notification.showPopupNotification("Stop Build", "Are you sure you want to stop this build?",
[
{
"text": "Yes, Stop Build",
"type": "danger",
"onClick": () => {
this.stopBuild(activeJobBuild);
}
},
{
"text": "Cancel",
"type": "secondary",
"onClick": () => {
return;
}
}
])
return;
break;
}
Expand Down
40 changes: 40 additions & 0 deletions src/screens/Jarvis/Views/SettingsView/SettingsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,46 @@ const SettingsView: React.FC = () => {
Show Banner
</button>
</div>
{/* Show Popup */}
<div className="flex flex-col h-full">
<p className="mb-2 text-lg font-bold">Popup Notification</p>
<p className="mb-2 leading-5 text-comment-color">...</p>
</div>
<div className="h-full">
<button
onClick={() => {
notification.showPopupNotification(
"Lorem ipsum dolor sit amet",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor",
[
{
"text": "Button 1",
onClick: () => {
notification.showNotification("Button 1 Clicked", "Button 1 was clicked", "success", {
soundOn: true,
soundType: "success",
});
},
"type": "primary"
},
{
"text": "Button 2",
onClick: () => {
notification.showNotification("Button 2 Clicked", "Button 2 was clicked", "success", {
soundOn: true,
soundType: "success",
});
},
"type": "primary"
},
]

)
}}
className="button_danger_zone">
Show Popup
</button>
</div>
</div>
</div>
</>
Expand Down