| @@ -1114,15 +1114,110 @@ function ToolkitWorkspace(props: { · 1113 unmodified lines |
| 1114 | 1114 | ); |
| 1115 | 1115 | } |
| 1116 | 1116 | |
| 1117 | | −function ToolkitsPageSkeleton() { |
| 1117 | +function ToolkitTileSkeleton(props: { index: number }) { |
| 1118 | 1118 | return ( |
| 1119 | | − <div className="flex min-h-0 flex-1 overflow-hidden"> |
| 1120 | | − <div className="flex-1 p-4"> |
| 1121 | | − <Skeleton className="h-8 w-full" /> |
| 1122 | | − <div className="grid grid-cols-1 gap-3 pt-4 sm:grid-cols-2 md:grid-cols-3"> |
| 1123 | | − {Array.from({ length: 6 }).map((_, index) => ( |
| 1124 | | − <Skeleton key={index} className="h-36 w-full rounded-md" /> |
| 1125 | | − ))} |
| 1119 | + <div |
| 1120 | + className="flex min-h-36 min-w-0 flex-col justify-between rounded-md border border-border/60 bg-card/35 p-3" |
| 1121 | + style={toolkitCardStyle} |
| 1122 | + aria-hidden="true" |
| 1123 | + > |
| 1124 | + <div className="flex min-w-0 items-start gap-3"> |
| 1125 | + <Skeleton className="size-9 shrink-0 rounded-md" /> |
| 1126 | + <div className="min-w-0 flex-1 space-y-2 pt-0.5"> |
| 1127 | + <Skeleton className="h-4" style={{ width: `${52 + ((props.index * 17) % 28)}%` }} /> |
| 1128 | + <Skeleton className="h-3" style={{ width: `${34 + ((props.index * 11) % 24)}%` }} /> |
| 1129 | + </div> |
| 1130 | + </div> |
| 1131 | + <div className="space-y-2"> |
| 1132 | + <Skeleton className="h-3 w-24" /> |
| 1133 | + <div className="flex min-w-0 items-center gap-1.5"> |
| 1134 | + <Skeleton className="h-5 w-14 rounded-full" /> |
| 1135 | + <Skeleton className="h-5 w-20 rounded-full" /> |
| 1136 | + </div> |
| 1137 | + </div> |
| 1138 | + </div> |
| 1139 | + ); |
| 1140 | +} |
| 1141 | + |
| 1142 | +function ToolkitSectionSkeleton(props: { title: string; offset: number }) { |
| 1143 | + return ( |
| 1144 | + <section className="space-y-3" aria-label={`Loading ${props.title.toLowerCase()} toolkits`}> |
| 1145 | + <div className="border-b border-border/60 pb-2"> |
| 1146 | + <h2 className="text-xs font-semibold uppercase tracking-widest text-muted-foreground"> |
| 1147 | + {props.title} |
| 1148 | + </h2> |
| 1149 | + </div> |
| 1150 | + |
| 1151 | + <div className="grid content-start grid-cols-1 gap-3 sm:grid-cols-2 md:grid-cols-3"> |
| 1152 | + {Array.from({ length: 3 }).map((_, index) => ( |
| 1153 | + <ToolkitTileSkeleton key={index} index={props.offset + index} /> |
| 1154 | + ))} |
| 1155 | + </div> |
| 1156 | + </section> |
| 1157 | + ); |
| 1158 | +} |
| 1159 | + |
| 1160 | +function ToolkitGridSkeleton() { |
| 1161 | + return ( |
| 1162 | + <div className="min-h-0 flex-1 overflow-y-auto"> |
| 1163 | + <div className="w-full space-y-7 px-4 py-4" style={toolkitGridContainerStyle}> |
| 1164 | + <ToolkitSectionSkeleton title="Workspace" offset={0} /> |
| 1165 | + <ToolkitSectionSkeleton title="Personal" offset={3} /> |
| 1166 | + </div> |
| 1167 | + </div> |
| 1168 | + ); |
| 1169 | +} |
| 1170 | + |
| 1171 | +function ToolkitDetailSkeleton() { |
| 1172 | + return ( |
| 1173 | + <div className="flex min-h-0 flex-1 flex-col overflow-hidden"> |
| 1174 | + <div className="shrink-0 border-b border-border/60 bg-background/95 px-4 py-3"> |
| 1175 | + <Skeleton className="mb-3 h-7 w-20 rounded-md" /> |
| 1176 | + <div className="space-y-2"> |
| 1177 | + <div className="flex min-w-0 items-center gap-2"> |
| 1178 | + <Skeleton className="h-4 w-40" /> |
| 1179 | + <Skeleton className="h-5 w-16 rounded-full" /> |
| 1180 | + <Skeleton className="h-3 w-12" /> |
| 1181 | + </div> |
| 1182 | + <Skeleton className="h-6 w-full max-w-md" /> |
| 1183 | + </div> |
| 1184 | + </div> |
| 1185 | + |
| 1186 | + <div className="flex min-h-0 flex-1 overflow-hidden"> |
| 1187 | + <div |
| 1188 | + className="flex min-h-0 shrink-0 flex-col border-r border-border/60" |
| 1189 | + style={toolkitToolTreeStyle} |
| 1190 | + > |
| 1191 | + <div className="border-b border-border/60 px-3 py-3"> |
| 1192 | + <div className="flex items-center justify-between gap-3"> |
| 1193 | + <div className="space-y-2"> |
| 1194 | + <Skeleton className="h-3 w-28" /> |
| 1195 | + <Skeleton className="h-3 w-14" /> |
| 1196 | + </div> |
| 1197 | + <Skeleton className="h-8 w-16 rounded-md" /> |
| 1198 | + </div> |
| 1199 | + </div> |
| 1200 | + <div className="space-y-1 p-3"> |
| 1201 | + {Array.from({ length: 9 }).map((_, index) => ( |
| 1202 | + <div key={index} className="flex items-center gap-2 rounded-md px-2 py-1.5"> |
| 1203 | + <Skeleton className="size-4 shrink-0 rounded" /> |
| 1204 | + <Skeleton className="h-3.5" style={{ width: `${48 + ((index * 13) % 38)}%` }} /> |
| 1205 | + </div> |
| 1206 | + ))} |
| 1207 | + </div> |
| 1208 | + </div> |
| 1209 | + <div className="flex min-w-0 flex-1 flex-col gap-5 overflow-hidden p-6"> |
| 1210 | + <div className="space-y-2"> |
| 1211 | + <Skeleton className="h-5 w-52" /> |
| 1212 | + <Skeleton className="h-4 w-full max-w-lg" /> |
| 1213 | + <Skeleton className="h-4 w-full max-w-sm" /> |
| 1214 | + </div> |
| 1215 | + <div className="space-y-3"> |
| 1216 | + <Skeleton className="h-4 w-24" /> |
| 1217 | + <Skeleton className="h-24 w-full rounded-md" /> |
| 1218 | + <Skeleton className="h-4 w-28" /> |
| 1219 | + <Skeleton className="h-24 w-full rounded-md" /> |
| 1220 | + </div> |
| 1126 | 1221 | </div> |
| 1127 | 1222 | </div> |
| 1128 | 1223 | </div> |
| @@ -1193,7 +1288,7 @@ function ToolkitDetailView(props: { · 64 unmodified lines |
| 1193 | 1288 | return <div className="p-6 text-sm text-destructive">Failed to load toolkit</div>; |
| 1194 | 1289 | } |
| 1195 | 1290 | if (!AsyncResult.isSuccess(policies) || !AsyncResult.isSuccess(connections)) { |
| 1196 | | − return <ToolkitsPageSkeleton />; |
| 1291 | + return <ToolkitDetailSkeleton />; |
| 1197 | 1292 | } |
| 1198 | 1293 | |
| 1199 | 1294 | return ( |
| @@ -1234,6 +1329,10 @@ export function ToolkitsPage(props: PluginPageProps) { · 34 unmodified lines |
| 1234 | 1329 | const integrationRows = AsyncResult.isSuccess(integrations) |
| 1235 | 1330 | ? (integrations.value as readonly Integration[]) |
| 1236 | 1331 | : []; |
| 1332 | + const toolkitsReady = AsyncResult.isSuccess(toolkits); |
| 1333 | + const toolkitsFailed = AsyncResult.isFailure(toolkits); |
| 1334 | + const toolsReady = AsyncResult.isSuccess(tools); |
| 1335 | + const toolsFailed = AsyncResult.isFailure(tools); |
| 1237 | 1336 | const navigateToIndex = () => |
| 1238 | 1337 | navigate({ |
| 1239 | 1338 | to: "/{-$orgSlug}/plugins/$pluginId/$", |
| @@ -1270,46 +1369,48 @@ export function ToolkitsPage(props: PluginPageProps) { · 30 unmodified lines |
| 1270 | 1369 | </div> |
| 1271 | 1370 | ) : null} |
| 1272 | 1371 | |
| 1273 | | − {!AsyncResult.isSuccess(toolkits) || !AsyncResult.isSuccess(tools) ? ( |
| 1274 | | − AsyncResult.isFailure(toolkits) || AsyncResult.isFailure(tools) ? ( |
| 1372 | + {!toolkitsReady ? ( |
| 1373 | + toolkitsFailed ? ( |
| 1275 | 1374 | <div className="p-6 text-sm text-destructive">Failed to load toolkits</div> |
| 1276 | 1375 | ) : ( |
| 1277 | | − <ToolkitsPageSkeleton /> |
| 1376 | + <ToolkitGridSkeleton /> |
| 1377 | + ) |
| 1378 | + ) : selectedToolkit ? ( |
| 1379 | + toolsFailed ? ( |
| 1380 | + <div className="p-6 text-sm text-destructive">Failed to load toolkit tools</div> |
| 1381 | + ) : !toolsReady ? ( |
| 1382 | + <ToolkitDetailSkeleton /> |
| 1383 | + ) : ( |
| 1384 | + <ToolkitDetailView |
| 1385 | + toolkit={selectedToolkit} |
| 1386 | + tools={toolRows} |
| 1387 | + integrations={integrationRows} |
| 1388 | + integrationPlugins={integrationPlugins} |
| 1389 | + orgSlug={params.orgSlug} |
| 1390 | + onBack={() => void navigateToIndex()} |
| 1391 | + onRemoveToolkit={removeToolkitHandler} |
| 1392 | + /> |
| 1278 | 1393 | ) |
| 1394 | + ) : selectedToolkitSlug !== null ? ( |
| 1395 | + <div className="flex min-h-0 flex-1 flex-col p-6"> |
| 1396 | + <Button |
| 1397 | + type="button" |
| 1398 | + variant="ghost" |
| 1399 | + size="xs" |
| 1400 | + onClick={() => void navigateToIndex()} |
| 1401 | + className="mb-4 w-fit -ml-1 text-muted-foreground" |
| 1402 | + > |
| 1403 | + <ArrowLeftIcon className="size-3.5" /> |
| 1404 | + Toolkits |
| 1405 | + </Button> |
| 1406 | + <div className="text-sm text-muted-foreground">Toolkit not found</div> |
| 1407 | + </div> |
| 1279 | 1408 | ) : ( |
| 1280 | | − <> |
| 1281 | | − {selectedToolkit ? ( |
| 1282 | | − <ToolkitDetailView |
| 1283 | | − toolkit={selectedToolkit} |
| 1284 | | − tools={toolRows} |
| 1285 | | − integrations={integrationRows} |
| 1286 | | − integrationPlugins={integrationPlugins} |
| 1287 | | − orgSlug={params.orgSlug} |
| 1288 | | − onBack={() => void navigateToIndex()} |
| 1289 | | − onRemoveToolkit={removeToolkitHandler} |
| 1290 | | − /> |
| 1291 | | − ) : selectedToolkitSlug !== null ? ( |
| 1292 | | − <div className="flex min-h-0 flex-1 flex-col p-6"> |
| 1293 | | − <Button |
| 1294 | | − type="button" |
| 1295 | | − variant="ghost" |
| 1296 | | − size="xs" |
| 1297 | | − onClick={() => void navigateToIndex()} |
| 1298 | | − className="mb-4 w-fit -ml-1 text-muted-foreground" |
| 1299 | | − > |
| 1300 | | − <ArrowLeftIcon className="size-3.5" /> |
| 1301 | | − Toolkits |
| 1302 | | − </Button> |
| 1303 | | − <div className="text-sm text-muted-foreground">Toolkit not found</div> |
| 1304 | | − </div> |
| 1305 | | − ) : ( |
| 1306 | | − <ToolkitGrid |
| 1307 | | − pluginId={props.pluginId} |
| 1308 | | − toolkits={toolkitRows} |
| 1309 | | − onCreate={createToolkitHandler} |
| 1310 | | − /> |
| 1311 | | − )} |
| 1312 | | − </> |
| 1409 | + <ToolkitGrid |
| 1410 | + pluginId={props.pluginId} |
| 1411 | + toolkits={toolkitRows} |
| 1412 | + onCreate={createToolkitHandler} |
| 1413 | + /> |
| 1313 | 1414 | )} |
| 1314 | 1415 | </div> |
| 1315 | 1416 | ); |