Skip to content
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
7 changes: 5 additions & 2 deletions internal/services/buildsystem.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,11 @@ func GenerateMavenWrapper(serviceDir string) error {
return fmt.Errorf("%s path %s is not executable or inaccessible: %v", mvnExecutable, mvnPath, err)
}

// Execute mvn -N wrapper:wrapper
cmd := exec.Command(mvnPath, "-N", "wrapper:wrapper")
// Execute mvn -N io.takari:maven:wrapper
cmd := exec.Command(mvnPath, "-N", "io.takari:maven:wrapper")

fmt.Printf("[DEBUG] Executing command: %s\n", strings.Join(cmd.Args, " "))

cmd.Dir = serviceDir
output, err := cmd.CombinedOutput()
if err != nil {
Expand Down
121 changes: 65 additions & 56 deletions web/src/components/ui/toast.tsx
Original file line number Diff line number Diff line change
@@ -1,79 +1,81 @@
import * as React from "react"
import { cn } from "@/lib/utils"
import { X, CheckCircle, AlertCircle, Info, AlertTriangle } from "lucide-react"
import * as React from "react";
import { cn } from "@/lib/utils";
import { X, CheckCircle, AlertCircle, Info, AlertTriangle } from "lucide-react";

const TOAST_LIMIT = 5
const TOAST_LIMIT = 5;

export type ToastVariant = "default" | "success" | "error" | "warning" | "info"
export type ToastVariant = "default" | "success" | "error" | "warning" | "info";

export interface Toast {
id: string
title?: string
description?: string
variant?: ToastVariant
duration?: number
action?: React.ReactNode
id: string;
title?: string;
description?: string;
variant?: ToastVariant;
duration?: number;
action?: React.ReactNode;
}

interface ToastContextType {
toasts: Toast[]
addToast: (toast: Omit<Toast, "id">) => void
removeToast: (id: string) => void
removeAllToasts: () => void
toasts: Toast[];
addToast: (toast: Omit<Toast, "id">) => void;
removeToast: (id: string) => void;
removeAllToasts: () => void;
}

const ToastContext = React.createContext<ToastContextType | undefined>(undefined)
const ToastContext = React.createContext<ToastContextType | undefined>(
undefined,
);

export function useToast() {
const context = React.useContext(ToastContext)
const context = React.useContext(ToastContext);
if (!context) {
throw new Error("useToast must be used within a ToastProvider")
throw new Error("useToast must be used within a ToastProvider");
}
return context
return context;
}

let toastCount = 0
let toastCount = 0;

function generateId() {
toastCount = (toastCount + 1) % Number.MAX_SAFE_INTEGER
return toastCount.toString()
toastCount = (toastCount + 1) % Number.MAX_SAFE_INTEGER;
return toastCount.toString();
}

export function ToastProvider({ children }: { children: React.ReactNode }) {
const [toasts, setToasts] = React.useState<Toast[]>([])
const [toasts, setToasts] = React.useState<Toast[]>([]);

const addToast = React.useCallback((toast: Omit<Toast, "id">) => {
const id = generateId()
const id = generateId();
const newToast: Toast = {
...toast,
id,
duration: toast.duration ?? 5000,
}
};

setToasts((currentToasts) => {
const updatedToasts = [...currentToasts, newToast]
const updatedToasts = [...currentToasts, newToast];
if (updatedToasts.length > TOAST_LIMIT) {
return updatedToasts.slice(-TOAST_LIMIT)
return updatedToasts.slice(-TOAST_LIMIT);
}
return updatedToasts
})
return updatedToasts;
});

if (newToast.duration && newToast.duration > 0) {
setTimeout(() => {
removeToast(id)
}, newToast.duration)
removeToast(id);
}, newToast.duration);
}
}, [])
}, []);

const removeToast = React.useCallback((id: string) => {
setToasts((currentToasts) =>
currentToasts.filter((toast) => toast.id !== id)
)
}, [])
setToasts((currentToasts) =>
currentToasts.filter((toast) => toast.id !== id),
);
}, []);

const removeAllToasts = React.useCallback(() => {
setToasts([])
}, [])
setToasts([]);
}, []);

return (
<ToastContext.Provider
Expand All @@ -86,59 +88,66 @@ export function ToastProvider({ children }: { children: React.ReactNode }) {
>
{children}
</ToastContext.Provider>
)
);
}

const toastVariants = {
default: "bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 text-gray-900 dark:text-gray-100",
success: "bg-green-50 dark:bg-green-900/20 border-green-200 dark:border-green-700 text-green-900 dark:text-green-100",
error: "bg-red-50 dark:bg-red-900/20 border-red-200 dark:border-red-700 text-red-900 dark:text-red-100",
warning: "bg-yellow-50 dark:bg-yellow-900/20 border-yellow-200 dark:border-yellow-700 text-yellow-900 dark:text-yellow-100",
default:
"bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 text-gray-900 dark:text-gray-100",
success:
"bg-green-50 dark:bg-green-900/20 border-green-200 dark:border-green-700 text-green-900 dark:text-green-100",
error:
"bg-red-50 dark:bg-red-900/20 border-red-200 dark:border-red-700 text-red-900 dark:text-red-100",
warning:
"bg-yellow-50 dark:bg-yellow-900/20 border-yellow-200 dark:border-yellow-700 text-yellow-900 dark:text-yellow-100",
info: "bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-700 text-blue-900 dark:text-blue-100",
}
};

const iconVariants = {
default: Info,
success: CheckCircle,
error: AlertCircle,
warning: AlertTriangle,
info: Info,
}
};

