mirror of
https://github.com/infinilabs/coco-app.git
synced 2025-12-16 11:37:47 +01:00
* feat: add connect services * chore: adjust auth and userInfo * chore: add responsiveness to auth and userInfo * chore: add dark css * chore: handle /, to join the baseURL and url. * chore: use http:// or https:// rather http * chore: handle /, to join the baseURL and url. * chore: active current service * chore: connect * chore: data source & connect data * feat: add handleKeyDown open settings * chore: settings name
165 lines
5.2 KiB
TypeScript
165 lines
5.2 KiB
TypeScript
import { useState, useEffect } from "react";
|
|
import { Plus } from "lucide-react";
|
|
|
|
import cocoLogoImg from "@/assets/app-icon.png";
|
|
import { tauriFetch } from "@/api/tauriFetchClient";
|
|
import { useConnectStore } from "@/stores/connectStore";
|
|
import { useAppStore } from "@/stores/appStore";
|
|
|
|
interface SidebarProps {
|
|
addService: () => void;
|
|
}
|
|
|
|
type StringBooleanMap = {
|
|
[key: string]: boolean;
|
|
};
|
|
|
|
export function Sidebar({ addService }: SidebarProps) {
|
|
const defaultService = useConnectStore((state) => state.defaultService);
|
|
const currentService = useConnectStore((state) => state.currentService);
|
|
const otherServices = useConnectStore((state) => state.otherServices);
|
|
const setCurrentService = useConnectStore((state) => state.setCurrentService);
|
|
|
|
const setEndpoint = useAppStore((state) => state.setEndpoint);
|
|
|
|
const [defaultHealth, setDefaultHealth] = useState(false);
|
|
const [otherHealth, setOtherHealth] = useState<StringBooleanMap>({});
|
|
|
|
const addServiceClick = () => {
|
|
addService();
|
|
};
|
|
|
|
useEffect(() => {
|
|
getDefaultHealth();
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
getOtherHealth(currentService);
|
|
setEndpoint(currentService.endpoint);
|
|
}, [currentService.endpoint]);
|
|
|
|
const getDefaultHealth = () => {
|
|
let baseURL = defaultService.endpoint
|
|
if (baseURL.endsWith("/")) {
|
|
baseURL = baseURL.slice(0, -1);
|
|
}
|
|
tauriFetch({
|
|
url: `${baseURL}/health`,
|
|
method: "GET",
|
|
})
|
|
.then((res) => {
|
|
// "services": {
|
|
// "system_cluster": "yellow"
|
|
// },
|
|
// "status": "yellow"
|
|
setDefaultHealth(res.data?.status !== "red");
|
|
})
|
|
.catch((err) => {
|
|
console.error(err);
|
|
});
|
|
};
|
|
|
|
const getOtherHealth = (item: any) => {
|
|
if (!item.endpoint) return;
|
|
//
|
|
let baseURL = item.endpoint
|
|
if (baseURL.endsWith("/")) {
|
|
baseURL = baseURL.slice(0, -1);
|
|
}
|
|
tauriFetch({
|
|
url: `${baseURL}/health`,
|
|
method: "GET",
|
|
})
|
|
.then((res) => {
|
|
let obj = {
|
|
...otherHealth,
|
|
[item.endpoint]: res.data?.status !== "red",
|
|
};
|
|
setOtherHealth(obj);
|
|
})
|
|
.catch((err) => {
|
|
console.error(err);
|
|
});
|
|
};
|
|
|
|
return (
|
|
<div className="w-64 min-h-[550px] border-r border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800">
|
|
<div className="p-4 py-8">
|
|
<div
|
|
className={`flex items-center space-x-2 px-3 py-2 bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400 rounded-lg mb-6 ${
|
|
currentService.endpoint === defaultService.endpoint
|
|
? "border border-[rgba(0,135,255,1)]"
|
|
: ""
|
|
}`}
|
|
onClick={() => {
|
|
setCurrentService(defaultService);
|
|
setEndpoint(defaultService.endpoint);
|
|
getDefaultHealth();
|
|
}}
|
|
>
|
|
<img
|
|
src={defaultService.provider.icon || cocoLogoImg}
|
|
alt="cocoLogoImg"
|
|
className="w-5 h-5"
|
|
/>
|
|
|
|
<span className="font-medium">{defaultService.name}</span>
|
|
<div className="flex-1" />
|
|
<button className="text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300">
|
|
{defaultHealth ? (
|
|
<div className="w-3 h-3 rounded-full bg-[#00DB5E]"></div>
|
|
) : (
|
|
<div className="w-3 h-3 rounded-full bg-[#FF4747]"></div>
|
|
)}
|
|
</button>
|
|
</div>
|
|
|
|
<div className="text-sm font-medium text-gray-500 dark:text-gray-400 mb-2">
|
|
Third-party services
|
|
</div>
|
|
|
|
{otherServices?.map((item, index) => (
|
|
<div
|
|
key={item.name + index}
|
|
className={`flex items-center space-x-2 px-3 py-2 bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400 rounded-lg mb-2 ${
|
|
currentService.endpoint === item.endpoint
|
|
? "border border-[rgba(0,135,255,1)]"
|
|
: ""
|
|
}`}
|
|
onClick={() => {
|
|
setEndpoint(item.endpoint);
|
|
setCurrentService(item);
|
|
getOtherHealth(item);
|
|
}}
|
|
>
|
|
<img
|
|
src={item.provider.icon || cocoLogoImg}
|
|
alt="LogoImg"
|
|
className="w-5 h-5"
|
|
/>
|
|
|
|
<span className="font-medium">{item.name}</span>
|
|
<div className="flex-1" />
|
|
<button className="text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300">
|
|
{otherHealth[item.endpoint] ? (
|
|
<div className="w-3 h-3 rounded-full bg-[#00DB5E]"></div>
|
|
) : (
|
|
<div className="w-3 h-3 rounded-full bg-[#FF4747]"></div>
|
|
)}
|
|
</button>
|
|
</div>
|
|
))}
|
|
|
|
<div className="space-y-2">
|
|
<button
|
|
className="w-full flex items-center justify-center p-2 border-2 border-dashed border-gray-200 dark:border-gray-700 rounded-lg text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-300 hover:border-gray-300 dark:hover:border-gray-600"
|
|
onClick={addServiceClick}
|
|
>
|
|
<Plus className="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|