diff --git a/ui/package-lock.json b/ui/package-lock.json index ee2565f..05111c2 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -17,6 +17,7 @@ "@mui/icons-material": "6.1.9", "@mui/material": "6.1.9", "react": "^18.2.0", + "react-device-detect": "^2.2.3", "react-dom": "^18.2.0" }, "devDependencies": { @@ -4605,6 +4606,19 @@ "node": ">=0.10.0" } }, + "node_modules/react-device-detect": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-device-detect/-/react-device-detect-2.2.3.tgz", + "integrity": "sha512-buYY3qrCnQVlIFHrC5UcUoAj7iANs/+srdkwsnNjI7anr3Tt7UY6MqNxtMLlr0tMBied0O49UZVK8XKs3ZIiPw==", + "license": "MIT", + "dependencies": { + "ua-parser-js": "^1.0.33" + }, + "peerDependencies": { + "react": ">= 0.14.0", + "react-dom": ">= 0.14.0" + } + }, "node_modules/react-dom": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", @@ -5070,6 +5084,32 @@ "node": ">=4.2.0" } }, + "node_modules/ua-parser-js": { + "version": "1.0.40", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.40.tgz", + "integrity": "sha512-z6PJ8Lml+v3ichVojCiB8toQJBuwR42ySM4ezjXIqXK3M0HczmKQ3LF4rhU55PfD99KEEXQG6yb7iOMyvYuHew==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "license": "MIT", + "bin": { + "ua-parser-js": "script/cli.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", diff --git a/ui/package.json b/ui/package.json index b59b412..11861c9 100644 --- a/ui/package.json +++ b/ui/package.json @@ -13,6 +13,7 @@ "@mui/icons-material": "6.1.9", "@mui/material": "6.1.9", "react": "^18.2.0", + "react-device-detect": "^2.2.3", "react-dom": "^18.2.0" }, "scripts": { diff --git a/ui/src/components/ConnectionOptions.tsx b/ui/src/components/ConnectionOptions.tsx new file mode 100644 index 0000000..afffb15 --- /dev/null +++ b/ui/src/components/ConnectionOptions.tsx @@ -0,0 +1,64 @@ +import { osName } from 'react-device-detect'; +import { IconButton, Tooltip } from '@mui/material'; +import { DvrRounded } from '@mui/icons-material'; +import { copyToClipboard } from './ContainerList'; + +// copies the password to the clipboard, then opens the connectionURI +const openURI = (connectionURI: string, saPassword: string) => { + copyToClipboard(saPassword); + // wait 1 second + setTimeout(() => { + window.location.href = connectionURI; + }, 1000); +} + +export const ConnectionOptions = ({ container, trackEvent }) => { + +if (osName !== "Windows") { + return ( + <> + + { + trackEvent('OpenADS', { containerId: container.Id }); + openURI(container.adsConnectionURI(), container.SApassword); + }}> + + + + + { + trackEvent('OpenVSC', { containerId: container.Id }); + openURI(container.vscConnectionURI(), container.SApassword); + }}> + + + + + ); +} else { + return ( + <> + + { + trackEvent('OpenADS', { containerId: container.Id }); + openURI(container.adsConnectionURI(), container.SApassword); + }}> + + + + + { + trackEvent('OpenVSC', { containerId: container.Id }); + openURI(container.vscConnectionURI(), container.SApassword); + }}> + + + + + ); +} +} \ No newline at end of file diff --git a/ui/src/components/ContainerList.tsx b/ui/src/components/ContainerList.tsx index 183a3c3..f7747a7 100644 --- a/ui/src/components/ContainerList.tsx +++ b/ui/src/components/ContainerList.tsx @@ -1,12 +1,15 @@ import * as React from 'react'; import { AppBar, Box, Button, Chip, CircularProgress, Collapse, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, FormControl, IconButton, InputLabel, List, ListItem, ListItemIcon, ListItemText, OutlinedInput, Stack, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Toolbar, Tooltip, Typography } from '@mui/material'; -import { AddCircleRounded, ArticleRounded, ContentCopyRounded, StopRounded, PlayArrowRounded, KeyboardArrowUpRounded, KeyboardArrowDownRounded, TerminalRounded, DvrRounded, CloseRounded, DeleteRounded } from "@mui/icons-material"; +import { AddCircleRounded, ArticleRounded, ContentCopyRounded, StopRounded, PlayArrowRounded, KeyboardArrowUpRounded, KeyboardArrowDownRounded, TerminalRounded, CloseRounded, DeleteRounded } from "@mui/icons-material"; import { SqlContainer } from '../models/SqlContainer'; +import { ConnectionOptions } from './ConnectionOptions'; import { useDockerDesktopClient } from '../App'; -const copyToClipboard = (text: string) => { +export const copyToClipboard = (text: string) => { navigator.clipboard.writeText(text); + const ddClient = useDockerDesktopClient(); + ddClient.desktopUI.toast.success("Password copied to clipboard"); } const navigateToContainer = (containerId: string) => { @@ -14,12 +17,6 @@ const navigateToContainer = (containerId: string) => { ddClient.desktopUI.navigate.viewContainerLogs(containerId); } -// copies the password to the clipboard, then opens the connectionURI -const openADS = (connectionURI: string, saPassword: string) => { - copyToClipboard(saPassword); - window.location.href = connectionURI; -} - var ContainerStatus = ({ status }) => { if (status === "running") { return ( @@ -205,15 +202,7 @@ var ContainerRow = ({ container, startContainer, stopContainer, deleteContainer, - - { - trackEvent('OpenADS', { containerId: container.Id }); - openADS(container.adsConnectionURI(), container.SApassword); - }}> - - - + { trackEvent('ViewLogs', { containerId: container.Id }); diff --git a/ui/src/models/SqlContainer.tsx b/ui/src/models/SqlContainer.tsx index bc2d794..a0c5b7a 100644 --- a/ui/src/models/SqlContainer.tsx +++ b/ui/src/models/SqlContainer.tsx @@ -41,7 +41,11 @@ export class SqlContainer { } public adsConnectionURI(): string { - return `azuredatastudio://openConnectionDialog?connectionName=${this.Name}&server=localhost,${this.Port1433}&authenticationType=SqlLogin&user=sa&password=${this.SApassword}&database=master&connectionProperties={"trustServerCertificate":"true"}`; + return `azuredatastudio://openConnectionDialog?connectionName=${this.Name}&server=localhost,${this.Port1433}&authenticationType=SqlLogin&user=sa&database=master&connectionProperties={"trustServerCertificate":"true"}`; + } + + public vscConnectionURI(): string { + return `vscode://ms-mssql.mssql/connect?profileName=${this.Name}&server=localhost,${this.Port1433}&database=master&authenticationType=SqlLogin&user=sa&trustServerCertificate=true&persistSecurityInfo=true&savePassword=true`; } }