When a transparent-DCR (or CIMD) OAuth integration also exposed a custom-method '+' (createCustomMethod), the method tab strip rendered but its TabsContent card was gated out by a 'dcrActive ? null' wrapper, leaving the tabs floating with no body and a detached-looking border. That same wrapper also made the in-card 'No app to choose' DCR explainer unreachable dead code. Always render TabsContent and let the inner dcrActive / cimdActive branches show the explainer, so the tab strip gets its card back.
Unified · Split
| @@ -1722,186 +1722,184 @@ function AddAccountModalView(props: AddAccountModalProps) { · 1721 unmodified lines | |||
| 1722 | </div> | 1722 | </div> |
| 1723 | </TabsList> | 1723 | </TabsList> |
| 1724 | 1724 | ||
| 1725 | {dcrActive ? null : ( | 1725 | <TabsContent |
| 1726 | <TabsContent | 1726 | value={methodId} |
| 1727 | value={methodId} | 1727 | className={cn( |
| 1728 | className={cn( | 1728 | "mt-0 min-w-0 space-y-5", |
| 1729 | "mt-0 min-w-0 space-y-5", | 1729 | // No-auth renders no fields, so skip the framed box that |
| 1730 | // No-auth renders no fields, so skip the framed box that | 1730 | // would otherwise show up as an empty bordered panel. |
| 1731 | // would otherwise show up as an empty bordered panel. | 1731 | // Otherwise the panel attaches to the tab header above |
| 1732 | // Otherwise the panel attaches to the tab header above | 1732 | // (square top, rounded bottom). |
| 1733 | // (square top, rounded bottom). | 1733 | isNoAuth |
| 1734 | isNoAuth | 1734 | ? null |
| 1735 | ? null | 1735 | : "rounded-b-md rounded-t-none border border-border/60 bg-muted/15 p-4", |
| 1736 | : "rounded-b-md rounded-t-none border border-border/60 bg-muted/15 p-4", | 1736 | )} |
| 1737 | )} | 1737 | > |
| 1738 | > | 1738 | {method?.placements && !isEnvMethod && singleInput && !singleCredentialAffix |
| 1739 | {method?.placements && !isEnvMethod && singleInput && !singleCredentialAffix | 1739 | ? (() => { |
| 1740 | ? (() => { | 1740 | const shown = method.placements.filter((p) => p.carrier !== "env"); |
| 1741 | const shown = method.placements.filter((p) => p.carrier !== "env"); | 1741 | return shown.length > 0 ? ( |
| 1742 | return shown.length > 0 ? ( | 1742 | <div className="flex flex-wrap gap-x-3.5 gap-y-1"> |
| 1743 | <div className="flex flex-wrap gap-x-3.5 gap-y-1"> | 1743 | {shown.map((placement, i: number) => ( |
| 1744 | {shown.map((placement, i: number) => ( | 1744 | <PlacementLine key={i} placement={placement} /> |
| 1745 | <PlacementLine key={i} placement={placement} /> | 1745 | ))} |
| 1746 | ))} | 1746 | </div> |
| 1747 | </div> | 1747 | ) : null; |
| 1748 | ) : null; | 1748 | })() |
| 1749 | })() | 1749 | : null} |
| 1750 | : null} | 1750 | |
| 1751 | 1751 | {!isNoAuth && ( | |
| 1752 | {!isNoAuth && ( | 1752 | <div className="space-y-2"> |
| 1753 | <div className="space-y-2"> | 1753 | <StepHeader index={2} label={authStepLabel} /> |
| 1754 | <StepHeader index={2} label={authStepLabel} /> | 1754 | |
| 1755 | 1755 | {isOAuth && method ? ( | |
| 1756 | {isOAuth && method ? ( | 1756 | cimdActive ? ( |
| 1757 | cimdActive ? ( | 1757 | <div className="space-y-2 rounded-lg border border-ring/40 bg-accent/30 px-3 py-3"> |
| 1758 | <div className="space-y-2 rounded-lg border border-ring/40 bg-accent/30 px-3 py-3"> | 1758 | <p className="text-sm font-medium">No app registration</p> |
| 1759 | <p className="text-sm font-medium">No app registration</p> | 1759 | <p className="text-xs text-muted-foreground"> |
| 1760 | <p className="text-xs text-muted-foreground"> | 1760 | {cimdConnecting |
| 1761 | {cimdConnecting | 1761 | ? `Connecting to ${integrationName}…` |
| 1762 | ? `Connecting to ${integrationName}…` | 1762 | : `${integrationName} supports Client ID Metadata Document OAuth. We'll use this Executor host's public client metadata document and sign you in.`} |
| 1763 | : `${integrationName} supports Client ID Metadata Document OAuth. We'll use this Executor host's public client metadata document and sign you in.`} | 1763 | </p> |
| 1764 | </p> | 1764 | </div> |
| 1765 | </div> | 1765 | ) : dcrActive ? ( |
| 1766 | ) : dcrActive ? ( | 1766 | // Transparent DCR: no picker. We register an app for you and run |
| 1767 | // Transparent DCR: no picker. We register an app for you and run | 1767 | // the OAuth flow with a single Connect click. |
| 1768 | // the OAuth flow with a single Connect click. | 1768 | <div className="space-y-2 rounded-lg border border-ring/40 bg-accent/30 px-3 py-3"> |
| 1769 | <div className="space-y-2 rounded-lg border border-ring/40 bg-accent/30 px-3 py-3"> | 1769 | <p className="text-sm font-medium">No app to choose</p> |
| 1770 | <p className="text-sm font-medium">No app to choose</p> | 1770 | <p className="text-xs text-muted-foreground"> |
| 1771 | <p className="text-xs text-muted-foreground"> | 1771 | {dcrConnecting |
| 1772 | {dcrConnecting | 1772 | ? `Connecting to ${integrationName}…` |
| 1773 | ? `Connecting to ${integrationName}…` | 1773 | : `${integrationName} supports automatic setup. We register an app for you and sign you in — no client ID or app to pick.`} |
| 1774 | : `${integrationName} supports automatic setup. We register an app for you and sign you in — no client ID or app to pick.`} | 1774 | </p> |
| 1775 | </p> | 1775 | </div> |
| 1776 | </div> | 1776 | ) : oauthLoading ? ( |
| 1777 | ) : oauthLoading ? ( | 1777 | <p className="text-xs text-muted-foreground">Loading OAuth apps…</p> |
| 1778 | <p className="text-xs text-muted-foreground">Loading OAuth apps…</p> | 1778 | ) : ( |
| 1779 | ) : ( | 1779 | <div className="space-y-3"> |
| 1780 | <div className="space-y-3"> | 1780 | {dcrFallbackMessage ? ( |
| 1781 | {dcrFallbackMessage ? ( | 1781 | <div |
| 1782 | <div | 1782 | role="alert" |
| 1783 | role="alert" | 1783 | className="space-y-1 rounded-lg border border-destructive/40 bg-destructive/5 px-3 py-3" |
| 1784 | className="space-y-1 rounded-lg border border-destructive/40 bg-destructive/5 px-3 py-3" | 1784 | > |
| 1785 | > | 1785 | <p className="text-sm font-medium text-destructive"> |
| 1786 | <p className="text-sm font-medium text-destructive"> | 1786 | Couldn't set up {integrationName} automatically |
| 1787 | Couldn't set up {integrationName} automatically | 1787 | </p> |
| 1788 | </p> | 1788 | <p className="text-xs text-muted-foreground"> |
| 1789 | <p className="text-xs text-muted-foreground"> | 1789 | {dcrFallbackMessage} |
| 1790 | {dcrFallbackMessage} | 1790 | </p> |
| 1791 | </p> | 1791 | <p className="text-xs text-muted-foreground"> |
| 1792 | <p className="text-xs text-muted-foreground"> | 1792 | Register an app below to connect. |
| 1793 | Register an app below to connect. | 1793 | </p> |
| 1794 | </p> | 1794 | </div> |
| 1795 | </div> | 1795 | ) : null} |
| 1796 | ) : null} | 1796 | {/* No registered app matched the integration's endpoint: |
| 1797 | {/* No registered app matched the integration's endpoint: | ||
| 1798 | empty state + a prominent register CTA, and an opt-in | 1797 | empty state + a prominent register CTA, and an opt-in |
| 1799 | collapsed "use a different registered app" escape hatch. */} | 1798 | collapsed "use a different registered app" escape hatch. */} |
| 1800 | {oauthDisplayRegisterCTA && ( | 1799 | {oauthDisplayRegisterCTA && ( |
| 1801 | <div className="space-y-3 rounded-lg border border-border/60 bg-muted/20 px-3 py-3"> | 1800 | <div className="space-y-3 rounded-lg border border-border/60 bg-muted/20 px-3 py-3"> |
| 1802 | <p className="text-sm font-medium"> | 1801 | <p className="text-sm font-medium"> |
| 1803 | No app for {integrationName} yet | 1802 | No app for {integrationName} yet |
| 1804 | </p> | 1803 | </p> |
| 1805 | <p className="text-xs text-muted-foreground"> | 1804 | <p className="text-xs text-muted-foreground"> |
| 1806 | None of your registered apps target this integration's OAuth | 1805 | None of your registered apps target this integration's OAuth |
| 1807 | endpoint. Register one to connect. | 1806 | endpoint. Register one to connect. |
| 1808 | </p> | 1807 | </p> |
| 1809 | <div className="flex flex-wrap items-center gap-2 pt-1"> | 1808 | <div className="flex flex-wrap items-center gap-2 pt-1"> |
| 1809 | <Button | ||
| 1810 | type="button" | ||
| 1811 | size="sm" | ||
| 1812 | onClick={() => setRegisteringOAuthClient(true)} | ||
| 1813 | > | ||
| 1814 | {dcrFallbackMessage | ||
| 1815 | ? "Manually register an app" | ||
| 1816 | : "Register app"} | ||
| 1817 | </Button> | ||
| 1818 | {oauthOtherApps.length > 0 && !showOtherApps ? ( | ||
| 1810 | <Button | 1819 | <Button |
| 1811 | type="button" | 1820 | type="button" |
| 1821 | variant="outline" | ||
| 1812 | size="sm" | 1822 | size="sm" |
| 1813 | onClick={() => setRegisteringOAuthClient(true)} | 1823 | onClick={() => setShowOtherApps(true)} |
| 1814 | > | 1824 | > |
| 1815 | {dcrFallbackMessage | 1825 | Use another app |
| 1816 | ? "Manually register an app" | ||
| 1817 | : "Register app"} | ||
| 1818 | </Button> | 1826 | </Button> |
| 1819 | {oauthOtherApps.length > 0 && !showOtherApps ? ( | ||
| 1820 | <Button | ||
| 1821 | type="button" | ||
| 1822 | variant="outline" | ||
| 1823 | size="sm" | ||
| 1824 | onClick={() => setShowOtherApps(true)} | ||
| 1825 | > | ||
| 1826 | Use another app | ||
| 1827 | </Button> | ||
| 1828 | ) : null} | ||
| 1829 | </div> | ||
| 1830 | {oauthOtherApps.length > 0 && showOtherApps ? ( | ||
| 1831 | <RadioGroup | ||
| 1832 | value={selectedApp} | ||
| 1833 | onValueChange={setPickedApp} | ||
| 1834 | className="gap-2 pt-1" | ||
| 1835 | > | ||
| 1836 | {oauthOtherApps.map((app: OAuthClientOption) => ( | ||
| 1837 | <OAuthAppRadioRow | ||
| 1838 | key={String(app.slug)} | ||
| 1839 | app={app} | ||
| 1840 | idPrefix="other-app" | ||
| 1841 | variant="other" | ||
| 1842 | showOwnerLabel={ownerDisplay.showOwnerLabels} | ||
| 1843 | onManage={manageHandlersFor(app)} | ||
| 1844 | /> | ||
| 1845 | ))} | ||
| 1846 | </RadioGroup> | ||
| 1847 | ) : null} | 1827 | ) : null} |
| 1848 | </div> | 1828 | </div> |
| 1849 | )} | 1829 | {oauthOtherApps.length > 0 && showOtherApps ? ( |
| 1830 | <RadioGroup | ||
| 1831 | value={selectedApp} | ||
| 1832 | onValueChange={setPickedApp} | ||
| 1833 | className="gap-2 pt-1" | ||
| 1834 | > | ||
| 1835 | {oauthOtherApps.map((app: OAuthClientOption) => ( | ||
| 1836 | <OAuthAppRadioRow | ||
| 1837 | key={String(app.slug)} | ||
| 1838 | app={app} | ||
| 1839 | idPrefix="other-app" | ||
| 1840 | variant="other" | ||
| 1841 | showOwnerLabel={ownerDisplay.showOwnerLabels} | ||
| 1842 | onManage={manageHandlersFor(app)} | ||
| 1843 | /> | ||
| 1844 | ))} | ||
| 1845 | </RadioGroup> | ||
| 1846 | ) : null} | ||
| 1847 | </div> | ||
| 1848 | )} | ||
| 1850 | 1849 | ||
| 1851 | {oauthApps.length > 0 && ( | 1850 | {oauthApps.length > 0 && ( |
| 1852 | <RadioGroup | 1851 | <RadioGroup |
| 1853 | value={selectedApp} | 1852 | value={selectedApp} |
| 1854 | onValueChange={setPickedApp} | 1853 | onValueChange={setPickedApp} |
| 1855 | className="gap-2" | 1854 | className="gap-2" |
| 1855 | > | ||
| 1856 | {oauthApps.map((app: OAuthClientOption) => ( | ||
| 1857 | <OAuthAppRadioRow | ||
| 1858 | key={String(app.slug)} | ||
| 1859 | app={app} | ||
| 1860 | idPrefix="app" | ||
| 1861 | variant="matched" | ||
| 1862 | showOwnerLabel={ownerDisplay.showOwnerLabels} | ||
| 1863 | onManage={manageHandlersFor(app)} | ||
| 1864 | /> | ||
| 1865 | ))} | ||
| 1866 | <Button | ||
| 1867 | type="button" | ||
| 1868 | variant="outline" | ||
| 1869 | className="h-auto justify-start gap-3 rounded-lg border-dashed border-border/60 px-3 py-2.5 text-sm font-normal text-muted-foreground hover:text-foreground" | ||
| 1870 | onClick={() => setRegisteringOAuthClient(true)} | ||
| 1856 | > | 1871 | > |
| 1857 | {oauthApps.map((app: OAuthClientOption) => ( | 1872 | <PlusIcon className="size-4" /> |
| 1858 | <OAuthAppRadioRow | 1873 | Register a new app |
| 1859 | key={String(app.slug)} | 1874 | </Button> |
| 1860 | app={app} | 1875 | </RadioGroup> |
| 1861 | idPrefix="app" | 1876 | )} |
| 1862 | variant="matched" | 1877 | </div> |
| 1863 | showOwnerLabel={ownerDisplay.showOwnerLabels} | 1878 | ) |
| 1864 | onManage={manageHandlersFor(app)} | 1879 | ) : ( |
| 1865 | /> | 1880 | <CredentialValueFields |
| 1866 | ))} | 1881 | inputs={credentialInputs} |
| 1867 | <Button | 1882 | singleInput={singleInput} |
| 1868 | type="button" | 1883 | showLabels={isEnvMethod} |
| 1869 | variant="outline" | 1884 | affix={singleCredentialAffix} |
| 1870 | className="h-auto justify-start gap-3 rounded-lg border-dashed border-border/60 px-3 py-2.5 text-sm font-normal text-muted-foreground hover:text-foreground" | 1885 | allowExternalProvider={!isEnvMethod} |
| 1871 | onClick={() => setRegisteringOAuthClient(true)} | 1886 | values={values} |
| 1872 | > | 1887 | onValuesChange={setValues} |
| 1873 | <PlusIcon className="size-4" /> | 1888 | origin={credentialOrigin} |
| 1874 | Register a new app | 1889 | onOriginChange={(next) => { |
| 1875 | </Button> | 1890 | setCredentialOrigin(next); |
| 1876 | </RadioGroup> | 1891 | if (next === "paste") setOnePasswordItemId(""); |
| 1877 | )} | 1892 | }} |
| 1878 | </div> | 1893 | onePasswordItemId={onePasswordItemId} |
| 1879 | ) | 1894 | onOnePasswordItemIdChange={setOnePasswordItemId} |
| 1880 | ) : ( | 1895 | /> |
| 1881 | <CredentialValueFields | 1896 | )} |
| 1882 | inputs={credentialInputs} | 1897 | {isOAuth && oauthPopup.error ? ( |
| 1883 | singleInput={singleInput} | 1898 | <p className="text-xs text-destructive">{oauthPopup.error}</p> |
| 1884 | showLabels={isEnvMethod} | 1899 | ) : null} |
| 1885 | affix={singleCredentialAffix} | 1900 | </div> |
| 1886 | allowExternalProvider={!isEnvMethod} | 1901 | )} |
| 1887 | values={values} | 1902 | </TabsContent> |
| 1888 | onValuesChange={setValues} | ||
| 1889 | origin={credentialOrigin} | ||
| 1890 | onOriginChange={(next) => { | ||
| 1891 | setCredentialOrigin(next); | ||
| 1892 | if (next === "paste") setOnePasswordItemId(""); | ||
| 1893 | }} | ||
| 1894 | onePasswordItemId={onePasswordItemId} | ||
| 1895 | onOnePasswordItemIdChange={setOnePasswordItemId} | ||
| 1896 | /> | ||
| 1897 | )} | ||
| 1898 | {isOAuth && oauthPopup.error ? ( | ||
| 1899 | <p className="text-xs text-destructive">{oauthPopup.error}</p> | ||
| 1900 | ) : null} | ||
| 1901 | </div> | ||
| 1902 | )} | ||
| 1903 | </TabsContent> | ||
| 1904 | )} | ||
| 1905 | </Tabs> | 1903 | </Tabs> |
| 1906 | )} | 1904 | )} |
| 1907 | 1905 | ||