const iconColors = {
default: "text-gray-500 dark:text-gray-400",
success: "text-green-500 dark:text-green-400",
error: "text-red-500 dark:text-red-400",
warning: "text-yellow-500 dark:text-yellow-400",
info: "text-blue-500 dark:text-blue-400",
}
};

export function ToastContainer() {
const { toasts, removeToast } = useToast()
const { toasts, removeToast } = useToast();

return (
<div className="fixed top-4 right-4 z-50 flex flex-col gap-2 max-w-sm w-full">
{toasts.map((toast) => {
const Icon = iconVariants[toast.variant || "default"]
const Icon = iconVariants[toast.variant || "default"];
return (
<div
key={toast.id}
className={cn(
"flex items-start gap-3 p-4 rounded-lg border shadow-lg animate-in slide-in-from-right-full",
toastVariants[toast.variant || "default"]
toastVariants[toast.variant || "default"],
)}
>
<Icon className={cn("h-5 w-5 mt-0.5 flex-shrink-0", iconColors[toast.variant || "default"])} />
<Icon
className={cn(
"h-5 w-5 mt-0.5 flex-shrink-0",
iconColors[toast.variant || "default"],
)}
/>
<div className="flex-1 min-w-0">
{toast.title && (
<div className="font-medium text-sm mb-1">{toast.title}</div>
)}
{toast.description && (
<div className="text-sm opacity-90">{toast.description}</div>
)}
{toast.action && (
<div className="mt-2">{toast.action}</div>
)}
{toast.action && <div className="mt-2">{toast.action}</div>}
</div>
<button
onClick={() => removeToast(toast.id)}
Expand All @@ -147,10 +156,10 @@ export function ToastContainer() {
<X className="h-4 w-4" />
</button>
</div>
)
);
})}
</div>
)
);
}

// Convenience functions
Expand All @@ -175,4 +184,4 @@ export const toast = {
description,
variant: "info" as const,
}),
}
};
14 changes: 11 additions & 3 deletions web/src/containers/ServiceModals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,20 @@ export function ServiceModals({ serviceManagement }: ServiceModalsProps) {
serviceName={serviceManagement.wrapperManagementData?.name || ""}
isOpen={serviceManagement.isWrapperManagementOpen}
onClose={serviceManagement.closeWrapperManagement}
onValidateWrapper={() => ServiceOperations.validateWrapper(serviceManagement.wrapperManagementData?.id || "")}
onValidateWrapper={() =>
ServiceOperations.validateWrapper(
serviceManagement.wrapperManagementData?.id || "",
)
}
onGenerateWrapper={async () => {
await ServiceOperations.generateWrapper(serviceManagement.wrapperManagementData?.id || "");
await ServiceOperations.generateWrapper(
serviceManagement.wrapperManagementData?.id || "",
);
}}
onRepairWrapper={async () => {
await ServiceOperations.repairWrapper(serviceManagement.wrapperManagementData?.id || "");
await ServiceOperations.repairWrapper(
serviceManagement.wrapperManagementData?.id || "",
);
}}
/>
</>
Expand Down
38 changes: 20 additions & 18 deletions web/src/contexts/AuthContext.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { createContext, useContext, useState, useEffect, ReactNode } from 'react';
import {
createContext,
useContext,
useState,
useEffect,
ReactNode,
} from "react";

interface User {
id: string;
Expand All @@ -24,7 +30,7 @@ const AuthContext = createContext<AuthContextType | undefined>(undefined);
export function useAuth() {
const context = useContext(AuthContext);
if (context === undefined) {
throw new Error('useAuth must be used within an AuthProvider');
throw new Error("useAuth must be used within an AuthProvider");
}
return context;
}
Expand All @@ -43,20 +49,20 @@ export function AuthProvider({ children }: AuthProviderProps) {
const login = (userData: User, authToken: string) => {
setUser(userData);
setToken(authToken);
localStorage.setItem('authToken', authToken);
localStorage.setItem('user', JSON.stringify(userData));
localStorage.setItem("authToken", authToken);
localStorage.setItem("user", JSON.stringify(userData));
};

const logout = () => {
setUser(null);
setToken(null);
localStorage.removeItem('authToken');
localStorage.removeItem('user');
localStorage.removeItem("authToken");
localStorage.removeItem("user");
};

const checkAuth = async (): Promise<boolean> => {
const storedToken = localStorage.getItem('authToken');
const storedUser = localStorage.getItem('user');
const storedToken = localStorage.getItem("authToken");
const storedUser = localStorage.getItem("user");

if (!storedToken || !storedUser) {
setIsLoading(false);
Expand All @@ -65,10 +71,10 @@ export function AuthProvider({ children }: AuthProviderProps) {

try {
// Verify token is still valid
const response = await fetch('/api/auth/user', {
const response = await fetch("/api/auth/user", {
headers: {
'Authorization': `Bearer ${storedToken}`,
'Content-Type': 'application/json',
Authorization: `Bearer ${storedToken}`,
"Content-Type": "application/json",
},
});

Expand All @@ -85,7 +91,7 @@ export function AuthProvider({ children }: AuthProviderProps) {
return false;
}
} catch (error) {
console.error('Auth check failed:', error);
console.error("Auth check failed:", error);
logout();
setIsLoading(false);
return false;
Expand All @@ -106,9 +112,5 @@ export function AuthProvider({ children }: AuthProviderProps) {
checkAuth,
};

return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
);
}
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
Loading
Loading