@hot-updater/console 0.36.7 → 1.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.output/nitro.json +1 -1
- package/.output/public/assets/AnalyticsCapabilityContext-BCke57-7.js +1 -0
- package/.output/public/assets/BundleActivityChart-CnsBe_1J.js +42 -0
- package/.output/public/assets/BundleIdDisplay-Dky75zLU.js +1 -0
- package/.output/public/assets/alert-C-1KvDW8.js +1 -0
- package/.output/public/assets/analytics-Bqz0c-BT.js +1 -0
- package/.output/public/assets/analytics-api-CJdrTLLO.js +1 -0
- package/.output/public/assets/api-B_Rh-uzC.js +1 -0
- package/.output/public/assets/api-keys-BurCFhzH.js +1 -0
- package/.output/public/assets/api-keys-api-BEDJdQg3.js +1 -0
- package/.output/public/assets/badge-Bf0fZ-Gx.js +1 -0
- package/.output/public/assets/chart-no-axes-combined-FaYMGmQS.js +1 -0
- package/.output/public/assets/createServerFn-gM2-LFBy.js +3 -0
- package/.output/public/assets/dialog-BS60BRr2.js +1 -0
- package/.output/public/assets/dist-DspNKfMj.js +1 -0
- package/.output/public/assets/download-O-yNBc9H.js +1 -0
- package/.output/public/assets/field-Buq148-A.js +1 -0
- package/.output/public/assets/index-kFhd2S78.js +10 -0
- package/.output/public/assets/installations-BrQ-od41.js +2 -0
- package/.output/public/assets/installations-CGlZ3ROz.js +1 -0
- package/.output/public/assets/lazyRouteComponent-rPzgA7S-.js +1 -0
- package/.output/public/assets/loader-circle-DCYF-wJl.js +1 -0
- package/.output/public/assets/preload-helper-TKOBYGYD.js +1 -0
- package/.output/public/assets/refresh-cw-n8XYbPPj.js +1 -0
- package/.output/public/assets/routes-CrdokJ80.js +1 -0
- package/.output/public/assets/routes-DcQnVajO.js +2 -0
- package/.output/public/assets/search-DLhbWw3W.js +1 -0
- package/.output/public/assets/shield-check-DE_hVuHu.js +1 -0
- package/.output/public/assets/signing-HRexfjsH.js +1 -0
- package/.output/public/assets/styles-eB4EtvFS.css +2 -0
- package/.output/public/assets/table-D8CLieHX.js +1 -0
- package/.output/public/assets/textarea-YJLTVAoq.js +1 -0
- package/.output/public/assets/useMutation-JsGVjQMY.js +1 -0
- package/.output/public/assets/useOpenInteractionType-4cRD5xO0.js +1 -0
- package/.output/server/__23tanstack-start-server-fn-resolver-DBP9TQ7K.mjs +146 -0
- package/.output/server/_libs/@base-ui/react+[...].mjs +16124 -0
- package/.output/server/_libs/@reduxjs/toolkit+[...].mjs +1554 -0
- package/.output/server/_libs/@tanstack/react-router+[...].mjs +41 -10
- package/.output/server/_libs/@tanstack/react-router-ssr-query+[...].mjs +132 -0
- package/.output/server/_libs/d3-array.mjs +231 -0
- package/.output/server/_libs/d3-color.mjs +330 -0
- package/.output/server/_libs/d3-format.mjs +252 -0
- package/.output/server/_libs/d3-interpolate.mjs +207 -0
- package/.output/server/_libs/d3-path.mjs +77 -0
- package/.output/server/_libs/d3-scale+[...].mjs +1907 -0
- package/.output/server/_libs/d3-shape.mjs +818 -0
- package/.output/server/_libs/decimal.js-light.mjs +884 -0
- package/.output/server/_libs/es-toolkit.mjs +1134 -0
- package/.output/server/_libs/eventemitter3.mjs +236 -0
- package/.output/server/_libs/immer.mjs +532 -0
- package/.output/server/_libs/lucide-react.mjs +141 -13
- package/.output/server/_libs/next-themes.mjs +2 -2
- package/.output/server/_libs/react-redux+[...].mjs +205 -0
- package/.output/server/_libs/recharts+[...].mjs +14117 -0
- package/.output/server/_libs/sonner.mjs +2 -2
- package/.output/server/_libs/tanstack__query-core.mjs +114 -1
- package/.output/server/_libs/tanstack__react-query.mjs +3 -4
- package/.output/server/_runtime.mjs +10 -1
- package/.output/server/_ssr/AnalyticsCapabilityContext-ui4DX8GO.mjs +15 -0
- package/.output/server/_ssr/BundleActivityChart-BiB9ZORH.mjs +288 -0
- package/.output/server/_ssr/BundleIdDisplay-Czk6iR6p.mjs +13 -0
- package/.output/server/_ssr/alert-Dim3MGY5.mjs +86 -0
- package/.output/server/_ssr/analytics-DtMTn8fn.mjs +858 -0
- package/.output/server/_ssr/analytics-api-Cg1M5v_H.mjs +71 -0
- package/.output/server/_ssr/analytics-input-fRRRn0uA.mjs +96 -0
- package/.output/server/_ssr/analytics-rpc-DY5uXvHT.mjs +155 -0
- package/.output/server/_ssr/api-BTSv1tZb.mjs +282 -0
- package/.output/server/_ssr/api-keys-BnM_o2nS.mjs +300 -0
- package/.output/server/_ssr/api-keys-api-CIywsSF5.mjs +60 -0
- package/.output/server/_ssr/api-keys-rpc-BSp1wkIV.mjs +136 -0
- package/.output/server/_ssr/api-rpc-cG5wd_tO.mjs +1468 -0
- package/.output/server/_ssr/auth-BF8mbAgF.mjs +21 -0
- package/.output/server/_ssr/auth-rpc-BrJ6obHJ.mjs +24 -0
- package/.output/server/_ssr/auth.server-DwKoZoQM.mjs +50 -0
- package/.output/server/_ssr/badge-DCxbEeof.mjs +26 -0
- package/.output/server/_ssr/bundle-signing-rpc-COoQhW8E.mjs +16 -0
- package/.output/server/_ssr/bundle-signing.server-D5u8KkMp.mjs +51 -0
- package/.output/server/_ssr/config-Dv8qLDZ0.mjs +13 -0
- package/.output/server/_ssr/config.server-DEiLhKmy.mjs +361 -0
- package/.output/server/_ssr/{createServerFn-DzU0k62V.mjs → createServerFn-B2hcXoPM.mjs} +1 -1
- package/.output/server/_ssr/createServerRpc-DB29C_5p.mjs +12 -0
- package/.output/server/_ssr/{sidebar-Cv_H0_Jv.mjs → createSsrRpc-9P0S0lJ3.mjs} +68 -50
- package/.output/server/_ssr/databaseRows-CwbNwYSl.mjs +166 -0
- package/.output/server/_ssr/deleteBundle-Cu_XOMt0.mjs +66 -0
- package/.output/server/_ssr/dialog-CQuRMNZ4.mjs +170 -0
- package/.output/server/_ssr/dist-BVSLTpoP.mjs +250 -0
- package/.output/server/_ssr/downloadBundle-CNnYxYO2.mjs +24 -0
- package/.output/server/_ssr/{empty-plugin-adapters-B4HCzgmM.mjs → empty-plugin-adapters-B5QvGpEl.mjs} +1 -1
- package/.output/server/_ssr/field-DRIXKjZG.mjs +74 -0
- package/.output/server/_ssr/{getBundleChildren-CVtSiH5t.mjs → getBundleChildren-BcRMFoDF.mjs} +10 -18
- package/.output/server/_ssr/installations-B-azgAvX.mjs +19 -0
- package/.output/server/_ssr/installations-Ctvzp2JH.mjs +535 -0
- package/.output/server/_ssr/router-C-KUBfOi.mjs +513 -0
- package/.output/server/_ssr/routes-BM4ht9jb.mjs +2286 -0
- package/.output/server/_ssr/routes-Z7TwC6nC.mjs +37 -0
- package/.output/server/_ssr/runtime.server-awTLiiXW.mjs +527 -0
- package/.output/server/_ssr/server-BjzmvXnO.mjs +7468 -0
- package/.output/server/_ssr/signing-k8GxyBDF.mjs +202 -0
- package/.output/server/_ssr/ssr.mjs +4 -5740
- package/.output/server/_ssr/start-DpViQGnt.mjs +4 -0
- package/.output/server/_ssr/table-v1yCvYo7.mjs +53 -0
- package/.output/server/_ssr/textarea-DYHnskhb.mjs +14 -0
- package/.output/server/_ssr/uuidv7-BbJ-9D_I.mjs +31 -0
- package/.output/server/_tanstack-start-manifest_v-CH4StzJu.mjs +108 -0
- package/.output/server/index.mjs +256 -53
- package/README.md +29 -8
- package/lib/index.d.mts +36 -0
- package/lib/index.mjs +4 -0
- package/lib/vite-internal.mjs +127 -0
- package/lib/vite.d.mts +10 -0
- package/lib/vite.mjs +5 -0
- package/package.json +51 -29
- package/public/apple-touch-icon.png +0 -0
- package/public/favicon-16x16.png +0 -0
- package/public/favicon-32x32.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/logo.svg +1 -0
- package/public/manifest.json +30 -0
- package/public/robots.txt +3 -0
- package/src/components/AppSidebar.tsx +159 -0
- package/src/components/BundleIdDisplay.tsx +18 -0
- package/src/components/ChannelBadge.tsx +31 -0
- package/src/components/ConsoleAccessPage.tsx +122 -0
- package/src/components/EnabledStatusIcon.tsx +54 -0
- package/src/components/HashValueDisplay.tsx +85 -0
- package/src/components/HotUpdaterLogo.tsx +30 -0
- package/src/components/NotFoundPage.tsx +33 -0
- package/src/components/PlatformIcon.tsx +39 -0
- package/src/components/RolloutPercentageBadge.tsx +28 -0
- package/src/components/ThemeProvider.tsx +116 -0
- package/src/components/TimestampDisplay.tsx +41 -0
- package/src/components/features/analytics/ActivityChart.tsx +150 -0
- package/src/components/features/analytics/AnalyticsCapabilityContext.tsx +24 -0
- package/src/components/features/analytics/AnalyticsControls.tsx +115 -0
- package/src/components/features/analytics/AnalyticsErrorAlert.tsx +21 -0
- package/src/components/features/analytics/AnalyticsOverview.tsx +208 -0
- package/src/components/features/analytics/BundleDistribution.tsx +133 -0
- package/src/components/features/analytics/BundleSelector.tsx +114 -0
- package/src/components/features/analytics/InstallationHistoryCard.tsx +248 -0
- package/src/components/features/analytics/InstallationMatchesCard.tsx +132 -0
- package/src/components/features/analytics/InstallationPageHeader.tsx +138 -0
- package/src/components/features/analytics/InstallationPagination.tsx +48 -0
- package/src/components/features/analytics/InstallationSearch.tsx +230 -0
- package/src/components/features/analytics/UpdateOutcomes.tsx +147 -0
- package/src/components/features/api-keys/ApiKeysPage.tsx +377 -0
- package/src/components/features/bundles/BundleActivityChart.tsx +193 -0
- package/src/components/features/bundles/BundleAnalyticsSummary.tsx +123 -0
- package/src/components/features/bundles/BundleChildrenPanel.tsx +201 -0
- package/src/components/features/bundles/BundleMetadata.tsx +136 -0
- package/src/components/features/bundles/RolloutCohortsDialog.tsx +201 -0
- package/src/components/features/channels/ChannelManagementDialog.tsx +228 -0
- package/src/components/features/releases/ReleaseEditorSheet.tsx +879 -0
- package/src/components/features/releases/ReleaseStateBadge.tsx +20 -0
- package/src/components/features/signing/BundleSigningPage.tsx +247 -0
- package/src/components/ui/alert-dialog.tsx +185 -0
- package/src/components/ui/alert.tsx +76 -0
- package/src/components/ui/badge.tsx +49 -0
- package/src/components/ui/button.tsx +58 -0
- package/src/components/ui/card.tsx +89 -0
- package/src/components/ui/chart.tsx +371 -0
- package/src/components/ui/combobox.tsx +303 -0
- package/src/components/ui/dialog.tsx +156 -0
- package/src/components/ui/dropdown-menu.tsx +270 -0
- package/src/components/ui/field.tsx +235 -0
- package/src/components/ui/input-group.tsx +147 -0
- package/src/components/ui/input.tsx +19 -0
- package/src/components/ui/label.tsx +25 -0
- package/src/components/ui/progress.tsx +84 -0
- package/src/components/ui/select.tsx +205 -0
- package/src/components/ui/separator.tsx +23 -0
- package/src/components/ui/sheet.tsx +132 -0
- package/src/components/ui/sidebar.tsx +779 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/slider.tsx +56 -0
- package/src/components/ui/sonner.tsx +45 -0
- package/src/components/ui/switch.tsx +25 -0
- package/src/components/ui/table.tsx +114 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/toggle-group.tsx +89 -0
- package/src/components/ui/toggle.tsx +43 -0
- package/src/components/ui/tooltip.tsx +66 -0
- package/src/hooks/use-mobile.ts +30 -0
- package/src/index.ts +48 -0
- package/src/lib/analytics-api.ts +145 -0
- package/src/lib/analytics-error.ts +19 -0
- package/src/lib/analytics-input.ts +187 -0
- package/src/lib/analytics-overview.ts +170 -0
- package/src/lib/analytics-rpc.ts +218 -0
- package/src/lib/api-keys-api.ts +69 -0
- package/src/lib/api-keys-rpc.ts +87 -0
- package/src/lib/api-rpc.ts +478 -0
- package/src/lib/api.ts +444 -0
- package/src/lib/auth-rpc.ts +16 -0
- package/src/lib/bundle-signing-api.ts +14 -0
- package/src/lib/bundle-signing-rpc.ts +16 -0
- package/src/lib/constants.ts +1 -0
- package/src/lib/git.ts +51 -0
- package/src/lib/server/auth.server.ts +43 -0
- package/src/lib/server/bundle-signing.server.ts +100 -0
- package/src/lib/server/config.server.ts +109 -0
- package/src/lib/server/console-runtime.server.ts +70 -0
- package/src/lib/server/deleteBundle.ts +145 -0
- package/src/lib/server/downloadBundle.ts +46 -0
- package/src/lib/server/getBundleChildren.ts +130 -0
- package/src/lib/server/listReleases.ts +61 -0
- package/src/lib/server/releaseActivity.ts +27 -0
- package/src/lib/server/releaseReachability.ts +95 -0
- package/src/lib/server/runtime.server.ts +172 -0
- package/src/lib/utils.ts +9 -0
- package/src/logo.svg +1 -0
- package/src/routeTree.gen.ts +199 -0
- package/src/router.tsx +21 -0
- package/src/routes/-installations-search.ts +19 -0
- package/src/routes/-releases-search.ts +74 -0
- package/src/routes/__root.tsx +174 -0
- package/src/routes/analytics.tsx +139 -0
- package/src/routes/api/auth/$.ts +19 -0
- package/src/routes/api/bundles/$bundleId/download.ts +17 -0
- package/src/routes/api-keys.tsx +37 -0
- package/src/routes/index.tsx +982 -0
- package/src/routes/installations.tsx +194 -0
- package/src/routes/signing.tsx +37 -0
- package/src/styles.css +152 -0
- package/src/virtual-modules.d.ts +21 -0
- package/src/vite-env.d.ts +1 -0
- package/src/vite-internal.ts +195 -0
- package/src/vite.ts +12 -0
- package/.output/public/assets/dist-DJX53faP.js +0 -3
- package/.output/public/assets/index-Iylis5kY.js +0 -10
- package/.output/public/assets/routes-HT21hs0C.js +0 -54
- package/.output/public/assets/styles-C0Re4quB.css +0 -2
- package/.output/server/__23tanstack-start-server-fn-resolver-CgwgZ0-i.mjs +0 -66
- package/.output/server/_libs/@floating-ui/core+[...].mjs +0 -698
- package/.output/server/_libs/@floating-ui/dom+[...].mjs +0 -644
- package/.output/server/_libs/@floating-ui/react-dom+[...].mjs +0 -839
- package/.output/server/_libs/@radix-ui/react-alert-dialog+[...].mjs +0 -2138
- package/.output/server/_libs/@radix-ui/react-popper+[...].mjs +0 -308
- package/.output/server/_libs/@radix-ui/react-select+[...].mjs +0 -1038
- package/.output/server/_libs/@tanstack/devtools-event-client+[...].mjs +0 -196
- package/.output/server/_libs/@tanstack/form-core+[...].mjs +0 -3228
- package/.output/server/_libs/@tanstack/react-form+[...].mjs +0 -430
- package/.output/server/_libs/@tanstack/react-table+[...].mjs +0 -2382
- package/.output/server/_libs/radix-ui__number.mjs +0 -6
- package/.output/server/_libs/radix-ui__primitive.mjs +0 -9
- package/.output/server/_libs/radix-ui__react-arrow.mjs +0 -23
- package/.output/server/_libs/radix-ui__react-collection.mjs +0 -78
- package/.output/server/_libs/radix-ui__react-direction.mjs +0 -13
- package/.output/server/_libs/radix-ui__react-label.mjs +0 -22
- package/.output/server/_libs/radix-ui__react-separator.mjs +0 -30
- package/.output/server/_libs/radix-ui__react-slider.mjs +0 -510
- package/.output/server/_libs/radix-ui__react-switch.mjs +0 -157
- package/.output/server/_libs/radix-ui__react-tooltip.mjs +0 -493
- package/.output/server/_ssr/api-rpc-BIbDUKSz.mjs +0 -292
- package/.output/server/_ssr/config.server-BSS366KT.mjs +0 -41
- package/.output/server/_ssr/deleteBundle-BgbZTtpW.mjs +0 -141
- package/.output/server/_ssr/dist-KboydRVk.mjs +0 -120
- package/.output/server/_ssr/router-C3hqEy3k.mjs +0 -307
- package/.output/server/_ssr/routes-B0lQvvlC.mjs +0 -3048
- package/.output/server/_ssr/start-02sLteYG.mjs +0 -4
- package/.output/server/_ssr/storageProfile-FVkjPoGb.mjs +0 -9
- package/.output/server/_tanstack-start-manifest_v-Bpx52WOU.mjs +0 -20
- package/.output/server/node_modules/tslib/modules/index.js +0 -70
- package/.output/server/node_modules/tslib/modules/package.json +0 -3
- package/.output/server/node_modules/tslib/package.json +0 -47
- package/.output/server/node_modules/tslib/tslib.js +0 -484
- package/.output/server/package.json +0 -9
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import type { Bundle } from "@hot-updater/plugin-core";
|
|
2
|
+
import { ArrowRight } from "lucide-react";
|
|
3
|
+
|
|
4
|
+
import { BundleIdDisplay } from "@/components/BundleIdDisplay";
|
|
5
|
+
import { TimestampDisplay } from "@/components/TimestampDisplay";
|
|
6
|
+
import { Badge } from "@/components/ui/badge";
|
|
7
|
+
import { Button } from "@/components/ui/button";
|
|
8
|
+
import { Skeleton } from "@/components/ui/skeleton";
|
|
9
|
+
import {
|
|
10
|
+
Table,
|
|
11
|
+
TableBody,
|
|
12
|
+
TableCell,
|
|
13
|
+
TableHead,
|
|
14
|
+
TableHeader,
|
|
15
|
+
TableRow,
|
|
16
|
+
} from "@/components/ui/table";
|
|
17
|
+
import { useIsMobile } from "@/hooks/use-mobile";
|
|
18
|
+
|
|
19
|
+
interface BundleChildrenPanelProps {
|
|
20
|
+
panelId: string;
|
|
21
|
+
bundle: Bundle;
|
|
22
|
+
bundles: Bundle[];
|
|
23
|
+
loading: boolean;
|
|
24
|
+
onDetailClick: (bundle: Bundle) => void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function BundleChildrenPanel({
|
|
28
|
+
panelId,
|
|
29
|
+
bundle,
|
|
30
|
+
bundles,
|
|
31
|
+
loading,
|
|
32
|
+
onDetailClick,
|
|
33
|
+
}: BundleChildrenPanelProps) {
|
|
34
|
+
const isMobile = useIsMobile();
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div
|
|
38
|
+
aria-live="polite"
|
|
39
|
+
className="border-t bg-muted/10 p-3 sm:p-4"
|
|
40
|
+
id={panelId}
|
|
41
|
+
>
|
|
42
|
+
<div className="flex flex-col gap-4">
|
|
43
|
+
<div className="flex flex-wrap items-center justify-between gap-3">
|
|
44
|
+
<div className="flex min-w-0 items-start gap-2 text-sm sm:items-center">
|
|
45
|
+
<span className="text-muted-foreground">Base bundle</span>
|
|
46
|
+
<BundleIdDisplay bundleId={bundle.id} fullOnMobile maxLength={18} />
|
|
47
|
+
</div>
|
|
48
|
+
<Badge variant="outline">
|
|
49
|
+
{bundles.length} {bundles.length === 1 ? "patch" : "patches"}
|
|
50
|
+
</Badge>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
{loading ? (
|
|
54
|
+
<div className="flex flex-col gap-2">
|
|
55
|
+
<Skeleton className="h-10 w-full" />
|
|
56
|
+
<Skeleton className="h-10 w-full" />
|
|
57
|
+
</div>
|
|
58
|
+
) : bundles.length > 0 ? (
|
|
59
|
+
<div className="flex flex-col gap-2">
|
|
60
|
+
<div className="text-xs font-semibold uppercase text-muted-foreground/70">
|
|
61
|
+
Patch bundles from this base
|
|
62
|
+
</div>
|
|
63
|
+
{isMobile ? (
|
|
64
|
+
<div className="flex flex-col gap-2">
|
|
65
|
+
{bundles.map((childBundle) => (
|
|
66
|
+
<div
|
|
67
|
+
className="rounded-md border bg-background p-3"
|
|
68
|
+
key={childBundle.id}
|
|
69
|
+
>
|
|
70
|
+
<div className="flex flex-col gap-3">
|
|
71
|
+
<div className="space-y-1">
|
|
72
|
+
<div className="text-[11px] font-medium uppercase text-muted-foreground/70">
|
|
73
|
+
Patch Bundle
|
|
74
|
+
</div>
|
|
75
|
+
<BundleIdDisplay
|
|
76
|
+
bundleId={childBundle.id}
|
|
77
|
+
fullOnMobile
|
|
78
|
+
maxLength={18}
|
|
79
|
+
/>
|
|
80
|
+
</div>
|
|
81
|
+
<div className="space-y-1">
|
|
82
|
+
<div className="text-[11px] font-medium uppercase text-muted-foreground/70">
|
|
83
|
+
Relation
|
|
84
|
+
</div>
|
|
85
|
+
<div className="flex flex-col items-start gap-1 text-sm">
|
|
86
|
+
<BundleIdDisplay
|
|
87
|
+
bundleId={bundle.id}
|
|
88
|
+
fullOnMobile
|
|
89
|
+
maxLength={12}
|
|
90
|
+
/>
|
|
91
|
+
<ArrowRight className="size-4 rotate-90 text-muted-foreground" />
|
|
92
|
+
<BundleIdDisplay
|
|
93
|
+
bundleId={childBundle.id}
|
|
94
|
+
fullOnMobile
|
|
95
|
+
maxLength={12}
|
|
96
|
+
/>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
<div className="flex items-center justify-between gap-3">
|
|
100
|
+
<div className="space-y-1">
|
|
101
|
+
<div className="text-[11px] font-medium uppercase text-muted-foreground/70">
|
|
102
|
+
Artifact
|
|
103
|
+
</div>
|
|
104
|
+
<Badge variant="secondary">bsdiff</Badge>
|
|
105
|
+
</div>
|
|
106
|
+
<div className="space-y-1 text-right">
|
|
107
|
+
<div className="text-[11px] font-medium uppercase text-muted-foreground/70">
|
|
108
|
+
Created
|
|
109
|
+
</div>
|
|
110
|
+
<div className="text-xs tabular-nums text-foreground">
|
|
111
|
+
<TimestampDisplay uuid={childBundle.id} />
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
<Button
|
|
116
|
+
className="w-full"
|
|
117
|
+
onClick={() => onDetailClick(childBundle)}
|
|
118
|
+
size="sm"
|
|
119
|
+
type="button"
|
|
120
|
+
variant="outline"
|
|
121
|
+
>
|
|
122
|
+
Detail
|
|
123
|
+
</Button>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
))}
|
|
127
|
+
</div>
|
|
128
|
+
) : (
|
|
129
|
+
<div className="overflow-x-auto rounded-md border bg-background">
|
|
130
|
+
<Table className="min-w-max">
|
|
131
|
+
<TableHeader>
|
|
132
|
+
<TableRow className="hover:bg-transparent">
|
|
133
|
+
<TableHead>Patch Bundle</TableHead>
|
|
134
|
+
<TableHead>Relation</TableHead>
|
|
135
|
+
<TableHead>Artifact</TableHead>
|
|
136
|
+
<TableHead>Created</TableHead>
|
|
137
|
+
<TableHead className="w-24 text-right">Detail</TableHead>
|
|
138
|
+
</TableRow>
|
|
139
|
+
</TableHeader>
|
|
140
|
+
<TableBody>
|
|
141
|
+
{bundles.map((childBundle) => (
|
|
142
|
+
<TableRow key={childBundle.id}>
|
|
143
|
+
<TableCell>
|
|
144
|
+
<BundleIdDisplay
|
|
145
|
+
bundleId={childBundle.id}
|
|
146
|
+
fullOnMobile
|
|
147
|
+
maxLength={18}
|
|
148
|
+
/>
|
|
149
|
+
</TableCell>
|
|
150
|
+
<TableCell>
|
|
151
|
+
<div className="flex min-w-[280px] flex-col items-start gap-1 sm:flex-row sm:items-center sm:gap-2">
|
|
152
|
+
<BundleIdDisplay
|
|
153
|
+
bundleId={bundle.id}
|
|
154
|
+
fullOnMobile
|
|
155
|
+
maxLength={12}
|
|
156
|
+
/>
|
|
157
|
+
<ArrowRight className="size-4 shrink-0 rotate-90 text-muted-foreground sm:rotate-0" />
|
|
158
|
+
<BundleIdDisplay
|
|
159
|
+
bundleId={childBundle.id}
|
|
160
|
+
fullOnMobile
|
|
161
|
+
maxLength={12}
|
|
162
|
+
/>
|
|
163
|
+
</div>
|
|
164
|
+
</TableCell>
|
|
165
|
+
<TableCell>
|
|
166
|
+
<Badge variant="secondary">bsdiff</Badge>
|
|
167
|
+
</TableCell>
|
|
168
|
+
<TableCell className="tabular-nums">
|
|
169
|
+
<TimestampDisplay uuid={childBundle.id} />
|
|
170
|
+
</TableCell>
|
|
171
|
+
<TableCell className="text-right">
|
|
172
|
+
<Button
|
|
173
|
+
onClick={() => onDetailClick(childBundle)}
|
|
174
|
+
size="sm"
|
|
175
|
+
type="button"
|
|
176
|
+
variant="outline"
|
|
177
|
+
>
|
|
178
|
+
Detail
|
|
179
|
+
</Button>
|
|
180
|
+
</TableCell>
|
|
181
|
+
</TableRow>
|
|
182
|
+
))}
|
|
183
|
+
</TableBody>
|
|
184
|
+
</Table>
|
|
185
|
+
</div>
|
|
186
|
+
)}
|
|
187
|
+
</div>
|
|
188
|
+
) : (
|
|
189
|
+
<div className="flex flex-col gap-2">
|
|
190
|
+
<div className="text-xs font-semibold uppercase text-muted-foreground/70">
|
|
191
|
+
Patch bundles from this base
|
|
192
|
+
</div>
|
|
193
|
+
<div className="rounded-md border bg-background p-3 text-sm text-muted-foreground">
|
|
194
|
+
No direct patch bundles.
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
)}
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
);
|
|
201
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getPatchBaseBundleId,
|
|
3
|
+
getPatchBaseFileHash,
|
|
4
|
+
getPatchFileHash,
|
|
5
|
+
} from "@hot-updater/core";
|
|
6
|
+
import type { Bundle, ReleaseRow } from "@hot-updater/plugin-core";
|
|
7
|
+
import { ExternalLink } from "lucide-react";
|
|
8
|
+
|
|
9
|
+
import { BundleIdDisplay } from "@/components/BundleIdDisplay";
|
|
10
|
+
import { HashValueDisplay } from "@/components/HashValueDisplay";
|
|
11
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
12
|
+
import { useConfigQuery } from "@/lib/api";
|
|
13
|
+
import { getCommitUrl } from "@/lib/git";
|
|
14
|
+
|
|
15
|
+
interface BundleMetadataProps {
|
|
16
|
+
readonly bundle: Bundle | null | undefined;
|
|
17
|
+
readonly release: ReleaseRow;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function Row({ label, value }: { label: string; value: React.ReactNode }) {
|
|
21
|
+
return (
|
|
22
|
+
<div className="flex flex-col gap-1.5 sm:flex-row sm:items-start sm:justify-between sm:gap-4">
|
|
23
|
+
<dt className="text-muted-foreground">{label}</dt>
|
|
24
|
+
<dd className="min-w-0 text-left text-sm sm:text-right">{value}</dd>
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function BundleMetadata({ bundle, release }: BundleMetadataProps) {
|
|
30
|
+
const { data: configData, isFetched } = useConfigQuery();
|
|
31
|
+
const patchBaseBundleId = bundle ? getPatchBaseBundleId(bundle) : null;
|
|
32
|
+
const hbcPatchFileHash = bundle ? getPatchFileHash(bundle) : null;
|
|
33
|
+
const hbcPatchBaseFileHash = bundle ? getPatchBaseFileHash(bundle) : null;
|
|
34
|
+
const gitCommitUrl =
|
|
35
|
+
bundle?.gitCommitHash && isFetched
|
|
36
|
+
? getCommitUrl(configData?.console.gitUrl, bundle.gitCommitHash)
|
|
37
|
+
: null;
|
|
38
|
+
const target =
|
|
39
|
+
release.strategy === "APP_VERSION"
|
|
40
|
+
? release.target_app_version
|
|
41
|
+
: release.fingerprint_hash;
|
|
42
|
+
const hasMetadata =
|
|
43
|
+
target ||
|
|
44
|
+
bundle?.gitCommitHash ||
|
|
45
|
+
bundle?.fileHash ||
|
|
46
|
+
patchBaseBundleId ||
|
|
47
|
+
hbcPatchBaseFileHash ||
|
|
48
|
+
hbcPatchFileHash;
|
|
49
|
+
|
|
50
|
+
if (!hasMetadata) return null;
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<Card>
|
|
54
|
+
<CardHeader className="p-4 pb-3">
|
|
55
|
+
<CardTitle className="text-sm font-medium">Metadata</CardTitle>
|
|
56
|
+
</CardHeader>
|
|
57
|
+
<CardContent className="px-4 pb-4">
|
|
58
|
+
<dl className="flex flex-col gap-3 text-sm">
|
|
59
|
+
<Row
|
|
60
|
+
label={
|
|
61
|
+
release.strategy === "APP_VERSION"
|
|
62
|
+
? "Target app version"
|
|
63
|
+
: "Fingerprint target"
|
|
64
|
+
}
|
|
65
|
+
value={
|
|
66
|
+
<span className="break-all font-mono text-xs" translate="no">
|
|
67
|
+
{target ?? "—"}
|
|
68
|
+
</span>
|
|
69
|
+
}
|
|
70
|
+
/>
|
|
71
|
+
{bundle?.gitCommitHash ? (
|
|
72
|
+
<Row
|
|
73
|
+
label="Git commit"
|
|
74
|
+
value={
|
|
75
|
+
<div className="flex items-center justify-start gap-1 sm:justify-end">
|
|
76
|
+
<HashValueDisplay
|
|
77
|
+
className={gitCommitUrl ? "text-primary" : undefined}
|
|
78
|
+
maxLength={12}
|
|
79
|
+
value={bundle.gitCommitHash}
|
|
80
|
+
/>
|
|
81
|
+
{gitCommitUrl ? (
|
|
82
|
+
<a
|
|
83
|
+
aria-label="Open git commit"
|
|
84
|
+
className="shrink-0 text-primary hover:underline"
|
|
85
|
+
href={gitCommitUrl}
|
|
86
|
+
rel="noopener noreferrer"
|
|
87
|
+
target="_blank"
|
|
88
|
+
>
|
|
89
|
+
<ExternalLink aria-hidden="true" />
|
|
90
|
+
</a>
|
|
91
|
+
) : null}
|
|
92
|
+
</div>
|
|
93
|
+
}
|
|
94
|
+
/>
|
|
95
|
+
) : null}
|
|
96
|
+
{bundle?.fileHash ? (
|
|
97
|
+
<Row
|
|
98
|
+
label="Bundle hash"
|
|
99
|
+
value={
|
|
100
|
+
<HashValueDisplay maxLength={16} value={bundle.fileHash} />
|
|
101
|
+
}
|
|
102
|
+
/>
|
|
103
|
+
) : null}
|
|
104
|
+
{patchBaseBundleId ? (
|
|
105
|
+
<Row
|
|
106
|
+
label="Patch base"
|
|
107
|
+
value={
|
|
108
|
+
<BundleIdDisplay
|
|
109
|
+
bundleId={patchBaseBundleId}
|
|
110
|
+
fullOnMobile
|
|
111
|
+
maxLength={18}
|
|
112
|
+
/>
|
|
113
|
+
}
|
|
114
|
+
/>
|
|
115
|
+
) : null}
|
|
116
|
+
{hbcPatchBaseFileHash ? (
|
|
117
|
+
<Row
|
|
118
|
+
label="Base hash"
|
|
119
|
+
value={
|
|
120
|
+
<HashValueDisplay maxLength={16} value={hbcPatchBaseFileHash} />
|
|
121
|
+
}
|
|
122
|
+
/>
|
|
123
|
+
) : null}
|
|
124
|
+
{hbcPatchFileHash ? (
|
|
125
|
+
<Row
|
|
126
|
+
label="Patch hash"
|
|
127
|
+
value={
|
|
128
|
+
<HashValueDisplay maxLength={16} value={hbcPatchFileHash} />
|
|
129
|
+
}
|
|
130
|
+
/>
|
|
131
|
+
) : null}
|
|
132
|
+
</dl>
|
|
133
|
+
</CardContent>
|
|
134
|
+
</Card>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getNumericCohortRolloutPosition,
|
|
3
|
+
NUMERIC_COHORT_SIZE,
|
|
4
|
+
normalizeRolloutCohortCount,
|
|
5
|
+
} from "@hot-updater/core";
|
|
6
|
+
import { List } from "lucide-react";
|
|
7
|
+
|
|
8
|
+
import { Badge } from "@/components/ui/badge";
|
|
9
|
+
import { Button } from "@/components/ui/button";
|
|
10
|
+
import {
|
|
11
|
+
Card,
|
|
12
|
+
CardContent,
|
|
13
|
+
CardDescription,
|
|
14
|
+
CardHeader,
|
|
15
|
+
CardTitle,
|
|
16
|
+
} from "@/components/ui/card";
|
|
17
|
+
import {
|
|
18
|
+
Dialog,
|
|
19
|
+
DialogContent,
|
|
20
|
+
DialogDescription,
|
|
21
|
+
DialogFooter,
|
|
22
|
+
DialogHeader,
|
|
23
|
+
DialogTitle,
|
|
24
|
+
DialogTrigger,
|
|
25
|
+
} from "@/components/ui/dialog";
|
|
26
|
+
import { useIsMobile } from "@/hooks/use-mobile";
|
|
27
|
+
|
|
28
|
+
interface RolloutCohortsDialogProps {
|
|
29
|
+
readonly releaseId: string;
|
|
30
|
+
readonly rolloutCohortCount: number | null | undefined;
|
|
31
|
+
readonly targetCohorts?: readonly string[] | null | undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const ALL_NUMERIC_COHORTS = Array.from(
|
|
35
|
+
{ length: NUMERIC_COHORT_SIZE },
|
|
36
|
+
(_, index) => index + 1,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
export function RolloutCohortsDialog({
|
|
40
|
+
releaseId,
|
|
41
|
+
rolloutCohortCount,
|
|
42
|
+
targetCohorts,
|
|
43
|
+
}: RolloutCohortsDialogProps) {
|
|
44
|
+
const normalizedRolloutCount =
|
|
45
|
+
normalizeRolloutCohortCount(rolloutCohortCount);
|
|
46
|
+
const normalizedTargetCohorts = targetCohorts ?? [];
|
|
47
|
+
const hasTargetCohorts = normalizedTargetCohorts.length > 0;
|
|
48
|
+
const isPartialRollout =
|
|
49
|
+
normalizedRolloutCount > 0 && normalizedRolloutCount < NUMERIC_COHORT_SIZE;
|
|
50
|
+
const isFullRollout = normalizedRolloutCount === NUMERIC_COHORT_SIZE;
|
|
51
|
+
const isMobile = useIsMobile();
|
|
52
|
+
|
|
53
|
+
const rolloutCohorts = isFullRollout
|
|
54
|
+
? ALL_NUMERIC_COHORTS
|
|
55
|
+
: isPartialRollout
|
|
56
|
+
? ALL_NUMERIC_COHORTS.filter(
|
|
57
|
+
(cohortValue) =>
|
|
58
|
+
getNumericCohortRolloutPosition(releaseId, cohortValue) <
|
|
59
|
+
normalizedRolloutCount,
|
|
60
|
+
)
|
|
61
|
+
: [];
|
|
62
|
+
const rolloutPercentage = (normalizedRolloutCount / 10).toFixed(1);
|
|
63
|
+
const selectedCount = isFullRollout
|
|
64
|
+
? NUMERIC_COHORT_SIZE
|
|
65
|
+
: rolloutCohorts.length;
|
|
66
|
+
const excludedCount = NUMERIC_COHORT_SIZE - selectedCount;
|
|
67
|
+
|
|
68
|
+
const dialogBody = (
|
|
69
|
+
<div className="flex flex-col gap-4">
|
|
70
|
+
<div className="grid grid-cols-2 gap-3">
|
|
71
|
+
<Card>
|
|
72
|
+
<CardHeader className="p-4">
|
|
73
|
+
<CardDescription>Selected cohorts</CardDescription>
|
|
74
|
+
<CardTitle className="font-mono text-2xl tabular-nums">
|
|
75
|
+
{selectedCount}
|
|
76
|
+
</CardTitle>
|
|
77
|
+
</CardHeader>
|
|
78
|
+
</Card>
|
|
79
|
+
<Card>
|
|
80
|
+
<CardHeader className="p-4">
|
|
81
|
+
<CardDescription>Excluded cohorts</CardDescription>
|
|
82
|
+
<CardTitle className="font-mono text-2xl tabular-nums">
|
|
83
|
+
{excludedCount}
|
|
84
|
+
</CardTitle>
|
|
85
|
+
</CardHeader>
|
|
86
|
+
</Card>
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
{hasTargetCohorts ? (
|
|
90
|
+
<Card>
|
|
91
|
+
<CardHeader className="p-4 pb-3">
|
|
92
|
+
<CardTitle className="text-sm">Additional cohorts</CardTitle>
|
|
93
|
+
<CardDescription>
|
|
94
|
+
Always included in addition to the percentage rollout.
|
|
95
|
+
</CardDescription>
|
|
96
|
+
</CardHeader>
|
|
97
|
+
<CardContent className="p-4 pt-0">
|
|
98
|
+
<div className="flex flex-wrap gap-2 rounded-lg border bg-muted/20 p-3">
|
|
99
|
+
{normalizedTargetCohorts.map((cohort) => (
|
|
100
|
+
<Badge
|
|
101
|
+
className="max-w-full font-mono"
|
|
102
|
+
key={cohort}
|
|
103
|
+
variant="secondary"
|
|
104
|
+
>
|
|
105
|
+
<span className="truncate">{cohort}</span>
|
|
106
|
+
</Badge>
|
|
107
|
+
))}
|
|
108
|
+
</div>
|
|
109
|
+
</CardContent>
|
|
110
|
+
</Card>
|
|
111
|
+
) : null}
|
|
112
|
+
|
|
113
|
+
<Card>
|
|
114
|
+
<CardHeader className="p-4 pb-3">
|
|
115
|
+
<CardTitle className="text-sm">Numeric cohorts</CardTitle>
|
|
116
|
+
</CardHeader>
|
|
117
|
+
<CardContent className="p-4 pt-0">
|
|
118
|
+
<div className="max-h-[50vh] overflow-y-auto overscroll-contain rounded-lg border bg-muted/20 p-3 sm:max-h-[45vh]">
|
|
119
|
+
{rolloutCohorts.length > 0 ? (
|
|
120
|
+
<div
|
|
121
|
+
aria-label="Included numeric cohorts"
|
|
122
|
+
className="grid grid-cols-3 gap-2 sm:grid-cols-6 lg:grid-cols-8"
|
|
123
|
+
role="list"
|
|
124
|
+
>
|
|
125
|
+
{rolloutCohorts.map((cohortValue) => (
|
|
126
|
+
<Badge
|
|
127
|
+
className="justify-center font-mono tabular-nums"
|
|
128
|
+
key={cohortValue}
|
|
129
|
+
role="listitem"
|
|
130
|
+
variant="outline"
|
|
131
|
+
>
|
|
132
|
+
{cohortValue}
|
|
133
|
+
</Badge>
|
|
134
|
+
))}
|
|
135
|
+
</div>
|
|
136
|
+
) : (
|
|
137
|
+
<p className="text-sm text-muted-foreground">
|
|
138
|
+
No numeric cohorts are included.
|
|
139
|
+
</p>
|
|
140
|
+
)}
|
|
141
|
+
</div>
|
|
142
|
+
</CardContent>
|
|
143
|
+
</Card>
|
|
144
|
+
</div>
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<Dialog>
|
|
149
|
+
<DialogTrigger
|
|
150
|
+
render={<Button size="sm" type="button" variant="outline" />}
|
|
151
|
+
>
|
|
152
|
+
<List data-icon="inline-start" />
|
|
153
|
+
Preview cohorts
|
|
154
|
+
</DialogTrigger>
|
|
155
|
+
<DialogContent
|
|
156
|
+
className={
|
|
157
|
+
isMobile
|
|
158
|
+
? "top-0 left-0 h-dvh max-w-none translate-x-0 translate-y-0 rounded-none border-0 p-0"
|
|
159
|
+
: "sm:max-w-3xl"
|
|
160
|
+
}
|
|
161
|
+
>
|
|
162
|
+
{isMobile ? (
|
|
163
|
+
<div className="flex h-full flex-col overflow-hidden">
|
|
164
|
+
<DialogHeader className="shrink-0 border-b border-border/70 px-4 py-4">
|
|
165
|
+
<DialogTitle>Rolled out cohorts</DialogTitle>
|
|
166
|
+
<DialogDescription>
|
|
167
|
+
{rolloutPercentage}% includes {selectedCount} of{" "}
|
|
168
|
+
{NUMERIC_COHORT_SIZE} numeric cohorts.
|
|
169
|
+
{hasTargetCohorts
|
|
170
|
+
? " Additional cohorts are always included."
|
|
171
|
+
: ""}
|
|
172
|
+
</DialogDescription>
|
|
173
|
+
</DialogHeader>
|
|
174
|
+
<div className="flex-1 overflow-y-auto overscroll-contain px-4 py-4">
|
|
175
|
+
{dialogBody}
|
|
176
|
+
</div>
|
|
177
|
+
<DialogFooter
|
|
178
|
+
className="shrink-0 border-t border-border/70 px-4 py-3"
|
|
179
|
+
showCloseButton
|
|
180
|
+
/>
|
|
181
|
+
</div>
|
|
182
|
+
) : (
|
|
183
|
+
<>
|
|
184
|
+
<DialogHeader>
|
|
185
|
+
<DialogTitle>Rolled out cohorts</DialogTitle>
|
|
186
|
+
<DialogDescription>
|
|
187
|
+
{rolloutPercentage}% includes {selectedCount} of{" "}
|
|
188
|
+
{NUMERIC_COHORT_SIZE} numeric cohorts.
|
|
189
|
+
{hasTargetCohorts
|
|
190
|
+
? " Additional cohorts are always included."
|
|
191
|
+
: ""}
|
|
192
|
+
</DialogDescription>
|
|
193
|
+
</DialogHeader>
|
|
194
|
+
{dialogBody}
|
|
195
|
+
<DialogFooter showCloseButton />
|
|
196
|
+
</>
|
|
197
|
+
)}
|
|
198
|
+
</DialogContent>
|
|
199
|
+
</Dialog>
|
|
200
|
+
);
|
|
201
|
+
}
|