authorgravatar for maria@kuuro.netmaria <maria@kuuro.net> 2026-10-04 08:57:42-03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-04 08:57:42-03:00
logc346fb679bac106c4c162ab145c27e3c8b9640bd
treed8d657c209c938d85ed6ff7e6c303a54dc0d56e2
parent1c6269326a502874677192a844e449577df6279c
signature Signed by PGP key B5690EEEBB952194

fix(web): open provider update details from both icons (#15501)


2 files changed, 105 insertions(+), 141 deletions(-)

apps/web/src/components/settings/ProviderInstanceCard.tsx+102-138
......@@ -593,7 +593,11 @@ export function ProviderInstanceCard({
593593 compatibility.status !== "supported" &&
594594 compatibility.status !== "unknown";
595595 const VersionAdvisoryIcon = hasCompatibilityWarning ? AlertTriangleIcon : ArrowUpCircleIcon;
596 const onRunVersionAction = versionAdvisory?.targetVersion ? onInstallRecommended : onRunUpdate;
596 const onRunVersionAction = readOnly
597 ? undefined
598 : versionAdvisory?.targetVersion
599 ? onInstallRecommended
600 : onRunUpdate;
597601 const urlAuthAction = liveProvider?.auth.action;
598602 const displayName =
599603 instance.displayName?.trim() || driverOption?.label || String(instance.driver);
......@@ -765,6 +769,101 @@ export function ProviderInstanceCard({
765769 ) : null}
766770 </>
767771 );
772 const versionAdvisoryNode = versionAdvisory ? (
773 <Popover>
774 <Tooltip>
775 <TooltipTrigger
776 render={
777 <PopoverTrigger
778 render={
779 <Button
780 type="button"
781 size={mode === "list" ? "icon-micro" : "icon-xs"}
782 variant="ghost-muted"
783 className={mode === "list" ? "pointer-events-auto relative shrink-0" : undefined}
784 aria-label={`${versionAdvisory.title} — view details`}
785 >
786 <VersionAdvisoryIcon
787 className={cn(
788 mode === "list" && "size-3.5",
789 hasCompatibilityWarning && "text-warning",
790 )}
791 />
792 </Button>
793 }
794 />
795 }
796 />
797 <TooltipPopup side="top">{versionAdvisory.title}</TooltipPopup>
798 </Tooltip>
799 <PopoverPopup side="bottom" align="end" width="md" aria-label={versionAdvisory.title}>
800 <div className="grid min-w-0 gap-3">
801 <div className="grid gap-0.5">
802 <p className="text-sm font-semibold leading-tight text-foreground">
803 {versionAdvisory.title}
804 </p>
805 <p
806 className={cn(
807 "text-xs leading-snug",
808 versionAdvisory.emphasis === "strong" ? "text-warning" : "text-muted-foreground",
809 )}
810 >
811 {versionAdvisory.detail}
812 </p>
813 </div>
814 {onRunVersionAction ? (
815 <Button
816 type="button"
817 size="xs"
818 variant="outline"
819 className="w-full"
820 disabled={isUpdating}
821 onClick={onRunVersionAction}
822 >
823 {isUpdating ? <Spinner /> : <DownloadIcon />}
824 {isUpdating
825 ? "Updating"
826 : versionAdvisory.targetVersion
827 ? `Install ${getProviderVersionLabel(versionAdvisory.targetVersion)}`
828 : "Update now"}
829 </Button>
830 ) : null}
831 {onRunVersionAction && updateCommand ? (
832 <div className="flex items-center gap-2 text-3xs font-medium uppercase tracking-wider text-muted-foreground">
833 <span aria-hidden className="h-px flex-1 bg-border" />
834 or, update manually using
835 <span aria-hidden className="h-px flex-1 bg-border" />
836 </div>
837 ) : null}
838 {updateCommand ? (
839 <div className="flex min-w-0 items-center gap-1 rounded-md border border-border/70 bg-muted/40 py-0.5 pr-0.5 pl-2">
840 <code className="min-w-0 flex-1 truncate font-mono text-2xs text-foreground">
841 {updateCommand}
842 </code>
843 <Tooltip>
844 <TooltipTrigger
845 render={
846 <Button
847 type="button"
848 size="icon-xs"
849 variant="ghost-muted"
850 className="shrink-0"
851 onClick={() => copyToClipboard(updateCommand, { providerName: displayName })}
852 aria-label="Copy update command"
853 >
854 <CopyIcon className="size-3" />
855 </Button>
856 }
857 />
858 <TooltipPopup side="top">Copy command</TooltipPopup>
859 </Tooltip>
860 </div>
861 ) : null}
862 </div>
863 </PopoverPopup>
864 </Popover>
865 ) : null;
866
768867 if (mode === "list") {
769868 return (
770869 <div
......@@ -796,49 +895,7 @@ export function ProviderInstanceCard({
796895 {versionLabel}
797896 </code>
798897 ) : null}
799 {versionAdvisory ? (
800 hasCompatibilityWarning ? (
801 <Tooltip>
802 <TooltipTrigger
803 render={
804 <span
805 tabIndex={0}
806 role="img"
807 aria-label={versionAdvisory.title}
808 className="pointer-events-auto relative inline-flex shrink-0 text-warning"
809 >
810 <VersionAdvisoryIcon className="size-3.5" />
811 </span>
812 }
813 />
814 <TooltipPopup side="top">{versionAdvisory.detail}</TooltipPopup>
815 </Tooltip>
816 ) : updateCommand ? (
817 <Tooltip>
818 <TooltipTrigger
819 render={
820 <Button
821 type="button"
822 size="icon-micro"
823 variant="ghost-muted"
824 className="pointer-events-auto relative shrink-0"
825 aria-label={`Copy ${displayName} update command`}
826 onClick={() =>
827 copyToClipboard(updateCommand, { providerName: displayName })
828 }
829 >
830 <ArrowUpCircleIcon className="size-3.5" />
831 </Button>
832 }
833 />
834 <TooltipPopup side="top">Copy update command</TooltipPopup>
835 </Tooltip>
836 ) : (
837 <span role="img" aria-label="Update available" className="inline-flex shrink-0">
838 <ArrowUpCircleIcon className="size-3.5 text-muted-foreground" />
839 </span>
840 )
841 ) : null}
898 {versionAdvisoryNode}
842899 </span>
843900 <span className="mt-0.5 flex items-start gap-1.5 text-xs leading-normal text-muted-foreground/80">
844901 {statusDotNode ? (
......@@ -876,105 +933,12 @@ export function ProviderInstanceCard({
876933 </Badge>
877934 ) : null}
878935 {versionCodeNode}
936 {versionAdvisoryNode}
879937 <span
880938 inert={readOnly}
881939 aria-disabled={readOnly || undefined}
882940 className={cn("inline-flex items-center gap-1", readOnly && "opacity-50")}
883941 >
884 {versionAdvisory ? (
885 <Popover>
886 <Tooltip>
887 <TooltipTrigger
888 render={
889 <PopoverTrigger
890 render={
891 <Button
892 type="button"
893 size="icon-xs"
894 variant="ghost-muted"
895 aria-label={`${versionAdvisory.title} — view details`}
896 >
897 <VersionAdvisoryIcon
898 className={cn(hasCompatibilityWarning && "text-warning")}
899 />
900 </Button>
901 }
902 />
903 }
904 />
905 <TooltipPopup side="top">{versionAdvisory.title}</TooltipPopup>
906 </Tooltip>
907 <PopoverPopup side="bottom" align="end" width="md">
908 <div className="grid min-w-0 gap-3">
909 <div className="grid gap-0.5">
910 <p className="text-sm font-semibold leading-tight text-foreground">
911 {versionAdvisory.title}
912 </p>
913 <p
914 className={cn(
915 "text-xs leading-snug",
916 versionAdvisory.emphasis === "strong"
917 ? "text-warning"
918 : "text-muted-foreground",
919 )}
920 >
921 {versionAdvisory.detail}
922 </p>
923 </div>
924 {onRunVersionAction ? (
925 <Button
926 type="button"
927 size="xs"
928 variant="outline"
929 className="w-full"
930 disabled={isUpdating}
931 onClick={onRunVersionAction}
932 >
933 {isUpdating ? <Spinner /> : <DownloadIcon />}
934 {isUpdating
935 ? "Updating"
936 : versionAdvisory.targetVersion
937 ? `Install ${getProviderVersionLabel(versionAdvisory.targetVersion)}`
938 : "Update now"}
939 </Button>
940 ) : null}
941 {onRunVersionAction && updateCommand ? (
942 <div className="flex items-center gap-2 text-3xs font-medium uppercase tracking-wider text-muted-foreground">
943 <span aria-hidden className="h-px flex-1 bg-border" />
944 or, update manually using
945 <span aria-hidden className="h-px flex-1 bg-border" />
946 </div>
947 ) : null}
948 {updateCommand ? (
949 <div className="flex min-w-0 items-center gap-1 rounded-md border border-border/70 bg-muted/40 py-0.5 pr-0.5 pl-2">
950 <code className="min-w-0 flex-1 truncate font-mono text-2xs text-foreground">
951 {updateCommand}
952 </code>
953 <Tooltip>
954 <TooltipTrigger
955 render={
956 <Button
957 type="button"
958 size="icon-xs"
959 variant="ghost-muted"
960 className="shrink-0"
961 onClick={() =>
962 copyToClipboard(updateCommand, { providerName: displayName })
963 }
964 aria-label="Copy update command"
965 >
966 <CopyIcon className="size-3" />
967 </Button>
968 }
969 />
970 <TooltipPopup side="top">Copy command</TooltipPopup>
971 </Tooltip>
972 </div>
973 ) : null}
974 </div>
975 </PopoverPopup>
976 </Popover>
977 ) : null}
978942 {titleTailNode}
979943 {onDelete ? (
980944 <Button
apps/web/src/components/settings/ProviderSettingsPanel.tsx+3-3
......@@ -1164,7 +1164,7 @@ export function EnvironmentProviderSettings({
11641164 })
11651165 }
11661166 onInstallRecommended={
1167 mode === "editor" &&
1167 !readOnly &&
11681168 liveProvider?.compatibilityAdvisory?.message &&
11691169 liveProvider.compatibilityAdvisory.recommendedVersion &&
11701170 liveProvider.versionAdvisory?.canInstallVersion
......@@ -1177,13 +1177,13 @@ export function EnvironmentProviderSettings({
11771177 : undefined
11781178 }
11791179 onRunUpdate={
1180 mode === "editor" && showInlineUpdateButton && updateCandidate
1180 !readOnly && showInlineUpdateButton && updateCandidate
11811181 ? () => {
11821182 if (canRunInlineUpdate) void runProviderUpdate(updateCandidate);
11831183 }
11841184 : undefined
11851185 }
1186 isUpdating={mode === "editor" ? isInstanceUpdateRunning : undefined}
1186 isUpdating={isInstanceUpdateRunning}
11871187 />
11881188 );
11891189 };