| @@ -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 | return ( | 1118 | return ( |
| 1119 | <div className="flex min-h-0 flex-1 overflow-hidden"> | 1119 | <div |
| 1120 | <div className="flex-1 p-4"> | 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 | <Skeleton className="h-8 w-full" /> | 1121 | style={toolkitCardStyle} |
| 1122 | <div className="grid grid-cols-1 gap-3 pt-4 sm:grid-cols-2 md:grid-cols-3"> | 1122 | aria-hidden="true" |
| 1123 | {Array.from({ length: 6 }).map((_, index) => ( | 1123 | > |
| 1124 | <Skeleton key={index} className="h-36 w-full rounded-md" /> | 1124 | <div className="flex min-w-0 items-start gap-3"> |
| 1125 | ))} | 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 | </div> | 1221 | </div> |
| 1127 | </div> | 1222 | </div> |
| 1128 | </div> | 1223 | </div> |
| @@ -1193,7 +1288,7 @@ function ToolkitDetailView(props: { · 64 unmodified lines |
| 1193 | return <div className="p-6 text-sm text-destructive">Failed to load toolkit</div>; | 1288 | return <div className="p-6 text-sm text-destructive">Failed to load toolkit</div>; |
| 1194 | } | 1289 | } |
| 1195 | if (!AsyncResult.isSuccess(policies) || !AsyncResult.isSuccess(connections)) { | 1290 | if (!AsyncResult.isSuccess(policies) || !AsyncResult.isSuccess(connections)) { |
| 1196 | return <ToolkitsPageSkeleton />; | 1291 | return <ToolkitDetailSkeleton />; |
| 1197 | } | 1292 | } |
| 1198 | | 1293 | |
| 1199 | return ( | 1294 | return ( |
| @@ -1234,6 +1329,10 @@ export function ToolkitsPage(props: PluginPageProps) { · 34 unmodified lines |
| 1234 | const integrationRows = AsyncResult.isSuccess(integrations) | 1329 | const integrationRows = AsyncResult.isSuccess(integrations) |
| 1235 | ? (integrations.value as readonly Integration[]) | 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 | const navigateToIndex = () => | 1336 | const navigateToIndex = () => |
| 1238 | navigate({ | 1337 | navigate({ |
| 1239 | to: "/{-$orgSlug}/plugins/$pluginId/$", | 1338 | to: "/{-$orgSlug}/plugins/$pluginId/$", |
| @@ -1270,46 +1369,48 @@ export function ToolkitsPage(props: PluginPageProps) { · 30 unmodified lines |
| 1270 | </div> | 1369 | </div> |
| 1271 | ) : null} | 1370 | ) : null} |
| 1272 | | 1371 | |
| 1273 | {!AsyncResult.isSuccess(toolkits) || !AsyncResult.isSuccess(tools) ? ( | 1372 | {!toolkitsReady ? ( |
| 1274 | AsyncResult.isFailure(toolkits) || AsyncResult.isFailure(tools) ? ( | 1373 | toolkitsFailed ? ( |
| 1275 | <div className="p-6 text-sm text-destructive">Failed to load toolkits</div> | 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 | <> | 1409 | <ToolkitGrid |
| 1281 | {selectedToolkit ? ( | 1410 | pluginId={props.pluginId} |
| 1282 | <ToolkitDetailView | 1411 | toolkits={toolkitRows} |
| 1283 | toolkit={selectedToolkit} | 1412 | onCreate={createToolkitHandler} |
| 1284 | tools={toolRows} | 1413 | /> |
| 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 | </> | | |
| 1313 | )} | 1414 | )} |
| 1314 | </div> | 1415 | </div> |
| 1315 | ); | 1416 | ); |