From 5e2fb7ae0700f76e4c0ab769be84e37cc2aba3a3 Mon Sep 17 00:00:00 2001 From: Syed Bariman Jan Date: Sun, 28 Jan 2024 10:39:27 +0500 Subject: [PATCH] Feature/1269 add shortcut to tailwarden in inventory table (#1306) * feat: add service check helpers * feat(useInventory): track unsupported services state * feat(ui): enhance ui to reflect unsupported services * docs: update contributing guidelines * docs: update contributing guidelines * Update CONTRIBUTING.md * style: suggested changes Co-authored-by: Azanul Haque <42029519+Azanul@users.noreply.github.com> * fix: type error * style: add border radius --------- Co-authored-by: Azanul Haque <42029519+Azanul@users.noreply.github.com> --- CONTRIBUTING.md | 65 ++++- .../components/InventoryStatsCards.tsx | 26 +- .../inventory/components/InventoryTable.tsx | 60 ++++- .../hooks/useInventory/useInventory.tsx | 30 ++- dashboard/components/tooltip/Tooltip.tsx | 5 +- dashboard/pages/inventory.tsx | 4 +- dashboard/utils/serviceHelper.ts | 229 ++++++++++++++++++ 7 files changed, 408 insertions(+), 11 deletions(-) create mode 100644 dashboard/utils/serviceHelper.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 76c73f943..456aee415 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -185,12 +185,52 @@ source="ENVIRONMENT_VARIABLES" profile="production" ``` -#### 5️⃣ Compile a new Komiser binary: +#### 5️⃣ Update Dashboard Utils +Navigate to the `dashboard/utils` folder in your project and locate the file named `servicehelper.ts`. Open the file and follow these steps: + +##### a. Add Provider to Union Type +Locate the `Providers` union type at the top of the file. Add the new cloud provider in lowercase to the union. + +```typescript +// dashboard/utils/servicehelper.ts + +export type Providers = + | 'aws' + | 'gcp' + | 'digitalocean' + | 'azure' + | 'civo' + | 'kubernetes' + | 'linode' + | 'tencent' + | 'oci' + | 'scaleway' + | 'mongodbatlas' + | 'ovh' + | 'scaleway' + | 'tencent' + | 'provider_name'; // Add the new provider here + +``` +##### b. Add Provider and Service to `allProvidersServices` + +Find the `allProvidersServices` object in the `servicehelper.ts` file and add the new provider along with the service in lowercase. + +```typescript +// dashboard/utils/servicehelper.ts + +export const allProvidersServices: { [key in Providers | string]: string[] } = { + // ... other services + new_provider: ['new_service_name'], +}; +``` + +#### 6️⃣ Compile a new Komiser binary: ```bash go build ``` -#### 6️⃣ Start a new Komiser development server: +#### 7️⃣ Start a new Komiser development server: ```bash ./komiser start @@ -254,7 +294,26 @@ func listOfSupportedServices() []providers.FetchDataFunction { . ``` -#### 4️⃣ +#### 4️⃣ Update Dashboard Utils +Navigate to the `dashboard/utils` folder in your project and locate the file named `servicehelper.ts`. Open the file and find the `allProvidersServices` object. Add the new service name in lowercase under the respective provider, adhering to the existing structure. + +Here's an example of how to add the new service: + +```typescript +// dashboard/utils/servicehelper.ts + +export const allProvidersServices: { [key in Providers | string]: string[] } = { + // ... other services + your_provider_name: [ + 'existing_service_1', + 'existing_service_2', + // ... other existing services + 'new_service_name', // Add the new service here + ], +}; +``` + +#### 5️⃣ Do above mentioned steps [4](#4️⃣-add-provider-configuration), [5](#5️⃣-compile-a-new-komiser-binary) and [6](#6️⃣-start-a-new-komiser-development-server). You'll see a new resource/service added to Komiser, in the dashboard! Additionally, [here](https://youtu.be/Vn5uc2elcVg?feature=shared) is a video tutorial of the entire process for your reference. diff --git a/dashboard/components/inventory/components/InventoryStatsCards.tsx b/dashboard/components/inventory/components/InventoryStatsCards.tsx index cdd4d70d2..38741f10d 100644 --- a/dashboard/components/inventory/components/InventoryStatsCards.tsx +++ b/dashboard/components/inventory/components/InventoryStatsCards.tsx @@ -1,4 +1,5 @@ import { useRouter } from 'next/router'; +import { ErrorIcon } from '@components/icons'; import formatNumber from '../../../utils/formatNumber'; import Tooltip from '../../tooltip/Tooltip'; import { @@ -8,6 +9,7 @@ import { type InventoryStatsCardsProps = { inventoryStats: InventoryStats | undefined; + isSomeServiceUnavailable: boolean | undefined; error: boolean; statsLoading: boolean; hiddenResources: HiddenResource[] | undefined; @@ -15,6 +17,7 @@ type InventoryStatsCardsProps = { function InventoryStatsCards({ inventoryStats, + isSomeServiceUnavailable, error, statsLoading, hiddenResources @@ -31,7 +34,7 @@ function InventoryStatsCards({
@@ -138,8 +141,27 @@ function InventoryStatsCards({

${formatNumber(inventoryStats.costs)}

-

Cost

+

Discoverd Cost

+ {isSomeServiceUnavailable && ( +
+ window.open('https://www.tailwarden.com/', '_blank') + } + className="rounded-s absolute -top-[22px] -right-[22px] bg-white w-[44px] h-[44px] flex justify-center items-center border-2 border-gray-50" + > + + + We couldn't determine the exact cost of your resources + as some cloud providers service's costs are not yet + supported — we suggest trying Tailwarden. + +
+ )} Up-to-date monthly cost
{router.query.view && hiddenResources && ( diff --git a/dashboard/components/inventory/components/InventoryTable.tsx b/dashboard/components/inventory/components/InventoryTable.tsx index 9d53bef1d..df9b174c6 100644 --- a/dashboard/components/inventory/components/InventoryTable.tsx +++ b/dashboard/components/inventory/components/InventoryTable.tsx @@ -2,6 +2,8 @@ import { ToastProps } from '@components/toast/Toast'; import { NextRouter } from 'next/router'; import { ChangeEvent } from 'react'; import Avatar from '@components/avatar/Avatar'; +import ErrorIcon from '@components/icons/ErrorIcon'; +import { checkIfServiceIsSupported } from '@utils/serviceHelper'; import formatNumber from '../../../utils/formatNumber'; import Checkbox from '../../checkbox/Checkbox'; import SkeletonInventory from '../../skeleton/SkeletonInventory'; @@ -156,7 +158,34 @@ function InventoryTable({ onClick={() => openModal(item)} className="cursor-pointer whitespace-nowrap px-6 py-4 text-right" > - ${formatNumber(item.cost)} + {checkIfServiceIsSupported( + item.provider, + item.service + ) ? ( + `$${formatNumber(item.cost)}` + ) : ( +
{ + e.stopPropagation(); + window.open( + 'https://www.tailwarden.com/', + '_blank' + ); + }} + className="group relative" + > + +
+ Service-level cost analysis is not available in + Komiser.

For advanced insights, try + Tailwarden. +
+
+ )} openModal(item)} className="cursor-pointer whitespace-nowrap px-6 py-4 text-right" > - ${formatNumber(item.cost)} + {checkIfServiceIsSupported( + item.provider, + item.service + ) ? ( + `$${formatNumber(item.cost)}` + ) : ( +
{ + e.stopPropagation(); + window.open( + 'https://www.tailwarden.com/', + '_blank' + ); + }} + className="group relative" + > + +
+ Service-level cost analysis is not available in + Komiser.

For advanced insights, try + Tailwarden. +
+
+ )} (false); const [inventoryStats, setInventoryStats] = useState(); const [inventory, setInventory] = useState(); const [error, setError] = useState(false); @@ -56,6 +62,27 @@ function useInventory() { const batchSize: number = 50; const router = useRouter(); + /* + Check if there are items in searchedInventory: + - If yes, check if even one item is not supported. + - If unsupported item found, set isSomeServiceUnavailable to true. + - If searchedInventory is empty, get all services and check if even one is not supported. + - If unsupported service found, set isSomeServiceUnavailable to true. + */ + useEffect(() => { + if (searchedInventory) { + setIsSomeServiceUnavailable( + searchedInventory.some( + item => !checkIfServiceIsSupported(item.provider, item.service) + ) + ); + } else { + settingsService.getServices().then(res => { + setIsSomeServiceUnavailable(checkIsSomeServiceUnavailable(res)); + }); + } + }, [searchedInventory]); + /** Reset most of the UI states: * - skipped (used to skip results in the data fetch call) * - skippedSearch (same, but used to skip results in the searched data fetch call) @@ -794,7 +821,8 @@ function useInventory() { displayFilterIfIsNotCustomView, loadingFilters, hasFilters, - loadingInventory + loadingInventory, + isSomeServiceUnavailable }; } diff --git a/dashboard/components/tooltip/Tooltip.tsx b/dashboard/components/tooltip/Tooltip.tsx index eb9c1035e..3fef3acec 100644 --- a/dashboard/components/tooltip/Tooltip.tsx +++ b/dashboard/components/tooltip/Tooltip.tsx @@ -6,7 +6,7 @@ type TooltipProps = { top?: 'xs' | 'sm' | 'md' | 'lg'; bottom?: 'xs' | 'sm' | 'md' | 'lg'; align?: 'left' | 'center' | 'right'; - width?: 'sm' | 'md' | 'lg'; + width?: 'sm' | 'md' | 'lg' | 'xl'; }; function Tooltip({ @@ -31,7 +31,8 @@ function Tooltip({ { 'bottom-36': bottom === 'lg' }, { 'left-6': align === 'left' }, { 'right-6': align === 'right' }, - { 'w-72': width === 'lg' } + { 'w-72': width === 'lg' }, + { 'w-[30.5rem]': width === 'xl' } )} > {children} diff --git a/dashboard/pages/inventory.tsx b/dashboard/pages/inventory.tsx index 4ef3017a4..bd5de6e8b 100644 --- a/dashboard/pages/inventory.tsx +++ b/dashboard/pages/inventory.tsx @@ -67,7 +67,8 @@ export default function Inventory() { displayFilterIfIsNotCustomView, loadingFilters, hasFilters, - loadingInventory + loadingInventory, + isSomeServiceUnavailable } = useInventory(); return ( @@ -123,6 +124,7 @@ export default function Inventory() { {/* Inventory stats */} !allServices.includes(service.toLowerCase()) + ); +}