repositories
loading repo index
repositories
loading repo index
repository
loading code, commits, and activity
public Clawd ADK gateway launch mirror
stars
latest
clone command
git clone gitlawb://did:key:z6Mkq5mY...iFZ5/my-project-publ...git clone gitlawb://did:key:z6Mkq5mY.../my-project-publ...2fa351d6docs: add automaton and perps launch sources16d ago| #1 | import React, { useState } from "react"; |
| #2 | import { Button } from "@/components/ui/button"; |
| #3 | import { PauseIcon, Loader2, PlayIcon } from "lucide-react"; |
| #4 | import { useAppsApi } from "@/hooks/useAppsApi"; |
| #5 | import Image from "next/image"; |
| #6 | import { useDispatch, useSelector } from "react-redux"; |
| #7 | import { setAppDetails } from "@/store/appsSlice"; |
| #8 | import { BiEdit } from "react-icons/bi"; |
| #9 | import { constants } from "@/components/shared/source-app"; |
| #10 | import { RootState } from "@/store/store"; |
| #11 | |
| #12 | const capitalize = (str: string) => { |
| #13 | return str.charAt(0).toUpperCase() + str.slice(1); |
| #14 | }; |
| #15 | |
| #16 | const AppDetailCard = ({ |
| #17 | appId, |
| #18 | selectedApp, |
| #19 | }: { |
| #20 | appId: string; |
| #21 | selectedApp: any; |
| #22 | }) => { |
| #23 | const { updateAppDetails } = useAppsApi(); |
| #24 | const [isLoading, setIsLoading] = useState(false); |
| #25 | const dispatch = useDispatch(); |
| #26 | const apps = useSelector((state: RootState) => state.apps.apps); |
| #27 | const currentApp = apps.find((app: any) => app.id === appId); |
| #28 | const appConfig = currentApp |
| #29 | ? constants[currentApp.name as keyof typeof constants] || constants.default |
| #30 | : constants.default; |
| #31 | |
| #32 | const handlePauseAccess = async () => { |
| #33 | setIsLoading(true); |
| #34 | try { |
| #35 | await updateAppDetails(appId, { |
| #36 | is_active: !selectedApp.details.is_active, |
| #37 | }); |
| #38 | dispatch( |
| #39 | setAppDetails({ appId, isActive: !selectedApp.details.is_active }) |
| #40 | ); |
| #41 | } catch (error) { |
| #42 | console.error("Failed to toggle app pause state:", error); |
| #43 | } finally { |
| #44 | setIsLoading(false); |
| #45 | } |
| #46 | }; |
| #47 | |
| #48 | const buttonText = selectedApp.details.is_active |
| #49 | ? "Pause Access" |
| #50 | : "Unpause Access"; |
| #51 | |
| #52 | return ( |
| #53 | <div> |
| #54 | <div className="bg-zinc-900 border w-[320px] border-zinc-800 rounded-xl mb-6"> |
| #55 | <div className="flex items-center gap-2 mb-4 bg-zinc-800 rounded-t-xl p-3"> |
| #56 | <div className="w-5 h-5 flex items-center justify-center"> |
| #57 | {appConfig.iconImage ? ( |
| #58 | <div> |
| #59 | <div className="w-6 h-6 rounded-full bg-zinc-700 flex items-center justify-center overflow-hidden"> |
| #60 | <Image |
| #61 | src={appConfig.iconImage} |
| #62 | alt={appConfig.name} |
| #63 | width={40} |
| #64 | height={40} |
| #65 | /> |
| #66 | </div> |
| #67 | </div> |
| #68 | ) : ( |
| #69 | <div className="w-5 h-5 flex items-center justify-center bg-zinc-700 rounded-full"> |
| #70 | <BiEdit className="w-4 h-4 text-zinc-400" /> |
| #71 | </div> |
| #72 | )} |
| #73 | </div> |
| #74 | <h2 className="text-md font-semibold">{appConfig.name}</h2> |
| #75 | </div> |
| #76 | |
| #77 | <div className="space-y-4 p-3"> |
| #78 | <div> |
| #79 | <p className="text-xs text-zinc-400">Access Status</p> |
| #80 | <p |
| #81 | className={`font-medium ${ |
| #82 | selectedApp.details.is_active |
| #83 | ? "text-emerald-500" |
| #84 | : "text-red-500" |
| #85 | }`} |
| #86 | > |
| #87 | {capitalize( |
| #88 | selectedApp.details.is_active ? "active" : "inactive" |
| #89 | )} |
| #90 | </p> |
| #91 | </div> |
| #92 | |
| #93 | <div> |
| #94 | <p className="text-xs text-zinc-400">Total Memories Created</p> |
| #95 | <p className="font-medium"> |
| #96 | {selectedApp.details.total_memories_created} Memories |
| #97 | </p> |
| #98 | </div> |
| #99 | |
| #100 | <div> |
| #101 | <p className="text-xs text-zinc-400">Total Memories Accessed</p> |
| #102 | <p className="font-medium"> |
| #103 | {selectedApp.details.total_memories_accessed} Memories |
| #104 | </p> |
| #105 | </div> |
| #106 | |
| #107 | <div> |
| #108 | <p className="text-xs text-zinc-400">First Accessed</p> |
| #109 | <p className="font-medium"> |
| #110 | {selectedApp.details.first_accessed |
| #111 | ? new Date( |
| #112 | selectedApp.details.first_accessed |
| #113 | ).toLocaleDateString("en-US", { |
| #114 | day: "numeric", |
| #115 | month: "short", |
| #116 | year: "numeric", |
| #117 | hour: "numeric", |
| #118 | minute: "numeric", |
| #119 | }) |
| #120 | : "Never"} |
| #121 | </p> |
| #122 | </div> |
| #123 | |
| #124 | <div> |
| #125 | <p className="text-xs text-zinc-400">Last Accessed</p> |
| #126 | <p className="font-medium"> |
| #127 | {selectedApp.details.last_accessed |
| #128 | ? new Date( |
| #129 | selectedApp.details.last_accessed |
| #130 | ).toLocaleDateString("en-US", { |
| #131 | day: "numeric", |
| #132 | month: "short", |
| #133 | year: "numeric", |
| #134 | hour: "numeric", |
| #135 | minute: "numeric", |
| #136 | }) |
| #137 | : "Never"} |
| #138 | </p> |
| #139 | </div> |
| #140 | |
| #141 | <hr className="border-zinc-800" /> |
| #142 | |
| #143 | <div className="flex gap-2 justify-end"> |
| #144 | <Button |
| #145 | onClick={handlePauseAccess} |
| #146 | className="flex bg-transparent w-[170px] bg-zinc-800 border-zinc-800 hover:bg-zinc-800 text-white" |
| #147 | size="sm" |
| #148 | disabled={isLoading} |
| #149 | > |
| #150 | {isLoading ? ( |
| #151 | <Loader2 className="h-4 w-4 animate-spin" /> |
| #152 | ) : buttonText === "Pause Access" ? ( |
| #153 | <PauseIcon className="h-4 w-4" /> |
| #154 | ) : ( |
| #155 | <PlayIcon className="h-4 w-4" /> |
| #156 | )} |
| #157 | {buttonText} |
| #158 | </Button> |
| #159 | </div> |
| #160 | </div> |
| #161 | </div> |
| #162 | </div> |
| #163 | ); |
| #164 | }; |
| #165 | |
| #166 | export default AppDetailCard; |
| #167 |