mirror of
https://github.com/fleetdm/fleet
synced 2026-05-23 17:08:53 +00:00
UI hackathon: transitions, tooltips, empty states, buttons, bookmarks (#9195)
This commit is contained in:
commit
abb5a332c5
124 changed files with 2027 additions and 1485 deletions
Binary file not shown.
|
Before Width: | Height: | Size: 1.8 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB |
|
|
@ -1,43 +0,0 @@
|
|||
<svg width="323" height="143" viewBox="0 0 323 143" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="162" y="39" width="1" height="17" fill="#C5C7D1"/>
|
||||
<circle cx="163" cy="24" r="24" fill="#8B8FA2"/>
|
||||
<g clip-path="url(#clip0_2902:71050)">
|
||||
<path d="M173.066 20.2939V20.2941C173.066 21.7474 172.849 23.5519 172.427 25.3048C172.003 27.0663 171.388 28.7066 170.63 29.8693C169.914 30.9677 168.545 32.0283 166.906 32.9952C165.432 33.8648 163.835 34.6053 162.5 35.1982C161.165 34.6052 159.568 33.8648 158.094 32.9951C156.455 32.0282 155.086 30.9676 154.37 29.8692C153.612 28.7065 152.997 27.0662 152.573 25.3048C152.151 23.5519 151.934 21.7474 151.934 20.2941V20.2939V16.3705L162.5 12.7682L173.066 16.3705V20.2939Z" stroke="white" stroke-width="1.86875"/>
|
||||
<path d="M166.608 19.3127L161.552 24.9189L158.234 22.7627C157.76 22.4752 157.286 22.4752 156.97 22.9064C156.654 23.3377 156.654 23.7689 157.128 24.0564L161.868 27.2189L168.03 20.3189C168.346 19.8877 168.346 19.4564 167.872 19.1689C167.556 18.8814 166.924 18.8814 166.608 19.3127Z" fill="white"/>
|
||||
</g>
|
||||
<g filter="url(#filter0_d_2902:71050)">
|
||||
<rect x="1.5" y="57" width="320" height="80" rx="8" fill="white"/>
|
||||
<rect x="1" y="56.5" width="321" height="81" rx="8.5" stroke="#C5C7D1"/>
|
||||
</g>
|
||||
<rect x="14" y="96.5" width="11" height="11" rx="3.5" fill="white" stroke="#C5C7D1"/>
|
||||
<rect x="14" y="116.5" width="11" height="11" rx="3.5" fill="white" stroke="#C5C7D1"/>
|
||||
<rect x="37.5" y="99" width="132" height="6" rx="3" fill="#C5C7D1"/>
|
||||
<rect x="37.5" y="119" width="44" height="6" rx="3" fill="#E2E4EA"/>
|
||||
<rect x="85.5" y="119" width="72" height="6" rx="3" fill="#E2E4EA"/>
|
||||
<rect x="251.5" y="99" width="58" height="6" rx="3" fill="#C5C7D1"/>
|
||||
<rect x="251.5" y="119" width="34" height="6" rx="3" fill="#E2E4EA"/>
|
||||
<rect x="181.5" y="99" width="58" height="6" rx="3" fill="#C5C7D1"/>
|
||||
<rect x="181.5" y="119" width="46" height="6" rx="3" fill="#E2E4EA"/>
|
||||
<path d="M321.5 87.5H322V87V65C322 60.3056 318.194 56.5 313.5 56.5H9.5C4.80558 56.5 1 60.3056 1 65V87V87.5H1.5H321.5Z" fill="#F9FAFC" stroke="#C5C7D1"/>
|
||||
<rect x="14" y="66.5" width="11" height="11" rx="3.5" fill="white" stroke="#C5C7D1"/>
|
||||
<path d="M44.1 67.54H45.552V76H44.388L39.852 70.096V76H38.412V67.54H39.564L44.1 73.432V67.54ZM49.7648 69.988C50.5888 69.988 51.2008 70.188 51.6008 70.588C52.0008 70.988 52.2008 71.604 52.2008 72.436V76H50.7728V75.1C50.6368 75.412 50.4208 75.656 50.1248 75.832C49.8368 76.008 49.5008 76.096 49.1168 76.096C48.7248 76.096 48.3688 76.016 48.0488 75.856C47.7288 75.696 47.4768 75.476 47.2928 75.196C47.1088 74.916 47.0168 74.604 47.0168 74.26C47.0168 73.828 47.1248 73.488 47.3408 73.24C47.5648 72.992 47.9248 72.812 48.4208 72.7C48.9168 72.588 49.6008 72.532 50.4728 72.532H50.7728V72.256C50.7728 71.856 50.6848 71.568 50.5088 71.392C50.3408 71.216 50.0568 71.128 49.6568 71.128C49.3448 71.128 49.0168 71.184 48.6728 71.296C48.3368 71.4 48.0048 71.548 47.6768 71.74L47.2568 70.708C47.5848 70.5 47.9808 70.328 48.4448 70.192C48.9088 70.056 49.3488 69.988 49.7648 69.988ZM49.4168 75.016C49.8168 75.016 50.1408 74.884 50.3888 74.62C50.6448 74.348 50.7728 74 50.7728 73.576V73.324H50.5568C50.0208 73.324 49.6048 73.348 49.3088 73.396C49.0128 73.444 48.8008 73.528 48.6728 73.648C48.5448 73.768 48.4808 73.932 48.4808 74.14C48.4808 74.396 48.5688 74.608 48.7448 74.776C48.9208 74.936 49.1448 75.016 49.4168 75.016ZM60.6973 69.988C61.3613 69.988 61.8573 70.188 62.1853 70.588C62.5133 70.98 62.6773 71.584 62.6773 72.4V76H61.1773V72.46C61.1773 72.012 61.1013 71.688 60.9493 71.488C60.7973 71.28 60.5533 71.176 60.2173 71.176C59.8173 71.176 59.5053 71.312 59.2813 71.584C59.0573 71.856 58.9453 72.236 58.9453 72.724V76H57.4453V72.46C57.4453 72.012 57.3693 71.688 57.2173 71.488C57.0733 71.28 56.8293 71.176 56.4853 71.176C56.0933 71.176 55.7853 71.312 55.5613 71.584C55.3373 71.856 55.2253 72.236 55.2253 72.724V76H53.7253V70.132H55.1893V70.96C55.3653 70.64 55.6093 70.4 55.9213 70.24C56.2333 70.072 56.5893 69.988 56.9893 69.988C57.8773 69.988 58.4653 70.356 58.7533 71.092C58.9373 70.748 59.2013 70.48 59.5453 70.288C59.8893 70.088 60.2733 69.988 60.6973 69.988ZM69.3499 73.336H65.3539C65.4019 73.88 65.5659 74.284 65.8459 74.548C66.1259 74.812 66.5259 74.944 67.0459 74.944C67.6939 74.944 68.2859 74.736 68.8219 74.32L69.2539 75.352C68.9819 75.576 68.6379 75.756 68.2219 75.892C67.8139 76.028 67.4019 76.096 66.9859 76.096C66.0259 76.096 65.2659 75.824 64.7059 75.28C64.1539 74.736 63.8779 73.992 63.8779 73.048C63.8779 72.448 63.9979 71.916 64.2379 71.452C64.4779 70.988 64.8139 70.628 65.2459 70.372C65.6859 70.116 66.1819 69.988 66.7339 69.988C67.5419 69.988 68.1779 70.248 68.6419 70.768C69.1139 71.288 69.3499 72.004 69.3499 72.916V73.336ZM66.7699 71.044C66.3699 71.044 66.0459 71.172 65.7979 71.428C65.5579 71.684 65.4099 72.052 65.3539 72.532H68.0659C68.0499 72.044 67.9299 71.676 67.7059 71.428C67.4819 71.172 67.1699 71.044 66.7699 71.044Z" fill="#8B8FA2"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M181.5 72C181.5 76.42 185.08 80 189.5 80C193.92 80 197.5 76.42 197.5 72C197.5 67.58 193.92 64 189.5 64C185.08 64 181.5 67.58 181.5 72ZM192.79 69.29C192.97 69.11 193.22 69 193.5 69C194.05 69 194.5 69.45 194.5 70C194.5 70.28 194.39 70.53 194.21 70.71L189.21 75.71C189.03 75.89 188.78 76 188.5 76C188.22 76 187.97 75.89 187.79 75.71L184.79 72.71C184.61 72.53 184.5 72.28 184.5 72C184.5 71.45 184.95 71 185.5 71C185.78 71 186.03 71.11 186.21 71.29L188.5 73.59L192.79 69.29Z" fill="#C5C7D1"/>
|
||||
<path d="M209.444 67.54L206.012 72.1V76H204.464V72.1L201.044 67.54H202.784L205.232 70.864L207.692 67.54H209.444ZM213.967 73.336H209.971C210.019 73.88 210.183 74.284 210.463 74.548C210.743 74.812 211.143 74.944 211.663 74.944C212.311 74.944 212.903 74.736 213.439 74.32L213.871 75.352C213.599 75.576 213.255 75.756 212.839 75.892C212.431 76.028 212.019 76.096 211.603 76.096C210.643 76.096 209.883 75.824 209.323 75.28C208.771 74.736 208.495 73.992 208.495 73.048C208.495 72.448 208.615 71.916 208.855 71.452C209.095 70.988 209.431 70.628 209.863 70.372C210.303 70.116 210.799 69.988 211.351 69.988C212.159 69.988 212.795 70.248 213.259 70.768C213.731 71.288 213.967 72.004 213.967 72.916V73.336ZM211.387 71.044C210.987 71.044 210.663 71.172 210.415 71.428C210.175 71.684 210.027 72.052 209.971 72.532H212.683C212.667 72.044 212.547 71.676 212.323 71.428C212.099 71.172 211.787 71.044 211.387 71.044ZM217.354 76.096C216.834 76.096 216.354 76.032 215.914 75.904C215.482 75.776 215.114 75.592 214.81 75.352L215.23 74.308C215.854 74.764 216.57 74.992 217.378 74.992C217.722 74.992 217.982 74.936 218.158 74.824C218.342 74.712 218.434 74.552 218.434 74.344C218.434 74.168 218.37 74.032 218.242 73.936C218.122 73.84 217.902 73.752 217.582 73.672L216.622 73.456C216.078 73.336 215.674 73.14 215.41 72.868C215.146 72.596 215.014 72.244 215.014 71.812C215.014 71.452 215.114 71.136 215.314 70.864C215.522 70.584 215.806 70.368 216.166 70.216C216.534 70.064 216.958 69.988 217.438 69.988C217.854 69.988 218.254 70.052 218.638 70.18C219.03 70.308 219.374 70.488 219.67 70.72L219.25 71.74C218.642 71.3 218.034 71.08 217.426 71.08C217.098 71.08 216.842 71.14 216.658 71.26C216.474 71.38 216.382 71.548 216.382 71.764C216.382 71.924 216.438 72.056 216.55 72.16C216.67 72.264 216.858 72.344 217.114 72.4L218.11 72.628C218.694 72.756 219.122 72.96 219.394 73.24C219.666 73.512 219.802 73.872 219.802 74.32C219.802 74.864 219.582 75.296 219.142 75.616C218.702 75.936 218.106 76.096 217.354 76.096Z" fill="#8B8FA2"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M251.5 72C251.5 76.4183 255.082 80 259.5 80C263.918 80 267.5 76.4183 267.5 72C267.5 67.5817 263.918 64 259.5 64C255.082 64 251.5 67.5817 251.5 72ZM265.5 72C265.5 75.3137 262.814 78 259.5 78C256.186 78 253.5 75.3137 253.5 72C253.5 68.6863 256.186 66 259.5 66C262.814 66 265.5 68.6863 265.5 72ZM258.5 76V74H260.5V76H258.5ZM258.5 68V73H260.5V68H258.5Z" fill="#C5C7D1"/>
|
||||
<path d="M278.1 67.54H279.552V76H278.388L273.852 70.096V76H272.412V67.54H273.564L278.1 73.432V67.54ZM283.921 76.096C283.321 76.096 282.793 75.972 282.337 75.724C281.889 75.476 281.541 75.12 281.293 74.656C281.045 74.192 280.921 73.652 280.921 73.036C280.921 72.42 281.045 71.884 281.293 71.428C281.541 70.964 281.889 70.608 282.337 70.36C282.793 70.112 283.321 69.988 283.921 69.988C284.521 69.988 285.045 70.112 285.493 70.36C285.949 70.608 286.297 70.964 286.537 71.428C286.785 71.884 286.909 72.42 286.909 73.036C286.909 73.652 286.785 74.192 286.537 74.656C286.297 75.12 285.949 75.476 285.493 75.724C285.045 75.972 284.521 76.096 283.921 76.096ZM283.909 74.932C284.397 74.932 284.769 74.772 285.025 74.452C285.281 74.132 285.409 73.66 285.409 73.036C285.409 72.42 285.281 71.952 285.025 71.632C284.769 71.304 284.401 71.14 283.921 71.14C283.441 71.14 283.069 71.304 282.805 71.632C282.549 71.952 282.421 72.42 282.421 73.036C282.421 73.66 282.549 74.132 282.805 74.452C283.061 74.772 283.429 74.932 283.909 74.932Z" fill="#8B8FA2"/>
|
||||
<defs>
|
||||
<filter id="filter0_d_2902:71050" x="0.5" y="56" width="322" height="87" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feMorphology radius="5" operator="erode" in="SourceAlpha" result="effect1_dropShadow_2902:71050"/>
|
||||
<feOffset dy="7"/>
|
||||
<feGaussianBlur stdDeviation="1.5"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.0980392 0 0 0 0 0.129412 0 0 0 0 0.278431 0 0 0 0.1 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_2902:71050"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_2902:71050" result="shape"/>
|
||||
</filter>
|
||||
<clipPath id="clip0_2902:71050">
|
||||
<rect width="23" height="26" fill="white" transform="translate(151 11)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 9.6 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 43 KiB |
1
changes/UI-hackathon-localized-datetime-tooltips
Normal file
1
changes/UI-hackathon-localized-datetime-tooltips
Normal file
|
|
@ -0,0 +1 @@
|
|||
* Add locally-formated datetime tooltips for all "last Xed" data
|
||||
1
changes/issue-6799-software-empty-states
Normal file
1
changes/issue-6799-software-empty-states
Normal file
|
|
@ -0,0 +1 @@
|
|||
* Update software empty states
|
||||
1
changes/ui-hackathon-change-buttons
Normal file
1
changes/ui-hackathon-change-buttons
Normal file
|
|
@ -0,0 +1 @@
|
|||
- update buttons to the the new style guide.
|
||||
1
changes/ui-hackathon-filtering
Normal file
1
changes/ui-hackathon-filtering
Normal file
|
|
@ -0,0 +1 @@
|
|||
- adds bookmarkability of url when it includes the `query` query param on the manage hosts page.
|
||||
|
|
@ -143,7 +143,7 @@ describe("Policies flow (empty)", () => {
|
|||
managePoliciesPage.visitManagePoliciesPage();
|
||||
});
|
||||
it("creates a custom policy", () => {
|
||||
cy.getAttached(".policies-table__action-button-container").within(() => {
|
||||
cy.getAttached(".empty-table__cta-buttons").within(() => {
|
||||
cy.findByText(/add a policy/i).click();
|
||||
});
|
||||
cy.findByText(/create your own policy/i).click();
|
||||
|
|
|
|||
|
|
@ -66,8 +66,9 @@ const dashboardPage = {
|
|||
cy.getAttached(".dashboard-page__wrapper").within(() => {
|
||||
cy.findByText(/platform/i).should("exist");
|
||||
cy.getAttached(".hosts-summary").should("exist");
|
||||
cy.getAttached(".home-software").should("exist");
|
||||
cy.getAttached(".activity-feed").should("exist");
|
||||
// hidden if no software
|
||||
cy.get(".home-software").should("not.exist");
|
||||
if (tier === "premium") {
|
||||
cy.getAttached(".hosts-missing").should("exist");
|
||||
cy.getAttached(".hosts-low-space").should("exist");
|
||||
|
|
@ -82,7 +83,8 @@ const dashboardPage = {
|
|||
cy.getAttached(".dashboard-page__wrapper").within(() => {
|
||||
cy.findByText(/platform/i).should("exist");
|
||||
cy.getAttached(".hosts-summary").should("exist");
|
||||
cy.getAttached(".home-software").should("exist");
|
||||
// hidden if no software
|
||||
cy.get(".home-software").should("not.exist");
|
||||
cy.get(".activity-feed").should("not.exist");
|
||||
if (tier === "premium") {
|
||||
cy.getAttached(".hosts-missing").should("exist");
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ const manageHostsPage = {
|
|||
.click();
|
||||
cy.contains("button", /add secret/i).click();
|
||||
cy.contains("button", /save/i).click();
|
||||
cy.contains("button", /done/i).click();
|
||||
cy.findByText(/successfully added/i);
|
||||
cy.getAttached(".modal__ex").click();
|
||||
},
|
||||
|
||||
allowsAddHosts: () => {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ const manageSchedulePage = {
|
|||
|
||||
hidesButton: (text: string) => {
|
||||
if (text === "Advanced") {
|
||||
cy.getAttached(".no-schedule__cta-buttons").within(() => {
|
||||
cy.getAttached(".empty-table__cta-buttons").within(() => {
|
||||
cy.contains("button", text).should("not.exist");
|
||||
});
|
||||
} else cy.contains("button", text).should("not.exist");
|
||||
|
|
@ -25,7 +25,7 @@ const manageSchedulePage = {
|
|||
},
|
||||
|
||||
allowsAddSchedule: () => {
|
||||
cy.getAttached(".no-schedule__cta-buttons").within(() => {
|
||||
cy.getAttached(".empty-table__cta-buttons").within(() => {
|
||||
cy.findByRole("button", { name: /schedule a query/i }).click({
|
||||
force: true,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,10 +16,8 @@ describe("Teams flow (empty)", () => {
|
|||
cy.visit("/settings/teams");
|
||||
});
|
||||
it("creates a new team", () => {
|
||||
cy.getAttached(".no-teams").within(() => {
|
||||
cy.getAttached(".no-teams__inner-text").within(() => {
|
||||
cy.contains("button", /create team/i).click();
|
||||
});
|
||||
cy.getAttached(".empty-table__cta-buttons").within(() => {
|
||||
cy.contains("button", /create team/i).click();
|
||||
});
|
||||
cy.findByLabelText(/team name/i)
|
||||
.click()
|
||||
|
|
|
|||
|
|
@ -60,9 +60,9 @@ How to schedule a query:
|
|||
|
||||
5. Select **Schedule**.
|
||||
|
||||
With [the teams feature](https://fleetdm.com/docs/using-fleet/teams), you can schedule queries for groups of hosts. This allows you to collect different data for each group.
|
||||
With Fleet Premium, you can schedule queries for groups of hosts using [the teams feature](https://fleetdm.com/docs/using-fleet/teams). This allows you to collect different data for each group.
|
||||
|
||||
> In Fleet, groups of hosts are called "teams."
|
||||
> In Fleet Premium, groups of hosts are called "teams."
|
||||
|
||||
How to use teams to schedule queries for a group of hosts:
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
display: inline-block;
|
||||
height: 4px;
|
||||
width: 50px;
|
||||
background-color: $ui-fleet-blue-15;
|
||||
background-color: $ui-fleet-black-10;
|
||||
border-radius: 2px;
|
||||
margin-right: $pad-small;
|
||||
overflow: hidden;
|
||||
|
|
|
|||
41
frontend/components/EmptyTable/EmptyTable.tsx
Normal file
41
frontend/components/EmptyTable/EmptyTable.tsx
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import React from "react";
|
||||
import classnames from "classnames";
|
||||
import Icon from "components/Icon";
|
||||
import { IEmptyTableProps } from "interfaces/empty_table";
|
||||
|
||||
const baseClass = "empty-table";
|
||||
|
||||
const EmptyTable = ({
|
||||
iconName,
|
||||
header,
|
||||
info,
|
||||
additionalInfo,
|
||||
className,
|
||||
primaryButton,
|
||||
secondaryButton,
|
||||
}: IEmptyTableProps): JSX.Element => {
|
||||
const emptyTableClass = classnames(`${baseClass}__container`, className);
|
||||
|
||||
return (
|
||||
<div className={emptyTableClass}>
|
||||
{iconName && (
|
||||
<div className={`${baseClass}__image-wrapper`}>
|
||||
<Icon name={iconName} />
|
||||
</div>
|
||||
)}
|
||||
<div className={`${baseClass}__inner`}>
|
||||
{header && <h2>{header}</h2>}
|
||||
{info && <p>{info}</p>}
|
||||
{additionalInfo && <p>{additionalInfo}</p>}
|
||||
</div>
|
||||
{primaryButton && (
|
||||
<div className={`${baseClass}__cta-buttons`}>
|
||||
{primaryButton}
|
||||
{secondaryButton && secondaryButton}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmptyTable;
|
||||
52
frontend/components/EmptyTable/_styles.scss
Normal file
52
frontend/components/EmptyTable/_styles.scss
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
.empty-table {
|
||||
&__container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 96px auto 0; // 96px to top of div
|
||||
max-width: 450px; // standard empty state width
|
||||
gap: $pad-medium; // 16px between image, text, and buttons
|
||||
}
|
||||
|
||||
&__inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $pad-small; // 4px from header to info text
|
||||
|
||||
h2,
|
||||
h2 a {
|
||||
text-align: center;
|
||||
font-size: $small;
|
||||
font-weight: $bold;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
text-align: center;
|
||||
color: $core-fleet-blue;
|
||||
font-size: $x-small;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: $core-fleet-black;
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
&::before {
|
||||
content: "•";
|
||||
color: $core-vibrant-blue;
|
||||
margin-right: $pad-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__cta-buttons {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: $pad-medium; // 16px between buttons
|
||||
}
|
||||
}
|
||||
1
frontend/components/EmptyTable/index.ts
Normal file
1
frontend/components/EmptyTable/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./EmptyTable";
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
code {
|
||||
background-color: $ui-off-white;
|
||||
color: $core-fleet-blue;
|
||||
border: 1px solid $ui-fleet-blue-15;
|
||||
border: 1px solid $ui-fleet-black-10;
|
||||
border-radius: 4px;
|
||||
padding: 7px $pad-medium;
|
||||
margin: $pad-large 0 0 44px;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
code {
|
||||
background-color: $ui-off-white;
|
||||
color: $core-fleet-blue;
|
||||
border: 1px solid $ui-fleet-blue-15;
|
||||
border: 1px solid $ui-fleet-black-10;
|
||||
border-radius: 4px;
|
||||
padding: 7px $pad-medium;
|
||||
margin: $pad-large 0 0 44px;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
z-index: 999;
|
||||
background-color: $core-vibrant-blue;
|
||||
margin: auto;
|
||||
border: 1px solid $ui-fleet-blue-15;
|
||||
border: 1px solid $ui-fleet-black-10;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0px 7px 3px -5px rgba(25, 33, 71, 0.1);
|
||||
border-radius: 8px;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
import React from "react";
|
||||
|
||||
import { uniqueId } from "lodash";
|
||||
import { humanHostLastSeen } from "utilities/helpers";
|
||||
import ReactTooltip from "react-tooltip";
|
||||
import intlFormat from "date-fns/intlFormat";
|
||||
|
||||
export default ({ timeString }: { timeString: string }): JSX.Element => {
|
||||
const id = uniqueId();
|
||||
return timeString === "Unavailable" ? (
|
||||
<span>Unavailable</span>
|
||||
) : (
|
||||
<>
|
||||
<span className={"date-tooltip"} data-tip data-for={`tooltip-${id}`}>
|
||||
{humanHostLastSeen(timeString)}
|
||||
</span>
|
||||
<ReactTooltip
|
||||
className="date-tooltip-text"
|
||||
place="top"
|
||||
type="dark"
|
||||
effect="solid"
|
||||
id={`tooltip-${id}`}
|
||||
backgroundColor="#3e4771"
|
||||
>
|
||||
{intlFormat(
|
||||
new Date(timeString),
|
||||
{
|
||||
year: "numeric",
|
||||
month: "numeric",
|
||||
day: "numeric",
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
second: "numeric",
|
||||
},
|
||||
{ locale: window.navigator.languages[0] }
|
||||
)}
|
||||
</ReactTooltip>
|
||||
</>
|
||||
);
|
||||
};
|
||||
1
frontend/components/HumanTimeDiffWithDateTip/index.ts
Normal file
1
frontend/components/HumanTimeDiffWithDateTip/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./HumanTimeDiffWithDateTip";
|
||||
|
|
@ -6,4 +6,5 @@
|
|||
// of the main-content when there is a sidebar.
|
||||
// Without it the main content pushes the sidebar off the page.
|
||||
overflow: auto;
|
||||
animation: fade-in 250ms ease-out;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
overflow: scroll;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
animation: fade-in 150ms ease-out;
|
||||
}
|
||||
|
||||
&__content {
|
||||
|
|
@ -52,7 +53,7 @@
|
|||
font-weight: $regular;
|
||||
text-align: left;
|
||||
padding-bottom: $pad-xsmall;
|
||||
border-bottom: 1px solid $ui-fleet-blue-15;
|
||||
border-bottom: 1px solid $ui-fleet-black-10;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
|
|
@ -69,5 +70,6 @@
|
|||
width: 570px;
|
||||
padding: $pad-xxlarge;
|
||||
border-radius: 8px;
|
||||
animation: scale-up 150ms ease-out;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
animation: fade-and-scale 150ms ease-out;
|
||||
&.centered {
|
||||
margin: 120px auto;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
width: 100%;
|
||||
font-size: $x-small;
|
||||
background-color: $ui-off-white;
|
||||
border: 1px solid $ui-fleet-blue-15;
|
||||
border: 1px solid $ui-fleet-black-10;
|
||||
border-radius: 4px;
|
||||
padding: 4px;
|
||||
padding-left: 20px;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import React from "react";
|
||||
|
||||
interface ITextCellProps {
|
||||
value: string | number | boolean;
|
||||
formatter?: (val: any) => string; // string, number, or null
|
||||
value: string | number | boolean | { timeString: string };
|
||||
formatter?: (val: any) => JSX.Element | string; // string, number, or null
|
||||
greyed?: boolean;
|
||||
classes?: string;
|
||||
}
|
||||
|
|
@ -18,7 +18,6 @@ const TextCell = ({
|
|||
if (typeof value === "boolean") {
|
||||
val = value.toString();
|
||||
}
|
||||
|
||||
return (
|
||||
<span className={`text-cell ${classes} ${greyed && "grey-cell"}`}>
|
||||
{formatter(val)}
|
||||
|
|
|
|||
|
|
@ -9,14 +9,14 @@ $shadow-transition-width: 10px;
|
|||
.data-table {
|
||||
&__wrapper {
|
||||
position: relative;
|
||||
border: 1px solid $ui-fleet-blue-15;
|
||||
border: 1px solid $ui-fleet-black-10;
|
||||
border-radius: 6px;
|
||||
margin-top: $pad-small;
|
||||
flex-grow: 1;
|
||||
width: 100%;
|
||||
|
||||
// Shadow
|
||||
background-image:
|
||||
background-image:
|
||||
/* Shadows */ linear-gradient(
|
||||
to right,
|
||||
white,
|
||||
|
|
@ -55,18 +55,25 @@ $shadow-transition-width: 10px;
|
|||
white-space: initial; // wraps long text with tooltip
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
background-color: $ui-off-white-opaque; // opaque needed for horizontal scroll shadow
|
||||
tr, .single-row {
|
||||
transition: background-color 150ms ease-out;
|
||||
&:hover {
|
||||
background-color: $ui-off-white-opaque; // opaque needed for horizontal scroll shadow
|
||||
}
|
||||
}
|
||||
|
||||
.single-row:hover {
|
||||
cursor: pointer;
|
||||
background-color: $ui-vibrant-blue-10-opaque; // opaque needed for horizontal scroll shadow
|
||||
.single-row {
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
&:active {
|
||||
background-color: $ui-vibrant-blue-10-opaque; // opaque needed for horizontal scroll shadow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tr {
|
||||
border-bottom: 1px solid $ui-fleet-blue-15;
|
||||
border-bottom: 1px solid $ui-fleet-black-10;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 0;
|
||||
|
|
@ -88,7 +95,7 @@ $shadow-transition-width: 10px;
|
|||
background-color: $ui-off-white-opaque; // opaque needed for horizontal scroll shadow
|
||||
color: $core-fleet-black;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid $ui-fleet-blue-15;
|
||||
border-bottom: 1px solid $ui-fleet-black-10;
|
||||
|
||||
// resize header icons
|
||||
img {
|
||||
|
|
@ -109,7 +116,7 @@ $shadow-transition-width: 10px;
|
|||
th {
|
||||
padding: $pad-medium $pad-large;
|
||||
white-space: nowrap;
|
||||
border-right: 1px solid $ui-fleet-blue-15;
|
||||
border-right: 1px solid $ui-fleet-black-10;
|
||||
|
||||
&:first-child {
|
||||
border-top-left-radius: 6px;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ interface ITableContainerProps {
|
|||
manualSortBy?: boolean;
|
||||
defaultSortHeader?: string;
|
||||
defaultSortDirection?: string;
|
||||
defaultSearchQuery?: string;
|
||||
actionButtonText?: string;
|
||||
actionButtonIcon?: string;
|
||||
actionButtonVariant?: ButtonVariant;
|
||||
|
|
@ -97,6 +98,7 @@ const TableContainer = ({
|
|||
filters,
|
||||
isLoading,
|
||||
manualSortBy = false,
|
||||
defaultSearchQuery = "",
|
||||
defaultSortHeader = "name",
|
||||
defaultSortDirection = "asc",
|
||||
inputPlaceHolder = "Search",
|
||||
|
|
@ -143,7 +145,7 @@ const TableContainer = ({
|
|||
setExportRows,
|
||||
resetPageIndex,
|
||||
}: ITableContainerProps): JSX.Element => {
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [searchQuery, setSearchQuery] = useState(defaultSearchQuery);
|
||||
const [sortHeader, setSortHeader] = useState(defaultSortHeader || "");
|
||||
const [sortDirection, setSortDirection] = useState(
|
||||
defaultSortDirection || ""
|
||||
|
|
@ -269,6 +271,7 @@ const TableContainer = ({
|
|||
<div className={`${baseClass}__search-input wide-search`}>
|
||||
<SearchField
|
||||
placeholder={inputPlaceHolder}
|
||||
defaultValue={searchQuery}
|
||||
onChange={onSearchQueryChange}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -335,7 +338,6 @@ const TableContainer = ({
|
|||
{customControl && customControl()}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className={`${baseClass}__search`}>
|
||||
{/* Render search bar only if not empty component */}
|
||||
{searchable && !wideSearch && (
|
||||
|
|
@ -350,6 +352,7 @@ const TableContainer = ({
|
|||
>
|
||||
<SearchField
|
||||
placeholder={inputPlaceHolder}
|
||||
defaultValue={searchQuery}
|
||||
onChange={onSearchQueryChange}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,27 +1,51 @@
|
|||
$base-class: "button";
|
||||
|
||||
@mixin button-variant($color, $hover: null, $active: null, $inverse: null) {
|
||||
@mixin button-focus-outline($offset: 2px) {
|
||||
outline-color: #d9d9fe;
|
||||
outline-offset: $offset;
|
||||
outline-style: solid;
|
||||
outline-width: 2px;
|
||||
}
|
||||
|
||||
@mixin button-variant($color, $hover: null, $active: null, $inverse: false) {
|
||||
background-color: $color;
|
||||
|
||||
@if $inverse {
|
||||
&:hover,
|
||||
&:focus {
|
||||
border: 2px solid $hover;
|
||||
color: $hover;
|
||||
&:hover {
|
||||
background-color: rgba(#192147, .05);
|
||||
|
||||
&:active {
|
||||
background-color: $ui-fleet-black-10;
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
border: 2px solid $active;
|
||||
color: $active;
|
||||
&:focus-visible {
|
||||
// need a slightly larger focus outline to accomodate the :after content box
|
||||
// that correctly displays the border. We chose this approach as adding a
|
||||
// border to the button caused the button to jump around on the screen
|
||||
// when it was added and removed.
|
||||
@include button-focus-outline($offset: 3px);
|
||||
&::after {
|
||||
content: "";
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
border: 1px solid $core-vibrant-blue;
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
} @else {
|
||||
&:hover:not(.button--disabled),
|
||||
&:focus {
|
||||
&:hover:not(.button--disabled) {
|
||||
background-color: $hover;
|
||||
|
||||
&:active {
|
||||
background-color: $active;
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: $active;
|
||||
&:focus-visible {
|
||||
@include button-focus-outline()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -37,7 +61,7 @@ $base-class: "button";
|
|||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: $pad-small $pad-medium;
|
||||
border-radius: 4px;
|
||||
border-radius: 6px;
|
||||
font-size: $x-small;
|
||||
font-family: "Nunito Sans", sans-serif;
|
||||
font-weight: $bold;
|
||||
|
|
@ -241,14 +265,8 @@ $base-class: "button";
|
|||
$inverse: true
|
||||
);
|
||||
color: $core-vibrant-blue;
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
border: 0;
|
||||
}
|
||||
padding: $pad-small;
|
||||
}
|
||||
|
||||
&--inverse-alert {
|
||||
|
|
@ -316,7 +334,7 @@ $base-class: "button";
|
|||
display: block;
|
||||
width: 100%;
|
||||
border-radius: 0px;
|
||||
border-bottom: 1px solid $ui-fleet-blue-15;
|
||||
border-bottom: 1px solid $ui-fleet-black-10;
|
||||
|
||||
&:active {
|
||||
box-shadow: none;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
border-radius: 2px;
|
||||
background-color: $core-white;
|
||||
box-shadow: 0 4px 10px rgba(52, 59, 96, 0.15);
|
||||
|
||||
animation: fade-in 150ms ease-out;
|
||||
&--opened {
|
||||
display: inline-block;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
padding: 0 0 $pad-medium;
|
||||
margin: 0;
|
||||
margin-bottom: $pad-xxlarge;
|
||||
border-bottom: 1px solid $ui-fleet-blue-15;
|
||||
border-bottom: 1px solid $ui-fleet-black-10;
|
||||
}
|
||||
|
||||
p {
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
|
||||
.Select {
|
||||
&.dropdown__select {
|
||||
border: 1px solid $ui-fleet-blue-15;
|
||||
border: 1px solid $ui-fleet-black-10;
|
||||
border-radius: $border-radius;
|
||||
&:hover {
|
||||
box-shadow: none;
|
||||
|
|
@ -188,6 +188,7 @@
|
|||
border: 0;
|
||||
margin: 1px 0 0;
|
||||
padding: $pad-small;
|
||||
animation: fade-in 150ms ease-out;
|
||||
}
|
||||
|
||||
.Select-noresults {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
.input-field {
|
||||
line-height: 1.5;
|
||||
background-color: $ui-light-grey;
|
||||
border: solid 1px $ui-fleet-blue-15;
|
||||
border: solid 1px $ui-fleet-black-10;
|
||||
border-radius: 4px;
|
||||
font-size: $small;
|
||||
padding: 7px 12px;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
}
|
||||
|
||||
&__input {
|
||||
border: 1px solid $ui-fleet-blue-15;
|
||||
border: 1px solid $ui-fleet-black-10;
|
||||
background-color: $ui-light-grey;
|
||||
border-radius: $border-radius;
|
||||
padding: 9px 30px 9px $pad-medium;
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@
|
|||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid $ui-fleet-blue-15;
|
||||
border: 2px solid $ui-fleet-black-10;
|
||||
transform: translateY(-0.05em);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,14 +7,16 @@ const baseClass = "search-field";
|
|||
|
||||
export interface ISearchFieldProps {
|
||||
placeholder: string;
|
||||
defaultValue?: string;
|
||||
onChange: (value: string) => void;
|
||||
}
|
||||
|
||||
const SearchField = ({
|
||||
placeholder,
|
||||
defaultValue = "",
|
||||
onChange,
|
||||
}: ISearchFieldProps): JSX.Element => {
|
||||
const [searchQueryInput, setSearchQueryInput] = useState("");
|
||||
const [searchQueryInput, setSearchQueryInput] = useState(defaultValue);
|
||||
|
||||
const debouncedOnChange = useDebouncedCallback((newValue: string) => {
|
||||
onChange(newValue);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
font-weight: $bold;
|
||||
font-size: $x-small;
|
||||
color: $core-fleet-black;
|
||||
border-bottom: 1px solid $ui-fleet-blue-15;
|
||||
border-bottom: 1px solid $ui-fleet-black-10;
|
||||
|
||||
&::first-letter {
|
||||
text-transform: uppercase;
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
overflow-y: scroll;
|
||||
max-height: 100%;
|
||||
padding: 0 $pad-large;
|
||||
border-right: 1px solid $ui-fleet-blue-15;
|
||||
border-right: 1px solid $ui-fleet-black-10;
|
||||
background-color: $core-white;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
&.Select {
|
||||
border-radius: $border-radius;
|
||||
border: 1px solid $ui-fleet-blue-15;
|
||||
border: 1px solid $ui-fleet-black-10;
|
||||
|
||||
&.is-focused,
|
||||
&:hover {
|
||||
|
|
|
|||
173
frontend/components/icons/EmptyHosts.tsx
Normal file
173
frontend/components/icons/EmptyHosts.tsx
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
import React from "react";
|
||||
|
||||
const EmptyHosts = () => {
|
||||
return (
|
||||
<svg
|
||||
width="176"
|
||||
height="145"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 176 145"
|
||||
>
|
||||
<ellipse cx="87.762" cy="140.413" rx="87.762" ry="4.14" fill="#C9D5F3" />
|
||||
<path
|
||||
d="M87.57 53.902s9.782-4.437 12.77-10.176c2.988-5.74.144 8.198-6.361 11.526"
|
||||
fill="#506E92"
|
||||
/>
|
||||
<path
|
||||
d="M70.944 73.337c11.923 0 21.59-6.629 21.59-14.806s-9.667-14.805-21.59-14.805-21.589 6.628-21.589 14.805 9.666 14.806 21.589 14.806Z"
|
||||
fill="#E1E1FF"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M85.442 47.56c-6.28 6.553-21.222 2.63-28.424-.342 3.759-2.179 8.619-3.492 13.926-3.492 5.58 0 10.666 1.452 14.498 3.835Z"
|
||||
fill="#D2D2FF"
|
||||
/>
|
||||
<path
|
||||
d="M92.283 58.532c0 3.982-2.353 7.614-6.215 10.263-3.86 2.647-9.206 4.292-15.124 4.292-5.918 0-11.264-1.645-15.124-4.293-3.862-2.648-6.215-6.28-6.215-10.262 0-3.983 2.353-7.615 6.215-10.264 3.86-2.647 9.206-4.292 15.124-4.292 5.918 0 11.264 1.645 15.124 4.292 3.862 2.648 6.215 6.281 6.215 10.264Z"
|
||||
stroke="#CDCDF5"
|
||||
strokeWidth=".5"
|
||||
/>
|
||||
<path
|
||||
d="M17.786 97.113s-.867 19.87-2.361 28.02c-1.494 8.15-2.361 14.95-2.361 14.95s-2.12 1.061 2.361 1.061c4.482 0 84.206-2.122 105.795 0 0 0 1.205.145 3.614 0 3.663-.193 22.842-3.231 22.842-3.231s-2.12-8.343-2.12-20.545c0-12.201-1.494-56.666-1.494-56.666l-20.722-1.254H22.075l-4.289 37.665Z"
|
||||
fill="#F7F8FC"
|
||||
/>
|
||||
<path
|
||||
d="m13.176 140.307-.003.001-.014.007-.05.029c-.044.026-.1.062-.154.103a.642.642 0 0 0-.134.134.443.443 0 0 0 .025.019c.059.039.173.088.376.135.403.093 1.088.159 2.203.159.713 0 3.332-.053 7.32-.135 8.536-.176 23.346-.48 39.16-.66 23.184-.266 48.528-.266 59.339.796l.006.001-108.074-.589Zm0 0 .119-.06.017-.132-.248-.032.112.224Zm-.37.259.002.002-.002-.002Z"
|
||||
stroke="#D3DAE4"
|
||||
strokeWidth=".5"
|
||||
/>
|
||||
<path
|
||||
d="M145.604 117.417c0-12.202-1.494-56.667-1.494-56.667l-20.336-1.254c-.048 8.343-.241 24.258-.819 32.264-.868 11.14-.193 26.525 0 32.939.193 6.414-.241 10.031-.434 11.767-.193 1.736-.53 4.292.916 4.775 3.855.241 24.287-3.28 24.287-3.28s-2.12-8.343-2.12-20.544Z"
|
||||
fill="#E0E7F9"
|
||||
/>
|
||||
<path
|
||||
d="M123.204 91.78v-.002c.573-7.932.768-23.607.818-32.016l19.846 1.223.009.276.047 1.432c.041 1.24.1 3.024.17 5.203.14 4.36.326 10.299.513 16.62.374 12.642.747 26.804.747 32.901 0 6.115.531 11.263 1.063 14.884a77.31 77.31 0 0 0 .731 4.216c.1.489.183.864.242 1.118l.029.125-.622.106c-.566.095-1.379.232-2.366.395-1.975.326-4.649.759-7.442 1.183-2.794.425-5.707.842-8.161 1.138-2.434.293-4.392.464-5.342.411-.544-.199-.802-.799-.872-1.689-.066-.831.043-1.801.137-2.642a7.82 7.82 0 0 1 .019-.168l.019-.172c.201-1.804.603-5.405.416-11.631-.023-.741-.051-1.602-.083-2.562-.245-7.352-.684-20.511.082-30.35Z"
|
||||
stroke="#C8D1F2"
|
||||
strokeWidth=".5"
|
||||
/>
|
||||
<path
|
||||
d="m144.11 60.75-20.336-1.254-.385-.048H22.075l-4.048 35.591c14.264.483 49.606 3.231 75.87 2.074 17.107-.916 23.275-.916 23.275-.916l6.506-26.67s6.168 19.002 10.168 32.264c3.662-1.64 7.469-2.942 11.373-4.051-.434-16.88-1.109-36.99-1.109-36.99Z"
|
||||
fill="#000"
|
||||
style={{ mixBlendMode: "soft-light" }}
|
||||
/>
|
||||
<path
|
||||
d="M22.075 59.497 4.968 91.567s63.05 2.557 104.108 0l14.746-32.07H22.075Z"
|
||||
fill="#F7F8FC"
|
||||
/>
|
||||
<path
|
||||
d="m123.432 59.747-14.521 31.58c-20.495 1.269-46.439 1.268-67.282.95-10.446-.16-19.609-.4-26.16-.6a1422.782 1422.782 0 0 1-9.774-.331l-.319-.012 16.85-31.587h101.206Z"
|
||||
stroke="#D3DAE4"
|
||||
strokeWidth=".5"
|
||||
/>
|
||||
<path
|
||||
d="m123.774 59.496 15.613 31.396s13.879-7.91 22.842-7.717c-4.722-5.787-8.336-10.03-10.264-12.828-1.928-2.797-7.903-9.645-7.903-9.645l-20.288-1.206Z"
|
||||
fill="#E0E7F9"
|
||||
/>
|
||||
<path
|
||||
d="m139.882 90.333-.388.212-15.304-30.774 19.752 1.174.192.22.702.813c.59.686 1.397 1.628 2.263 2.658 1.737 2.064 3.704 4.466 4.66 5.853 1.461 2.12 3.885 5.065 7.013 8.866a1414.54 1414.54 0 0 1 2.929 3.568c-4.451.056-9.938 1.93-14.352 3.808a90.696 90.696 0 0 0-7.467 3.602Z"
|
||||
stroke="#CBCFF2"
|
||||
strokeWidth=".5"
|
||||
/>
|
||||
<path
|
||||
d="M50.207 18.057s-.277-3.373-3.674-4.215c-2.836-.684-8.785 11.312-10.662 15.261-.586 1.262-.753 2.703-.397 4.063.661 2.387 3.088 4.842 11.176 1.007 12.914-6.045 3.557-16.116 3.557-16.116Z"
|
||||
fill="#E1E1FF"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M49.603 16.188c.531.98.604 1.869.604 1.869s.353.38.826 1.035c.748 2.495 1.35 6.152 1.79 9.668-.879 1.913-2.738 3.805-6.173 5.413-8.087 3.835-10.515 1.38-11.176-1.007a5.71 5.71 0 0 1-.173-1.073c3.207-1.105 8.002-3.457 8.699-6.593 1-4.5 2-9 5-9.5.207-.034.408.032.603.188Z"
|
||||
fill="#D2D2FF"
|
||||
/>
|
||||
<path
|
||||
d="M69.494 13.175s-2.291-2.525-.513-5.532c1.457-2.568 14.246 1.313 18.406 2.671 1.334.429 2.488 1.275 3.21 2.481 1.252 2.1 1.393 5.543-6.87 8.87-13.285 5.223-14.233-8.49-14.233-8.49Z"
|
||||
fill="#E1E1FF"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M87.673 10.414c1.212.453 2.254 1.263 2.925 2.381 1.25 2.1 1.392 5.543-6.871 8.87-1.773.696-3.326 1.056-4.687 1.156C77.45 20.21 75.816 15.95 75 13.5c5.392 1.797 15.301 3.627 12.673-3.086Z"
|
||||
fill="#D2D2FF"
|
||||
/>
|
||||
<path
|
||||
d="m49.958 18.077.007.086.059.064.001.001.004.005.019.02.072.082a14.958 14.958 0 0 1 1.137 1.526c.667 1.025 1.417 2.444 1.77 4.041.353 1.594.31 3.357-.595 5.087-.906 1.732-2.693 3.462-5.888 4.957l-.002.001c-4.026 1.91-6.589 2.224-8.203 1.813-1.588-.404-2.309-1.524-2.623-2.659-.34-1.299-.181-2.68.381-3.892.938-1.973 2.89-5.949 4.959-9.344 1.035-1.7 2.094-3.242 3.064-4.315.485-.537.94-.946 1.349-1.198.412-.255.744-.33 1.005-.267 1.613.4 2.48 1.395 2.948 2.296a5.281 5.281 0 0 1 .52 1.567 2.8 2.8 0 0 1 .013.1l.002.024v.005h.001ZM69.68 13.007h-.001l-.004-.004-.016-.019a4.51 4.51 0 0 1-.287-.388 5.207 5.207 0 0 1-.565-1.142c-.332-.954-.454-2.258.39-3.684l.002-.004c.135-.238.419-.432.887-.56.465-.128 1.073-.182 1.796-.17 1.445.024 3.292.306 5.237.715 3.886.816 8.113 2.123 10.19 2.801h.001c1.283.412 2.385 1.223 3.073 2.372.595.998.914 2.287.128 3.726-.8 1.462-2.763 3.126-6.876 4.782-3.287 1.292-5.779 1.4-7.669.869-1.888-.532-3.207-1.712-4.131-3.064-.926-1.354-1.45-2.873-1.742-4.06-.146-.593-.234-1.1-.285-1.458a10.76 10.76 0 0 1-.062-.526l-.002-.027v-.008l-.006-.087-.058-.064Z"
|
||||
stroke="#CECEF5"
|
||||
strokeWidth=".5"
|
||||
/>
|
||||
<path
|
||||
d="M69.398 41.456c9.56-3.3 14.798-13.261 11.698-22.25-3.099-8.989-13.362-13.601-22.922-10.302-9.56 3.3-14.797 13.26-11.698 22.25 3.1 8.988 13.362 13.6 22.922 10.302Z"
|
||||
fill="#506E92"
|
||||
/>
|
||||
<path
|
||||
opacity=".25"
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M56.907 9.39C58.56 11.456 60.28 14.398 61 18c1.193 5.962-7.391 14.394-11.915 17.934l.032.04 32.334-7.266c.794-3.067.734-6.345-.354-9.502-3.1-8.989-13.362-13.601-22.923-10.302-.431.149-.854.312-1.267.487Z"
|
||||
fill="#113563"
|
||||
/>
|
||||
<path
|
||||
d="M60.29 43.242s2.691 4.17 7.139 3.289c4.447-.882 7.953-1.228 8.29-5.672.283-4.485-15.43 2.383-15.43 2.383Z"
|
||||
fill="#506E92"
|
||||
/>
|
||||
<path
|
||||
d="M62.432 21.44s-19.01 7.62-18.208 18.183c.803 10.563 7.046 11.209 10.933 9.535 3.838-1.666 8.972-5.046 13.636-6.107 4.663-1.062 5.784-1.382 8.733-1.544 2.95-.163 11.252-.713 11.637-5.799.322-5.173-4.075-22.599-26.73-14.269Z"
|
||||
fill="#F0F0FF"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M79.572 20.313C82.844 24.604 86.064 31.56 78.5 33c-15.629 1.488-27.276 9.439-32.045 14.029 2.373 3.332 6.076 3.26 8.702 2.129 1.278-.555 2.7-1.3 4.201-2.086 3.005-1.574 6.325-3.313 9.435-4.021l1.399-.32c3.529-.81 4.705-1.08 7.335-1.224 2.949-.163 11.25-.713 11.636-5.799.214-3.439-1.657-12.292-9.591-15.395Z"
|
||||
fill="#E9E9FF"
|
||||
/>
|
||||
<path
|
||||
d="m62.512 21.676.01-.004c5.634-2.07 10.113-2.533 13.653-1.998 3.537.535 6.152 2.067 8.06 4.011 3.83 3.9 4.835 9.472 4.678 12.006-.18 2.377-2.209 3.737-4.683 4.524-2.458.783-5.242.96-6.717 1.042-2.652.146-3.848.42-7.375 1.23l-1.4.32c-2.364.538-4.836 1.66-7.185 2.847-.792.4-1.569.807-2.323 1.202-1.494.783-2.902 1.521-4.172 2.072-1.897.817-4.34 1.055-6.397-.164-2.05-1.215-3.79-3.922-4.187-9.16-.195-2.56.81-4.958 2.456-7.128 1.647-2.17 3.925-4.096 6.24-5.7 2.313-1.605 4.653-2.881 6.416-3.757a52.732 52.732 0 0 1 2.73-1.261c.069-.03.121-.05.157-.065l.04-.017Z"
|
||||
stroke="#E2E2FE"
|
||||
strokeWidth=".5"
|
||||
/>
|
||||
<path
|
||||
d="M54.383 22.048a1.928 1.928 0 1 0-.59-3.812 1.928 1.928 0 0 0 .59 3.812ZM68.687 17.106a1.928 1.928 0 1 0-.59-3.811 1.928 1.928 0 0 0 .59 3.811Z"
|
||||
fill="#9DFFED"
|
||||
/>
|
||||
<path
|
||||
d="M65.858 31.537c2.968-1.024 4.98-2.998 4.495-4.408C69.867 25.72 67 23.5 63.5 26c-2.968 1.024-4.986 2.59-4.5 4 .486 1.41 3.89 2.562 6.858 1.537Z"
|
||||
fill="#E9E9FF"
|
||||
/>
|
||||
<path
|
||||
d="M64.858 30.537c2.968-1.024 4.981-2.998 4.495-4.408-.486-1.41-3.287-1.722-6.256-.698-2.968 1.025-4.98 2.998-4.495 4.408.486 1.41 3.287 1.723 6.256.698Z"
|
||||
fill="#506E92"
|
||||
/>
|
||||
<path
|
||||
d="M50.49 65.225c5.054-.915 8.645-4.46 8.02-7.92-.626-3.46-5.23-5.523-10.285-4.608-5.054.915-8.645 4.461-8.02 7.921.626 3.46 5.23 5.523 10.285 4.607Z"
|
||||
fill="#F0F0FF"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M41.731 63.297c4.301.445 10.427.387 11.769-2.297 1.386-2.771.621-6.243-.15-8.206 2.732.657 4.76 2.298 5.16 4.51.625 3.46-2.966 7.006-8.02 7.921-3.548.643-6.875-.182-8.759-1.928Z"
|
||||
fill="#E9E9FF"
|
||||
/>
|
||||
<path
|
||||
d="M99.623 60.59c.625-3.459-2.966-7.005-8.02-7.92-5.054-.915-9.659 1.148-10.284 4.607-.625 3.46 2.965 7.006 8.02 7.921 5.054.915 9.658-1.147 10.284-4.607Z"
|
||||
fill="#F0F0FF"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M83.436 62.417c4.288.595 10.243.725 11.564-1.917 1.005-2.01 1.273-4.256 1.252-6.02 2.401 1.575 3.777 3.861 3.37 6.11-.625 3.46-5.23 5.523-10.284 4.608-2.393-.433-4.457-1.456-5.902-2.78Z"
|
||||
fill="#E9E9FF"
|
||||
/>
|
||||
<path
|
||||
d="M58.263 57.35c.294 1.624-.397 3.297-1.81 4.691-1.41 1.394-3.524 2.489-6.008 2.938-2.484.45-4.846.166-6.656-.645-1.81-.811-3.044-2.136-3.337-3.76-.294-1.625.397-3.298 1.81-4.693 1.41-1.393 3.524-2.488 6.008-2.938 2.484-.45 4.847-.165 6.656.646 1.81.81 3.044 2.135 3.337 3.76ZM91.559 52.916c2.484.45 4.597 1.545 6.008 2.938 1.412 1.395 2.103 3.067 1.81 4.692-.294 1.625-1.527 2.95-3.338 3.76-1.81.812-4.172 1.096-6.656.646-2.484-.45-4.597-1.544-6.008-2.938-1.412-1.395-2.104-3.067-1.81-4.692.294-1.625 1.527-2.95 3.337-3.76 1.81-.811 4.173-1.096 6.657-.646Z"
|
||||
stroke="#E2E2FE"
|
||||
strokeWidth=".5"
|
||||
/>
|
||||
<path
|
||||
d="M75.376 117.757c-.559.142-1.151.294-1.736.48-.33-1.513-.638-2.865-.793-3.402-.23-.758-.52-.944-.83-.995-.27-.043-.576-.002-.808-.036-.327-.046-.489-.024-.663.095-.191.123-.208.202-.16.394.268 1.121.613 2.983.958 4.771-.183.084-.362.185-.527.265-.214.11-.156.262-.024.415.267.321.356.522.522.739.115.157.26.287.413.378.276 1.23.56 2.272.843 2.794.114.231.232.257.472.251.24-.005.52.072.817.071a2.65 2.65 0 0 0 .82-.135c.135-.053.222-.15.16-.394-.196-.693-.437-1.783-.7-2.961.55-.177 1.155-.35 1.718-.549.298 1.242.6 2.28.886 2.82.251.475.519.648.92.62.327-.028.48-.085.742-.078.262.008.493.041.712.023.2-.014.357-.054.453-.116.113-.066.17-.136.125-.385-.608-2.965-1.992-8.315-2.682-10.22-.239-.645-.502-.949-.778-1.083-.21-.095-.41-.081-.681-.049-.253.027-.432-.02-.686-.067a1.112 1.112 0 0 0-.69.064c-.134.054-.16.097-.116.272.355 1.247.813 3.694 1.313 6.018Zm12.618-2.606c-.64-2.512-2.757-5.204-6.084-4.262a3.684 3.684 0 0 1-.28-.078c-.555-.137-.943.018-1.26.303-1.156 1.001-2.078 3.577-1.468 6.115.548 2.294 2.272 3.953 4.297 3.957.985.436 1.972.667 2.934.068 1.297-.813 2.702-2.807 1.862-6.103Zm-2.6.143c.564 2.216-.535 4.242-2.233 4.285-1.669-1.153-2.424-4.769-.443-6.834 1.188.068 2.31 1.119 2.675 2.549Zm9.53 3.123c1.207-1.311.914-2.388.4-3.018-.913-1.105-3.207-1.578-4.176-2.316-.6-.46-.909-1.161-.633-1.826.924-.31 2.068.457 2.852 1.13.241.217.359.169.441-.019.074-.149.103-.324.158-.839.05-.385.18-.53.082-1.062-.18-.994-1.994-2.426-3.957-2.185-.685.082-1.335.303-1.64 2.016-.46.897-.438 1.635-.274 2.28.338 1.326 1.937 2.422 2.826 2.92.805.463 1.62.887 1.774 1.349-.656.558-2.65-.048-3.635-.707-.245-.16-.359-.094-.388.081-.026.192.058.449.065.837-.004.206.004.965.434 1.339 1.298 1.155 3.138 1.67 4.132 1.416.75-.191 1.19-.508 1.54-1.396Zm2.148-8.628c.465 2.11.77 5.562 1.91 7.482.162.274.433.391.796.354.379-.041.532-.024.777-.012.244.012.323.029.523-.06.183-.083.37-.224.44-.316.082-.114.09-.227.041-.493-.407-1.959-1.38-5.556-2.032-7.749 1.216-.403 2.305-.792 2.745-1.034.178-.102.174-.193.142-.315a1.457 1.457 0 0 0-.132-.375c-.119-.248-.351-.356-.549-.548-.171-.16-.426-.504-.728-.817-.272-.265-.456-.329-.896-.236-2.285.509-4.258 1.588-6.476 2.358-.34.124-.401.177-.23.486.137.244.292.632.428.801.167.218.337.304.6.46.24.143.412.23.618.381.205.152.402.121.746.014.38-.115.811-.244 1.277-.381Zm12.568 4.874c1.208-1.311.914-2.388.401-3.018-.914-1.105-3.208-1.578-4.176-2.316-.601-.46-.91-1.161-.633-1.826.924-.31 2.067.457 2.851 1.13.242.217.359.169.441-.02.074-.148.104-.323.158-.839.05-.384.181-.529.082-1.061-.179-.994-1.994-2.426-3.957-2.185-.685.081-1.335.303-1.641 2.016-.458.897-.437 1.634-.272 2.28.338 1.325 1.936 2.422 2.825 2.92.806.463 1.62.887 1.775 1.349-.657.558-2.651-.048-3.636-.707-.245-.16-.358-.094-.388.08-.025.193.059.45.065.838-.003.205.005.965.434 1.338 1.298 1.156 3.138 1.671 4.133 1.417.75-.191 1.189-.508 1.538-1.396Z"
|
||||
fill="#C9D5F3"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmptyHosts;
|
||||
77
frontend/components/icons/EmptyIntegrations.tsx
Normal file
77
frontend/components/icons/EmptyIntegrations.tsx
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import React from "react";
|
||||
|
||||
const EmptyIntegrations = () => {
|
||||
return (
|
||||
<svg
|
||||
width="322"
|
||||
height="176"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 322 176"
|
||||
>
|
||||
<circle cx="162.5" cy="101" r="75" fill="#F1F0FF" />
|
||||
<g filter="url(#a)">
|
||||
<path
|
||||
d="M21 69a8 8 0 0 1 8-8h263a8 8 0 0 1 8 8v64a8 8 0 0 1-8 8H29a8 8 0 0 1-8-8V69Z"
|
||||
fill="#fff"
|
||||
/>
|
||||
<path
|
||||
d="M29 60.5a8.5 8.5 0 0 0-8.5 8.5v64a8.5 8.5 0 0 0 8.5 8.5h263a8.5 8.5 0 0 0 8.5-8.5V69a8.5 8.5 0 0 0-8.5-8.5H29Z"
|
||||
stroke="#C5C7D1"
|
||||
/>
|
||||
</g>
|
||||
<rect x="69" y="104" width="101" height="6" rx="3" fill="#C5C7D1" />
|
||||
<rect x="28" y="104" width="23" height="6" rx="3" fill="#C5C7D1" />
|
||||
<rect x="28" y="123" width="23" height="6" rx="3" fill="#C5C7D1" />
|
||||
<rect x="69" y="123" width="94" height="6" rx="3" fill="#E2E4EA" />
|
||||
<rect x="181" y="123" width="60" height="6" rx="3" fill="#E2E4EA" />
|
||||
<rect x="183" y="104" width="43" height="6" rx="3" fill="#E2E4EA" />
|
||||
<path
|
||||
d="M300 91.5h.5V69a8.5 8.5 0 0 0-8.5-8.5H29a8.5 8.5 0 0 0-8.5 8.5v22.5H300Z"
|
||||
fill="#F9FAFC"
|
||||
stroke="#C5C7D1"
|
||||
/>
|
||||
<path
|
||||
d="M72.7 79.1c-.967 0-1.707-.25-2.22-.75-.507-.507-.76-1.253-.76-2.24v-4.16H71v4.15c0 .633.143 1.11.43 1.43.293.313.717.47 1.27.47 1.127 0 1.69-.633 1.69-1.9v-4.15h1.27v4.16c0 .987-.25 1.733-.75 2.24-.5.5-1.237.75-2.21.75Zm4.453-.1v-7.05h3.06c.773 0 1.37.187 1.79.56.42.367.63.883.63 1.55 0 .527-.137.963-.41 1.31-.273.34-.667.57-1.18.69.34.107.62.363.84.77l1.18 2.17h-1.41l-1.22-2.25c-.12-.22-.264-.37-.43-.45-.16-.08-.367-.12-.62-.12h-.95V79h-1.28Zm1.28-3.77h1.56c.933 0 1.4-.38 1.4-1.14 0-.753-.467-1.13-1.4-1.13h-1.56v2.27ZM84.008 79v-7.05h1.28v5.96h3.38V79h-4.66ZM30.47 79v-5.98h-2.42v-1.07h6.12v1.07h-2.42V79h-1.28Zm4.177 1.8.9-2-2.03-4.69h1.33l1.37 3.42 1.4-3.42h1.26l-2.94 6.69h-1.29Zm4.823 0v-6.69h1.22v.78c.14-.267.35-.48.63-.64.287-.16.61-.24.97-.24.427 0 .8.103 1.12.31.327.207.58.5.76.88.18.373.27.823.27 1.35 0 .52-.09.973-.27 1.36-.18.38-.43.673-.75.88-.32.207-.696.31-1.13.31-.346 0-.66-.073-.94-.22a1.632 1.632 0 0 1-.63-.61v2.53h-1.25Zm2.47-2.65c.374 0 .674-.133.9-.4.227-.273.34-.673.34-1.2 0-.533-.113-.93-.34-1.19-.226-.267-.526-.4-.9-.4-.373 0-.673.133-.9.4-.226.26-.34.657-.34 1.19 0 .527.114.927.34 1.2.227.267.527.4.9.4Zm5.936.95c-.553 0-1.03-.103-1.43-.31-.4-.207-.71-.5-.93-.88-.213-.38-.32-.83-.32-1.35 0-.507.104-.95.31-1.33.214-.38.504-.677.87-.89.374-.22.797-.33 1.27-.33.694 0 1.24.22 1.64.66.407.44.61 1.04.61 1.8v.37h-3.5c.094.873.594 1.31 1.5 1.31.274 0 .547-.04.82-.12a2.33 2.33 0 0 0 .75-.4l.35.84a2.615 2.615 0 0 1-.88.46c-.353.113-.706.17-1.06.17Zm-.18-4.24c-.366 0-.663.113-.89.34-.226.227-.363.533-.41.92h2.46c-.026-.407-.14-.717-.34-.93-.193-.22-.466-.33-.82-.33ZM183.77 79v-7.05h1.28v3.12h.02l3.11-3.12h1.56l-3.43 3.39 3.6 3.66h-1.58l-3.26-3.26h-.02V79h-1.28Zm8.804.1c-.554 0-1.03-.103-1.43-.31-.4-.207-.71-.5-.93-.88-.214-.38-.32-.83-.32-1.35 0-.507.103-.95.31-1.33a2.32 2.32 0 0 1 .87-.89c.373-.22.796-.33 1.27-.33.693 0 1.24.22 1.64.66.406.44.61 1.04.61 1.8v.37h-3.5c.093.873.593 1.31 1.5 1.31.273 0 .546-.04.82-.12.273-.087.523-.22.75-.4l.35.84a2.625 2.625 0 0 1-.88.46 3.46 3.46 0 0 1-1.06.17Zm-.18-4.24c-.367 0-.664.113-.89.34-.227.227-.364.533-.41.92h2.46c-.027-.407-.14-.717-.34-.93-.194-.22-.467-.33-.82-.33Zm3.551 5.94.9-2-2.03-4.69h1.33l1.37 3.42 1.4-3.42h1.26l-2.94 6.69h-1.29Zm7.609-1.09-.94-.28 2.59-8.03.94.28-2.59 8.03Zm5.861-.71v-7.05h1.28V79h-1.28Zm2.812 0v-7.05h2.62c1.18 0 2.09.303 2.73.91.647.607.97 1.477.97 2.61 0 1.127-.323 1.997-.97 2.61-.64.613-1.55.92-2.73.92h-2.62Zm1.28-1.06h1.26c1.627 0 2.44-.823 2.44-2.47 0-1.64-.813-2.46-2.44-2.46h-1.26v4.93Z"
|
||||
fill="#8B8FA2"
|
||||
/>
|
||||
<defs>
|
||||
<filter
|
||||
id="a"
|
||||
x="20"
|
||||
y="60"
|
||||
width="281"
|
||||
height="87"
|
||||
filterUnits="userSpaceOnUse"
|
||||
colorInterpolationFilters="sRGB"
|
||||
>
|
||||
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
||||
<feColorMatrix
|
||||
in="SourceAlpha"
|
||||
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
||||
result="hardAlpha"
|
||||
/>
|
||||
<feMorphology
|
||||
radius="5"
|
||||
in="SourceAlpha"
|
||||
result="effect1_dropShadow_2250_4620"
|
||||
/>
|
||||
<feOffset dy="7" />
|
||||
<feGaussianBlur stdDeviation="1.5" />
|
||||
<feColorMatrix values="0 0 0 0 0.0980392 0 0 0 0 0.129412 0 0 0 0 0.278431 0 0 0 0.1 0" />
|
||||
<feBlend
|
||||
in2="BackgroundImageFix"
|
||||
result="effect1_dropShadow_2250_4620"
|
||||
/>
|
||||
<feBlend
|
||||
in="SourceGraphic"
|
||||
in2="effect1_dropShadow_2250_4620"
|
||||
result="shape"
|
||||
/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmptyIntegrations;
|
||||
78
frontend/components/icons/EmptyMembers.tsx
Normal file
78
frontend/components/icons/EmptyMembers.tsx
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import React from "react";
|
||||
|
||||
const EmptyMembers = () => {
|
||||
return (
|
||||
<svg
|
||||
width="322"
|
||||
height="176"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 322 176"
|
||||
>
|
||||
<circle cx="162.5" cy="101" r="75" fill="#F1F0FF" />
|
||||
<g filter="url(#a)">
|
||||
<rect x="1" y="61" width="320" height="80" rx="8" fill="#fff" />
|
||||
<rect
|
||||
x=".5"
|
||||
y="60.5"
|
||||
width="321"
|
||||
height="81"
|
||||
rx="8.5"
|
||||
stroke="#C5C7D1"
|
||||
/>
|
||||
</g>
|
||||
<rect x="17" y="103" width="94" height="6" rx="3" fill="#C5C7D1" />
|
||||
<rect x="17" y="123" width="106" height="6" rx="3" fill="#E2E4EA" />
|
||||
<rect x="223" y="103" width="67" height="6" rx="3" fill="#C5C7D1" />
|
||||
<rect x="223" y="123" width="85" height="6" rx="3" fill="#E2E4EA" />
|
||||
<rect x="149" y="103" width="58" height="6" rx="3" fill="#D9D9FE" />
|
||||
<rect x="149" y="123" width="46" height="6" rx="3" fill="#F1F0FF" />
|
||||
<path
|
||||
d="M321 91.5h.5V69a8.5 8.5 0 0 0-8.5-8.5H9A8.5 8.5 0 0 0 .5 69v22.5H321Z"
|
||||
fill="#F9FAFC"
|
||||
stroke="#C5C7D1"
|
||||
/>
|
||||
<path
|
||||
d="M17.77 79v-7.05h.96l3.79 4.92v-4.92h1.19V79h-.95l-3.8-4.94V79h-1.19Zm8.97.1c-.353 0-.67-.067-.95-.2a1.722 1.722 0 0 1-.65-.56 1.406 1.406 0 0 1-.23-.79c0-.36.094-.643.28-.85.187-.213.49-.367.91-.46.42-.093.984-.14 1.69-.14h.35v-.21c0-.333-.073-.573-.22-.72-.146-.147-.393-.22-.74-.22-.273 0-.553.043-.84.13-.286.08-.576.207-.87.38l-.36-.85c.174-.12.377-.223.61-.31.24-.093.49-.163.75-.21.267-.053.517-.08.75-.08.714 0 1.244.167 1.59.5.347.327.52.837.52 1.53V79h-1.17v-.78c-.113.273-.293.49-.54.65-.246.153-.54.23-.88.23Zm.26-.86c.327 0 .597-.113.81-.34.22-.227.33-.513.33-.86v-.22h-.34c-.626 0-1.063.05-1.31.15-.24.093-.36.267-.36.52 0 .22.077.4.23.54.154.14.367.21.64.21Zm3.62.76v-4.89h1.22v.75a1.56 1.56 0 0 1 .61-.62c.26-.153.56-.23.9-.23.733 0 1.213.32 1.44.96.153-.3.373-.533.66-.7.286-.173.613-.26.98-.26 1.1 0 1.65.67 1.65 2.01V79h-1.25v-2.93c0-.373-.064-.647-.19-.82-.12-.173-.327-.26-.62-.26-.327 0-.584.117-.77.35-.187.227-.28.543-.28.95V79h-1.25v-2.93c0-.373-.064-.647-.19-.82-.12-.173-.324-.26-.61-.26-.327 0-.584.117-.77.35-.187.227-.28.543-.28.95V79h-1.25Zm11.159.1c-.554 0-1.03-.103-1.43-.31-.4-.207-.71-.5-.93-.88-.214-.38-.32-.83-.32-1.35 0-.507.103-.95.31-1.33.213-.38.503-.677.87-.89.373-.22.796-.33 1.27-.33.693 0 1.24.22 1.64.66.406.44.61 1.04.61 1.8v.37h-3.5c.093.873.593 1.31 1.5 1.31.273 0 .546-.04.82-.12a2.33 2.33 0 0 0 .75-.4l.35.84a2.616 2.616 0 0 1-.88.46c-.354.113-.707.17-1.06.17Zm-.18-4.24c-.367 0-.664.113-.89.34-.227.227-.364.533-.41.92h2.46c-.027-.407-.14-.717-.34-.93-.194-.22-.467-.33-.82-.33ZM149.77 79v-7.05h3.06c.773 0 1.37.187 1.79.56.42.367.63.883.63 1.55 0 .527-.137.963-.41 1.31-.273.34-.667.57-1.18.69.34.107.62.363.84.77l1.18 2.17h-1.41l-1.22-2.25c-.12-.22-.263-.37-.43-.45-.16-.08-.367-.12-.62-.12h-.95V79h-1.28Zm1.28-3.77h1.56c.933 0 1.4-.38 1.4-1.14 0-.753-.467-1.13-1.4-1.13h-1.56v2.27Zm7.559 3.87c-.507 0-.947-.103-1.32-.31a2.16 2.16 0 0 1-.87-.88c-.207-.387-.31-.84-.31-1.36 0-.52.103-.97.31-1.35a2.16 2.16 0 0 1 .87-.88c.373-.207.813-.31 1.32-.31.506 0 .946.103 1.32.31.373.207.663.5.87.88.206.38.31.83.31 1.35 0 .52-.104.973-.31 1.36-.207.38-.497.673-.87.88-.374.207-.814.31-1.32.31Zm0-.95c.373 0 .673-.133.9-.4.226-.273.34-.673.34-1.2 0-.533-.114-.93-.34-1.19-.227-.267-.527-.4-.9-.4-.374 0-.674.133-.9.4-.227.26-.34.657-.34 1.19 0 .527.113.927.34 1.2.226.267.526.4.9.4Zm3.531.85v-7.05h1.25V79h-1.25Zm4.949.1c-.553 0-1.03-.103-1.43-.31-.4-.207-.71-.5-.93-.88-.213-.38-.32-.83-.32-1.35 0-.507.104-.95.31-1.33.214-.38.504-.677.87-.89.374-.22.797-.33 1.27-.33.694 0 1.24.22 1.64.66.407.44.61 1.04.61 1.8v.37h-3.5c.094.873.594 1.31 1.5 1.31.274 0 .547-.04.82-.12.274-.087.524-.22.75-.4l.35.84a2.606 2.606 0 0 1-.88.46 3.45 3.45 0 0 1-1.06.17Zm-.18-4.24c-.366 0-.663.113-.89.34-.226.227-.363.533-.41.92h2.46c-.026-.407-.14-.717-.34-.93-.193-.22-.466-.33-.82-.33ZM223.77 79v-7.05h4.69v1.02H225v1.94h3.24v1.02H225v2.05h3.46V79h-4.69Zm5.867 0v-4.89h1.22v.75c.146-.267.35-.473.61-.62.26-.153.56-.23.9-.23.733 0 1.213.32 1.44.96.153-.3.373-.533.66-.7.286-.173.613-.26.98-.26 1.1 0 1.65.67 1.65 2.01V79h-1.25v-2.93c0-.373-.064-.647-.19-.82-.12-.173-.327-.26-.62-.26-.327 0-.584.117-.77.35-.187.227-.28.543-.28.95V79h-1.25v-2.93c0-.373-.064-.647-.19-.82-.12-.173-.324-.26-.61-.26-.327 0-.584.117-.77.35-.187.227-.28.543-.28.95V79h-1.25Zm10.359.1c-.353 0-.67-.067-.95-.2a1.725 1.725 0 0 1-.65-.56 1.41 1.41 0 0 1-.23-.79c0-.36.094-.643.28-.85.187-.213.49-.367.91-.46.42-.093.984-.14 1.69-.14h.35v-.21c0-.333-.073-.573-.22-.72-.146-.147-.393-.22-.74-.22-.273 0-.553.043-.84.13-.286.08-.576.207-.87.38l-.36-.85c.174-.12.377-.223.61-.31.24-.093.49-.163.75-.21.267-.053.517-.08.75-.08.714 0 1.244.167 1.59.5.347.327.52.837.52 1.53V79h-1.17v-.78a1.39 1.39 0 0 1-.54.65c-.246.153-.54.23-.88.23Zm.26-.86c.327 0 .597-.113.81-.34.22-.227.33-.513.33-.86v-.22h-.34c-.626 0-1.063.05-1.31.15-.24.093-.36.267-.36.52 0 .22.077.4.23.54.154.14.367.21.64.21Zm3.519-5.18v-1.22h1.4v1.22h-1.4Zm.08 5.94v-4.89h1.25V79h-1.25Zm2.549 0v-7.05h1.25V79h-1.25Z"
|
||||
fill="#8B8FA2"
|
||||
/>
|
||||
<defs>
|
||||
<filter
|
||||
id="a"
|
||||
x="0"
|
||||
y="60"
|
||||
width="322"
|
||||
height="87"
|
||||
filterUnits="userSpaceOnUse"
|
||||
colorInterpolationFilters="sRGB"
|
||||
>
|
||||
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
||||
<feColorMatrix
|
||||
in="SourceAlpha"
|
||||
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
||||
result="hardAlpha"
|
||||
/>
|
||||
<feMorphology
|
||||
radius="5"
|
||||
in="SourceAlpha"
|
||||
result="effect1_dropShadow_2261_4585"
|
||||
/>
|
||||
<feOffset dy="7" />
|
||||
<feGaussianBlur stdDeviation="1.5" />
|
||||
<feColorMatrix values="0 0 0 0 0.0980392 0 0 0 0 0.129412 0 0 0 0 0.278431 0 0 0 0.1 0" />
|
||||
<feBlend
|
||||
in2="BackgroundImageFix"
|
||||
result="effect1_dropShadow_2261_4585"
|
||||
/>
|
||||
<feBlend
|
||||
in="SourceGraphic"
|
||||
in2="effect1_dropShadow_2261_4585"
|
||||
result="shape"
|
||||
/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmptyMembers;
|
||||
115
frontend/components/icons/EmptyPacks.tsx
Normal file
115
frontend/components/icons/EmptyPacks.tsx
Normal file
File diff suppressed because one or more lines are too long
122
frontend/components/icons/EmptyPolicies.tsx
Normal file
122
frontend/components/icons/EmptyPolicies.tsx
Normal file
File diff suppressed because one or more lines are too long
115
frontend/components/icons/EmptyQueries.tsx
Normal file
115
frontend/components/icons/EmptyQueries.tsx
Normal file
File diff suppressed because one or more lines are too long
115
frontend/components/icons/EmptySchedule.tsx
Normal file
115
frontend/components/icons/EmptySchedule.tsx
Normal file
File diff suppressed because one or more lines are too long
143
frontend/components/icons/EmptySoftware.tsx
Normal file
143
frontend/components/icons/EmptySoftware.tsx
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
import React from "react";
|
||||
|
||||
const EmptySoftware = () => {
|
||||
return (
|
||||
<svg
|
||||
width="101"
|
||||
height="71"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 101 71"
|
||||
>
|
||||
<g clipPath="url(#a)">
|
||||
<path
|
||||
d="m16.438 54.738 26.923 8.726.111-31.327-8.9 9.518-18.134-5.666v18.749Z"
|
||||
fill="#E3E3FE"
|
||||
/>
|
||||
<path
|
||||
d="m85.303 54.774-41.942 8.69.111-31.327 9.902 10.65 32.04-4.758-.11 16.745Z"
|
||||
fill="#F8F8FF"
|
||||
/>
|
||||
<path
|
||||
d="m43.64 37.405 8.76 9.518 32.792-5.268.223-3.626-32.041 4.759-9.902-10.651.168 5.268Z"
|
||||
fill="#E3E3FE"
|
||||
/>
|
||||
<path
|
||||
d="m16.522 39.445 19.19 6.288 7.928-8.328-.168-5.268-8.9 9.518-18.134-5.666.084 3.456Z"
|
||||
fill="#CFCFF3"
|
||||
/>
|
||||
<path
|
||||
d="m8.484 33.326 26.088 8.329 8.9-9.518-27.257-7.988-7.73 9.177Z"
|
||||
fill="#F8F8FF"
|
||||
/>
|
||||
<path
|
||||
d="m85.692 26.613 6.509-6.883-14.852-4.25-1.419 8.414 9.762 2.719Z"
|
||||
fill="#E3E3FE"
|
||||
/>
|
||||
<path
|
||||
d="m22.696 25.933 13.657 4.107s2.28-5.438 6.452-6.855c4.171-1.417 5.173-.991 5.173-.991l-1.78-9.66s-1.724-4.702-5.897-3.965l-15.408 2.72s-4.283 1.812-3.615 5.976a1690.24 1690.24 0 0 0 1.418 8.668Z"
|
||||
fill="#9C9EDB"
|
||||
/>
|
||||
<path
|
||||
d="m7.677 16.245 8.538 7.903 5.98-.766-.918-6.119s-.084-2.633.334-3.144L7.677 16.246Z"
|
||||
fill="#E3E3FE"
|
||||
/>
|
||||
<path
|
||||
d="m28.557 19.554 8.949-1.602a2.213 2.213 0 0 1 1.678.38c.487.348.82.88.922 1.477l.84 4.867c.047.272-.014.551-.169.777a1.02 1.02 0 0 1-.66.427l-8.26 1.48a2.903 2.903 0 0 1-2.2-.499 3.006 3.006 0 0 1-1.21-1.937l-.72-4.17c-.045-.27.016-.55.172-.774a1.02 1.02 0 0 1 .658-.426ZM41.888 23.89l1.4.85c.378.229.817-.115.74-.577l-1.021-5.938c-.081-.463-.613-.633-.89-.286l-1.03 1.286a1.512 1.512 0 0 0-.297 1.208l.419 2.424a1.518 1.518 0 0 0 .679 1.033Z"
|
||||
fill="#F8F8FF"
|
||||
/>
|
||||
<path
|
||||
d="m47.645 20.155 4.338-.51 1.168-6.204-2.753-3.144-4.589.68.417 2.72 1.419 6.458Z"
|
||||
fill="#E3E3FE"
|
||||
/>
|
||||
<path
|
||||
d="m16.856 24.063 5.34-.679.5 2.549-5.84-1.87ZM47.645 20.155l.333 2.039 3.588.595.417-3.145-4.338.511ZM75.93 23.894l-.75 3.993 9.595-1.358-8.844-2.635Z"
|
||||
fill="#9C9EDB"
|
||||
/>
|
||||
<path
|
||||
d="m50.203 31.344 3.337-19.21s.445-1.813 2.615-1.643c2.17.17 18.624 1.532 18.624 1.532s1.78 1.078 1.447 3.174C75.892 17.293 74 28 74 28l-23.797 3.344Z"
|
||||
fill="#F8F8FF"
|
||||
/>
|
||||
<path
|
||||
d="M57.883 25.65c-.102.909-.892 1.472-1.776 1.263-.884-.209-1.518-1.1-1.42-1.995.1-.895.893-1.463 1.777-1.267l1.598.355-.179 1.644ZM58.68 25.833a1.48 1.48 0 0 1 .596-1.063 1.421 1.421 0 0 1 1.182-.228c.436.11.82.375 1.08.748.26.373.38.83.338 1.286l-.45 4.176c-.101.922-.893 1.482-1.775 1.248-.882-.234-1.517-1.144-1.418-2.055l.448-4.112Z"
|
||||
fill="#9C9EDB"
|
||||
/>
|
||||
<path
|
||||
d="M60.999 19.516a1.808 1.808 0 0 1-1.08-.713 1.868 1.868 0 0 1-.341-1.263 1.545 1.545 0 0 1 .592-1.084 1.486 1.486 0 0 1 1.187-.277c.885.167 1.52 1.053 1.42 1.984l-.18 1.681L61 19.516ZM60.907 20.366c.436.101.821.361 1.083.731.261.37.381.824.337 1.278-.102.927-.894 1.518-1.778 1.322l-4.008-.88c-.884-.192-1.52-1.071-1.421-1.975a1.506 1.506 0 0 1 .6-1.059 1.446 1.446 0 0 1 1.177-.242l4.01.825Z"
|
||||
fill="#C3C3EE"
|
||||
/>
|
||||
<path
|
||||
d="M67.135 23.415c.102-.948.895-1.56 1.779-1.376.884.184 1.518 1.116 1.418 2.08-.1.963-.896 1.573-1.78 1.372l-1.598-.362.18-1.714ZM66.335 23.241c-.102.944-.895 1.546-1.778 1.352-.883-.195-1.518-1.109-1.42-2.04l.453-4.227c.102-.936.895-1.56 1.78-1.386.883.174 1.518 1.071 1.418 2.018l-.453 4.283Z"
|
||||
fill="#9C9EDB"
|
||||
/>
|
||||
<path
|
||||
d="M64.015 29.674c.883.217 1.516 1.154 1.416 2.093-.1.939-.894 1.51-1.777 1.279a1.959 1.959 0 0 1-1.077-.786 2.02 2.02 0 0 1-.34-1.304l.18-1.675 1.598.393ZM64.105 28.83a1.917 1.917 0 0 1-1.08-.769 1.98 1.98 0 0 1-.338-1.296c.102-.929.894-1.517 1.778-1.317l4.007.918c.884.208 1.517 1.144 1.416 2.103-.101.958-.895 1.56-1.779 1.338l-4.004-.977Z"
|
||||
fill="#C3C3EE"
|
||||
/>
|
||||
<path
|
||||
d="m53.374 42.788 42.165-6.403-9.847-9.772-42.22 5.524 9.902 10.651Z"
|
||||
fill="#fff"
|
||||
/>
|
||||
<path
|
||||
d="m51.566 22.789-1.363 8.555-6.73.792-7.12-2.096s1.89-4.853 6.267-6.55c4.377-1.698 6.601-1.171 6.601-1.171l2.345.47Z"
|
||||
fill="#fff"
|
||||
/>
|
||||
<path
|
||||
d="m49.22 22.317-.825 9.14 1.808-.114 1.363-8.554-2.345-.473Z"
|
||||
fill="#E3E3FE"
|
||||
/>
|
||||
<path d="M76.5 14.5 74 28l3-.5L79 15l-.5-2-2-1-1 .5 1 2Z" fill="#fff" />
|
||||
<path
|
||||
d="m16.234 24.215 27.304 7.922 41.82-5.608M85.359 37.877v16.841L43.436 63.52l-26.998-8.838V35.933"
|
||||
stroke="#9C9EDB"
|
||||
strokeWidth=".789"
|
||||
strokeMiterlimit="10"
|
||||
/>
|
||||
<path
|
||||
d="m43.658 32.438 9.577 10.236 42.304-6.289-9.986-9.914-8.053-2.21M47.413 20.225l4.693-.596"
|
||||
stroke="#9C9EDB"
|
||||
strokeWidth=".789"
|
||||
strokeMiterlimit="10"
|
||||
/>
|
||||
<path
|
||||
d="M43.472 63.56V32.124l-8.844 9.53-26.2-8.328 7.776-9.139 6.124-.778"
|
||||
stroke="#9C9EDB"
|
||||
strokeWidth=".789"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M21.66 14.238 7.613 16.16l8.591 8.027M53.23 13.647l-2.982-3.322-4.784.655M85.553 26.471l6.731-6.656L79 15.991M36.107 30.054a13.034 13.034 0 0 1 6.424-6.62 12.693 12.693 0 0 1 9.118-.63"
|
||||
stroke="#9C9EDB"
|
||||
strokeWidth=".789"
|
||||
strokeMiterlimit="10"
|
||||
/>
|
||||
<path
|
||||
d="m50.382 31.24 2.974-18.69c.098-.621.423-1.18.91-1.568a2.42 2.42 0 0 1 1.712-.52l18.136 1.455c.336.029.664.128.96.291.298.164.559.388.767.659a2.539 2.539 0 0 1 .489 1.936L74.21 28.11"
|
||||
stroke="#9C9EDB"
|
||||
strokeWidth=".789"
|
||||
strokeMiterlimit="10"
|
||||
/>
|
||||
<path
|
||||
d="m77.016 27.89 2.049-12.777c.227-1.435-.76-2.835-2.183-2.957L73.8 11.9M22.709 26.144l-1.465-8.838a5.474 5.474 0 0 1 .06-2.097 5.426 5.426 0 0 1 .843-1.915 5.32 5.32 0 0 1 1.498-1.44 5.223 5.223 0 0 1 1.926-.746l14.599-2.51a5.185 5.185 0 0 1 2.06.06c.675.159 1.314.45 1.879.86.565.409 1.045.927 1.414 1.526.368.598.617 1.264.732 1.96l1.54 9.288"
|
||||
stroke="#9C9EDB"
|
||||
strokeWidth=".789"
|
||||
strokeMiterlimit="10"
|
||||
/>
|
||||
<path
|
||||
d="M1.675 54.645H16.29M85.395 54.645h14.615"
|
||||
stroke="#9C9EDB"
|
||||
strokeWidth=".789"
|
||||
strokeMiterlimit="10"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="a">
|
||||
<path fill="#fff" d="M.5 0h100v71H.5z" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmptySoftware;
|
||||
106
frontend/components/icons/EmptyTeams.tsx
Normal file
106
frontend/components/icons/EmptyTeams.tsx
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -3,6 +3,15 @@ import CalendarCheck from "./CalendarCheck";
|
|||
import Check from "./Check";
|
||||
import Chevron from "./Chevron";
|
||||
import Ex from "./Ex";
|
||||
import EmptyHosts from "./EmptyHosts";
|
||||
import EmptyIntegrations from "./EmptyIntegrations";
|
||||
import EmptyMembers from "./EmptyMembers";
|
||||
import EmptyPacks from "./EmptyPacks";
|
||||
import EmptyPolicies from "./EmptyPolicies";
|
||||
import EmptyQueries from "./EmptyQueries";
|
||||
import EmptySchedule from "./EmptySchedule";
|
||||
import EmptySoftware from "./EmptySoftware";
|
||||
import EmptyTeams from "./EmptyTeams";
|
||||
import ExternalLink from "./ExternalLink";
|
||||
import Issue from "./Issue";
|
||||
import Plus from "./Plus";
|
||||
|
|
@ -37,6 +46,15 @@ export const ICON_MAP = {
|
|||
chevron: Chevron,
|
||||
check: Check,
|
||||
ex: Ex,
|
||||
"empty-hosts": EmptyHosts,
|
||||
"empty-integrations": EmptyIntegrations,
|
||||
"empty-members": EmptyMembers,
|
||||
"empty-packs": EmptyPacks,
|
||||
"empty-policies": EmptyPolicies,
|
||||
"empty-queries": EmptyQueries,
|
||||
"empty-schedule": EmptySchedule,
|
||||
"empty-software": EmptySoftware,
|
||||
"empty-teams": EmptyTeams,
|
||||
"external-link": ExternalLink,
|
||||
"low-disk-space-hosts": LowDiskSpaceHosts,
|
||||
"missing-hosts": MissingHosts,
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
import React from "react";
|
||||
|
||||
const baseClass = "empty-pack";
|
||||
|
||||
const EmptyPack = (): JSX.Element => {
|
||||
return (
|
||||
<div className={`${baseClass}`}>
|
||||
<div className={`${baseClass}__inner`}>
|
||||
<div className={`${baseClass}__empty-filter-results`}>
|
||||
<h1>No queries matched your search criteria.</h1>
|
||||
<p>Try a different search.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmptyPack;
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
.empty-pack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: 80px;
|
||||
|
||||
&__inner {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
h1 {
|
||||
font-size: $small;
|
||||
font-weight: $bold;
|
||||
margin-bottom: $pad-medium;
|
||||
}
|
||||
|
||||
p {
|
||||
color: $core-fleet-black;
|
||||
font-weight: $regular;
|
||||
font-size: $x-small;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__empty-filter-results {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 350px;
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
export { default } from "./EmptySearch";
|
||||
|
|
@ -5,7 +5,7 @@ import { IScheduledQuery } from "interfaces/scheduled_query";
|
|||
|
||||
import TableContainer, { ITableQueryData } from "components/TableContainer";
|
||||
import Button from "components/buttons/Button";
|
||||
import EmptySearch from "./EmptySearch";
|
||||
import EmptyTable from "components/EmptyTable";
|
||||
import {
|
||||
generateTableHeaders,
|
||||
generateDataSet,
|
||||
|
|
@ -82,7 +82,12 @@ const PackQueriesTable = ({
|
|||
inputPlaceHolder={"Search queries"}
|
||||
onQueryChange={onTableQueryChange}
|
||||
resultsTitle={"queries"}
|
||||
emptyComponent={EmptySearch}
|
||||
emptyComponent={() =>
|
||||
EmptyTable({
|
||||
header: "No queries match your search criteria.",
|
||||
info: "Try a different search.",
|
||||
})
|
||||
}
|
||||
showMarkAllPages={false}
|
||||
actionButtonText={"Add query"}
|
||||
actionButtonIcon={AddQueryIcon}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
}
|
||||
@media (min-width: $break-990) {
|
||||
.interval__header {
|
||||
border-right: 1px solid $ui-fleet-blue-15;
|
||||
border-right: 1px solid $ui-fleet-black-10;
|
||||
}
|
||||
.performance__header {
|
||||
display: table-cell;
|
||||
|
|
@ -92,9 +92,4 @@
|
|||
color: $core-fleet-black;
|
||||
}
|
||||
}
|
||||
|
||||
&__no-queries {
|
||||
font-size: $x-small;
|
||||
font-weight: $bold;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
font-size: $medium;
|
||||
font-weight: $regular;
|
||||
color: $core-fleet-black;
|
||||
border-bottom: 1px solid $ui-fleet-blue-15;
|
||||
border-bottom: 1px solid $ui-fleet-black-10;
|
||||
padding-bottom: 8px;
|
||||
margin: 0 0 7px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
display: inline-flex;
|
||||
align-items: center;
|
||||
color: $core-fleet-black;
|
||||
background-color: $ui-fleet-blue-15;
|
||||
background-color: $ui-fleet-black-10;
|
||||
padding: $pad-xsmall $pad-small;
|
||||
border-radius: 6px;
|
||||
font-size: $xxx-small;
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@
|
|||
&__table-count {
|
||||
line-height: normal;
|
||||
margin-left: $pad-small;
|
||||
background-color: $ui-fleet-blue-15;
|
||||
background-color: $ui-fleet-black-10;
|
||||
padding: $pad-xsmall $pad-small;
|
||||
border-radius: 8px;
|
||||
font-size: $x-small;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
.dropdown-button__option:last-child,
|
||||
.dropdown-button__option:nth-last-child(2) {
|
||||
border-top: 1px solid $ui-fleet-blue-15;
|
||||
border-top: 1px solid $ui-fleet-black-10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
11
frontend/interfaces/empty_table.ts
Normal file
11
frontend/interfaces/empty_table.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { IconNames } from "components/icons";
|
||||
|
||||
export interface IEmptyTableProps {
|
||||
iconName?: IconNames;
|
||||
header?: JSX.Element | string;
|
||||
info?: JSX.Element | string;
|
||||
additionalInfo?: JSX.Element | string;
|
||||
className?: string;
|
||||
primaryButton?: JSX.Element;
|
||||
secondaryButton?: JSX.Element;
|
||||
}
|
||||
|
|
@ -590,7 +590,7 @@ const DashboardPage = ({
|
|||
{LearnFleetCard}
|
||||
</>
|
||||
)}
|
||||
{SoftwareCard}
|
||||
{!software && SoftwareCard}
|
||||
{!currentTeam && isOnGlobalTeam && <>{ActivityFeedCard}</>}
|
||||
{showMdmCard && <>{MDMCard}</>}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import React from "react";
|
||||
import { find, lowerCase, noop } from "lodash";
|
||||
import { formatDistanceToNowStrict } from "date-fns";
|
||||
import { intlFormat, formatDistanceToNowStrict } from "date-fns";
|
||||
|
||||
import { ActivityType, IActivity, IActivityDetails } from "interfaces/activity";
|
||||
import { addGravatarUrlToResource } from "utilities/helpers";
|
||||
import Avatar from "components/Avatar";
|
||||
import Button from "components/buttons/Button";
|
||||
import Icon from "components/Icon";
|
||||
import ReactTooltip from "react-tooltip";
|
||||
|
||||
const baseClass = "activity-item";
|
||||
|
||||
|
|
@ -237,6 +238,8 @@ const ActivityItem = ({
|
|||
? addGravatarUrlToResource({ email: actor_email })
|
||||
: { gravatarURL: DEFAULT_GRAVATAR_URL };
|
||||
|
||||
const activityCreatedAt = new Date(activity.created_at);
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<Avatar
|
||||
|
|
@ -257,12 +260,34 @@ const ActivityItem = ({
|
|||
<br />
|
||||
<span
|
||||
className={`${baseClass}__details-bottomline`}
|
||||
title={new Date(activity.created_at).toString()}
|
||||
data-tip
|
||||
data-for={`activity-${activity.id}`}
|
||||
>
|
||||
{formatDistanceToNowStrict(new Date(activity.created_at), {
|
||||
{formatDistanceToNowStrict(activityCreatedAt, {
|
||||
addSuffix: true,
|
||||
})}
|
||||
</span>
|
||||
<ReactTooltip
|
||||
className="date-tooltip"
|
||||
place="top"
|
||||
type="dark"
|
||||
effect="solid"
|
||||
id={`activity-${activity.id}`}
|
||||
backgroundColor="#3e4771"
|
||||
>
|
||||
{intlFormat(
|
||||
activityCreatedAt,
|
||||
{
|
||||
year: "numeric",
|
||||
month: "numeric",
|
||||
day: "numeric",
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
second: "numeric",
|
||||
},
|
||||
{ locale: window.navigator.languages[0] }
|
||||
)}
|
||||
</ReactTooltip>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
z-index: 0;
|
||||
top: 35px;
|
||||
left: 17px;
|
||||
border-left: 1px dashed $ui-fleet-blue-15;
|
||||
border-left: 1px dashed $ui-fleet-black-10;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@ import TabsWrapper from "components/TabsWrapper";
|
|||
import TableContainer from "components/TableContainer";
|
||||
import TableDataError from "components/DataError";
|
||||
import Spinner from "components/Spinner";
|
||||
import EmptyTable from "components/EmptyTable";
|
||||
import { IEmptyTableProps } from "interfaces/empty_table";
|
||||
|
||||
import generateTableHeaders from "./SoftwareTableConfig";
|
||||
import EmptySoftware from "../../../software/components/EmptySoftware";
|
||||
|
||||
interface ISoftwareCardProps {
|
||||
errorSoftware: Error | null;
|
||||
|
|
@ -62,6 +63,20 @@ const Software = ({
|
|||
router.push(path);
|
||||
};
|
||||
|
||||
const emptyState = (vuln = false) => {
|
||||
const emptySoftware: IEmptyTableProps = {
|
||||
header: "No software detected",
|
||||
info:
|
||||
"This report is updated every hour to protect the performance of your devices.",
|
||||
};
|
||||
if (vuln) {
|
||||
emptySoftware.header = "No vulnerable software detected";
|
||||
emptySoftware.info =
|
||||
"This report is updated every hour to protect the performance of your devices.";
|
||||
}
|
||||
return emptySoftware;
|
||||
};
|
||||
|
||||
// Renders opaque information as host information is loading
|
||||
const opacity = isSoftwareFetching ? { opacity: 0 } : { opacity: 1 };
|
||||
|
||||
|
|
@ -92,11 +107,10 @@ const Software = ({
|
|||
hideActionButton
|
||||
resultsTitle={"software"}
|
||||
emptyComponent={() =>
|
||||
EmptySoftware(
|
||||
(!isSoftwareEnabled && "disabled") ||
|
||||
(isCollectingInventory && "collecting") ||
|
||||
"default"
|
||||
)
|
||||
EmptyTable({
|
||||
header: emptyState().header,
|
||||
info: emptyState().info,
|
||||
})
|
||||
}
|
||||
showMarkAllPages={false}
|
||||
isAllPagesSelected={false}
|
||||
|
|
@ -122,11 +136,10 @@ const Software = ({
|
|||
hideActionButton
|
||||
resultsTitle={"software"}
|
||||
emptyComponent={() =>
|
||||
EmptySoftware(
|
||||
(!isSoftwareEnabled && "disabled") ||
|
||||
(isCollectingInventory && "collecting") ||
|
||||
"default"
|
||||
)
|
||||
EmptyTable({
|
||||
header: emptyState().header,
|
||||
info: emptyState().info,
|
||||
})
|
||||
}
|
||||
showMarkAllPages={false}
|
||||
isAllPagesSelected={false}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
padding: 32px;
|
||||
width: 100%;
|
||||
background-color: $core-white;
|
||||
border: 1px solid $ui-fleet-blue-15;
|
||||
border: 1px solid $ui-fleet-black-10;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 0 0 $ui-fleet-blue-15;
|
||||
box-shadow: 0 2px 0 0 $ui-fleet-black-10;
|
||||
box-sizing: border-box;
|
||||
|
||||
&__section-title-cta {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { formatDistanceToNow } from "date-fns";
|
||||
|
||||
import { IUser } from "interfaces/user";
|
||||
import { IVersionData } from "interfaces/version";
|
||||
|
|
@ -10,6 +9,7 @@ import versionAPI from "services/entities/version";
|
|||
|
||||
import Avatar from "components/Avatar";
|
||||
import Button from "components/buttons/Button";
|
||||
import HumanTimeDiffWithDateTip from "components/HumanTimeDiffWithDateTip";
|
||||
|
||||
import { generateRole, generateTeam, greyCell } from "utilities/helpers";
|
||||
|
||||
|
|
@ -53,11 +53,9 @@ const UserSidePanel = ({
|
|||
const roleText = generateRole(teams, globalRole);
|
||||
const teamsText = generateTeam(teams, globalRole);
|
||||
|
||||
const lastUpdatedAt =
|
||||
updatedAt &&
|
||||
formatDistanceToNow(new Date(updatedAt), {
|
||||
addSuffix: true,
|
||||
});
|
||||
const lastUpdatedAt = updatedAt && (
|
||||
<HumanTimeDiffWithDateTip timeString={updatedAt} />
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
&__section {
|
||||
@include clearfix;
|
||||
margin: 0 0 $pad-large;
|
||||
animation: fade-in 200ms ease-out;
|
||||
|
||||
.upcaret::after {
|
||||
content: url("../assets/images/icon-collapse-black-16x16@2x.png");
|
||||
|
|
@ -75,7 +76,7 @@
|
|||
font-size: $medium;
|
||||
font-weight: $regular;
|
||||
color: $core-fleet-black;
|
||||
border-bottom: solid 1px $ui-fleet-blue-15;
|
||||
border-bottom: solid 1px $ui-fleet-black-10;
|
||||
margin: 0 0 $pad-xxlarge;
|
||||
|
||||
@media (min-width: $break-990) {
|
||||
|
|
@ -198,7 +199,7 @@
|
|||
border-radius: 20%;
|
||||
height: 120px;
|
||||
width: 120px;
|
||||
border: 1px solid $ui-fleet-blue-15;
|
||||
border: 1px solid $ui-fleet-black-10;
|
||||
background-color: $ui-light-grey;
|
||||
position: relative;
|
||||
bottom: -20px;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {
|
|||
IIntegrations,
|
||||
} from "interfaces/integration";
|
||||
import { IApiError } from "interfaces/errors";
|
||||
import { IEmptyTableProps } from "interfaces/empty_table";
|
||||
|
||||
import Button from "components/buttons/Button";
|
||||
// @ts-ignore
|
||||
|
|
@ -20,6 +21,7 @@ import configAPI from "services/entities/config";
|
|||
|
||||
import TableContainer from "components/TableContainer";
|
||||
import TableDataError from "components/DataError";
|
||||
import EmptyTable from "components/EmptyTable";
|
||||
import CustomLink from "components/CustomLink";
|
||||
|
||||
import AddIntegrationModal from "./components/AddIntegrationModal";
|
||||
|
|
@ -41,7 +43,7 @@ const BAD_REQUEST_ERROR =
|
|||
const UNKNOWN_ERROR =
|
||||
"We experienced an error when attempting to connect. Please try again later.";
|
||||
|
||||
const Integrations = () => {
|
||||
const Integrations = (): JSX.Element => {
|
||||
const { renderFlash } = useContext(NotificationContext);
|
||||
|
||||
const [showAddIntegrationModal, setShowAddIntegrationModal] = useState(false);
|
||||
|
|
@ -361,35 +363,33 @@ const Integrations = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const NoIntegrationsComponent = () => {
|
||||
return (
|
||||
<div className={`${noIntegrationsClass}`}>
|
||||
<div className={`${noIntegrationsClass}__inner`}>
|
||||
<div className={`${noIntegrationsClass}__inner-text`}>
|
||||
<h2>Set up integrations</h2>
|
||||
<p>
|
||||
Create tickets automatically when Fleet detects new
|
||||
vulnerabilities.
|
||||
</p>
|
||||
<p>
|
||||
Want to learn more?
|
||||
<CustomLink
|
||||
url="https://fleetdm.com/docs/using-fleet/automations"
|
||||
text="Read about automations"
|
||||
newTab
|
||||
/>
|
||||
</p>
|
||||
<Button
|
||||
variant="brand"
|
||||
className={`${noIntegrationsClass}__add-button`}
|
||||
onClick={toggleAddIntegrationModal}
|
||||
>
|
||||
Add integration
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
const emptyState = () => {
|
||||
const emptyIntegrations: IEmptyTableProps = {
|
||||
iconName: "empty-integrations",
|
||||
header: "Set up integrations",
|
||||
info:
|
||||
"Create tickets automatically when Fleet detects new software vulnerabilities or hosts failing policies.",
|
||||
additionalInfo: (
|
||||
<>
|
||||
Want to learn more?
|
||||
<CustomLink
|
||||
url="https://fleetdm.com/docs/using-fleet/automations"
|
||||
text="Read about automations"
|
||||
newTab
|
||||
/>
|
||||
</>
|
||||
),
|
||||
primaryButton: (
|
||||
<Button
|
||||
variant="brand"
|
||||
className={`${noIntegrationsClass}__add-button`}
|
||||
onClick={toggleAddIntegrationModal}
|
||||
>
|
||||
Add integration
|
||||
</Button>
|
||||
),
|
||||
};
|
||||
return emptyIntegrations;
|
||||
};
|
||||
|
||||
const tableHeaders = generateTableHeaders(onActionSelection);
|
||||
|
|
@ -399,6 +399,10 @@ const Integrations = () => {
|
|||
return (
|
||||
<div className={`${baseClass}`}>
|
||||
<h2 className={`${baseClass}__title`}>Ticket Destinations</h2>
|
||||
<p className={`${baseClass}__page-description`}>
|
||||
Add or edit integrations to create tickets when Fleet detects new
|
||||
vulnerabilities.
|
||||
</p>
|
||||
{loadingIntegrationsError ? (
|
||||
<TableDataError />
|
||||
) : (
|
||||
|
|
@ -413,7 +417,15 @@ const Integrations = () => {
|
|||
actionButtonVariant={"brand"}
|
||||
onActionButtonClick={toggleAddIntegrationModal}
|
||||
resultsTitle={"integrations"}
|
||||
emptyComponent={NoIntegrationsComponent}
|
||||
emptyComponent={() =>
|
||||
EmptyTable({
|
||||
iconName: emptyState().iconName,
|
||||
header: emptyState().header,
|
||||
info: emptyState().info,
|
||||
additionalInfo: emptyState().additionalInfo,
|
||||
primaryButton: emptyState().primaryButton,
|
||||
})
|
||||
}
|
||||
showMarkAllPages={false}
|
||||
isAllPagesSelected={false}
|
||||
disablePagination
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
font-size: $medium;
|
||||
font-weight: $regular;
|
||||
color: $core-fleet-black;
|
||||
border-bottom: solid 1px $ui-fleet-blue-15;
|
||||
border-bottom: solid 1px $ui-fleet-black-10;
|
||||
margin: 0 0 $pad-xxlarge;
|
||||
|
||||
@media (min-width: $break-990) {
|
||||
|
|
@ -14,6 +14,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
&__page-description {
|
||||
font-size: $x-small;
|
||||
color: $core-fleet-black;
|
||||
width: 100%;
|
||||
|
||||
@media (min-width: $break-990) {
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
|
||||
.table-container {
|
||||
@media (min-width: $break-990) {
|
||||
max-width: 65%;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
font-size: $medium;
|
||||
font-weight: $regular;
|
||||
color: $core-fleet-black;
|
||||
border-bottom: solid 1px $ui-fleet-blue-15;
|
||||
border-bottom: solid 1px $ui-fleet-black-10;
|
||||
margin: 0 0 $pad-xxlarge;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import React, { useCallback, useContext, useState } from "react";
|
||||
import { useQuery } from "react-query";
|
||||
import { IconNames } from "components/icons";
|
||||
|
||||
import { NotificationContext } from "context/notification";
|
||||
import PATHS from "router/paths";
|
||||
import { IApiError } from "interfaces/errors";
|
||||
import { IUser, IUserFormErrors } from "interfaces/user";
|
||||
import { INewMembersBody, ITeam } from "interfaces/team";
|
||||
import { IEmptyTableProps } from "interfaces/empty_table";
|
||||
import { Link } from "react-router";
|
||||
import { AppContext } from "context/app";
|
||||
import usersAPI from "services/entities/users";
|
||||
|
|
@ -15,6 +17,7 @@ import teamsAPI, { ILoadTeamsResponse } from "services/entities/teams";
|
|||
import Button from "components/buttons/Button";
|
||||
import TableContainer from "components/TableContainer";
|
||||
import TableDataError from "components/DataError";
|
||||
import EmptyTable from "components/EmptyTable";
|
||||
import CreateUserModal from "pages/admin/UserManagementPage/components/CreateUserModal";
|
||||
import { DEFAULT_CREATE_USER_ERRORS } from "utilities/constants";
|
||||
import EditUserModal from "../../../UserManagementPage/components/EditUserModal";
|
||||
|
|
@ -339,51 +342,41 @@ const MembersPage = ({
|
|||
}
|
||||
};
|
||||
|
||||
const NoMembersComponent = useCallback(() => {
|
||||
return (
|
||||
<div className={`${noMembersClass}`}>
|
||||
<div className={`${noMembersClass}__inner`}>
|
||||
<div className={`${noMembersClass}__inner-text`}>
|
||||
{searchString === "" ? (
|
||||
<>
|
||||
<h1>This team doesn't have any members yet.</h1>
|
||||
<p>
|
||||
Expecting to see new team members listed here? Try again in a
|
||||
few seconds as the system catches up.
|
||||
</p>
|
||||
{isGlobalAdmin && (
|
||||
<Button
|
||||
variant="brand"
|
||||
className={`${noMembersClass}__create-button`}
|
||||
onClick={toggleAddUserModal}
|
||||
>
|
||||
Add member
|
||||
</Button>
|
||||
)}
|
||||
{isTeamAdmin && (
|
||||
<Button
|
||||
variant="brand"
|
||||
className={`${noMembersClass}__create-button`}
|
||||
onClick={toggleCreateMemberModal}
|
||||
>
|
||||
Create user
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<h2>We couldn’t find any members.</h2>
|
||||
<p>
|
||||
Expecting to see members? Try again in a few seconds as the
|
||||
system catches up.
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}, [searchString, toggleAddUserModal]);
|
||||
const emptyState = () => {
|
||||
const emptyMembers: IEmptyTableProps = {
|
||||
iconName: "empty-members",
|
||||
header: "This team doesn't have any members yet.",
|
||||
info:
|
||||
"Expecting to see new team members listed here? Try again in a few seconds as the system catches up.",
|
||||
};
|
||||
if (searchString !== "") {
|
||||
delete emptyMembers.iconName;
|
||||
emptyMembers.header = "We couldn’t find any members.";
|
||||
emptyMembers.info =
|
||||
"Expecting to see members? Try again in a few seconds as the system catches up.";
|
||||
} else if (isGlobalAdmin) {
|
||||
emptyMembers.primaryButton = (
|
||||
<Button
|
||||
variant="brand"
|
||||
className={`${noMembersClass}__create-button`}
|
||||
onClick={toggleAddUserModal}
|
||||
>
|
||||
Add member
|
||||
</Button>
|
||||
);
|
||||
} else if (isTeamAdmin) {
|
||||
emptyMembers.primaryButton = (
|
||||
<Button
|
||||
variant="brand"
|
||||
className={`${noMembersClass}__create-button`}
|
||||
onClick={toggleCreateMemberModal}
|
||||
>
|
||||
Create user
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
return emptyMembers;
|
||||
};
|
||||
|
||||
const tableHeaders = generateTableHeaders(onActionSelection);
|
||||
|
||||
|
|
@ -417,7 +410,14 @@ const MembersPage = ({
|
|||
hideActionButton={memberIds.length === 0 && searchString === ""}
|
||||
onQueryChange={({ searchQuery }) => setSearchString(searchQuery)}
|
||||
inputPlaceHolder={"Search"}
|
||||
emptyComponent={NoMembersComponent}
|
||||
emptyComponent={() =>
|
||||
EmptyTable({
|
||||
iconName: emptyState().iconName,
|
||||
header: emptyState().header,
|
||||
info: emptyState().info,
|
||||
primaryButton: emptyState().primaryButton,
|
||||
})
|
||||
}
|
||||
showMarkAllPages={false}
|
||||
isAllPagesSelected={false}
|
||||
searchable={memberIds.length > 0 || searchString !== ""}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
@media (min-width: $break-1400) {
|
||||
.role__header {
|
||||
border-right: 1px solid $ui-fleet-blue-15;
|
||||
border-right: 1px solid $ui-fleet-black-10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -45,75 +45,3 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.no-members {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: $pad-xxxlarge;
|
||||
|
||||
h1 {
|
||||
font-size: $large;
|
||||
font-weight: $regular;
|
||||
line-height: normal;
|
||||
letter-spacing: normal;
|
||||
color: $core-fleet-black;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: $small;
|
||||
font-weight: $bold;
|
||||
margin: 0 0 $pad-large;
|
||||
line-height: 20px;
|
||||
color: $core-fleet-black;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: $core-fleet-black;
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
&::before {
|
||||
content: "•";
|
||||
color: $core-vibrant-blue;
|
||||
margin-right: $pad-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__inner {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
h1 {
|
||||
font-size: $small;
|
||||
font-weight: $bold;
|
||||
margin-bottom: $pad-medium;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 176px;
|
||||
margin-right: $pad-xlarge;
|
||||
}
|
||||
|
||||
p {
|
||||
color: $core-fleet-black;
|
||||
font-weight: $regular;
|
||||
font-size: $x-small;
|
||||
margin: 0;
|
||||
margin-bottom: $pad-large;
|
||||
}
|
||||
|
||||
.no-filter-results {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 350px;
|
||||
}
|
||||
}
|
||||
|
||||
&__inner-text {
|
||||
width: 350px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
.autocomplete-dropdown {
|
||||
.Select {
|
||||
border: solid 1px $ui-fleet-blue-15;
|
||||
border: solid 1px $ui-fleet-black-10;
|
||||
border-radius: 4px;
|
||||
|
||||
&:hover,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React, { useState, useCallback, useContext } from "react";
|
||||
import { IconNames } from "components/icons";
|
||||
import { useQuery } from "react-query";
|
||||
import { useErrorHandler } from "react-error-boundary";
|
||||
|
||||
|
|
@ -6,6 +7,7 @@ import { NotificationContext } from "context/notification";
|
|||
import { AppContext } from "context/app";
|
||||
import { ITeam } from "interfaces/team";
|
||||
import { IApiError } from "interfaces/errors";
|
||||
import { IEmptyTableProps } from "interfaces/empty_table";
|
||||
import teamsAPI, {
|
||||
ILoadTeamsResponse,
|
||||
ITeamFormData,
|
||||
|
|
@ -14,6 +16,7 @@ import teamsAPI, {
|
|||
import Button from "components/buttons/Button";
|
||||
import TableContainer from "components/TableContainer";
|
||||
import TableDataError from "components/DataError";
|
||||
import EmptyTable from "components/EmptyTable";
|
||||
import CustomLink from "components/CustomLink";
|
||||
|
||||
import CreateTeamModal from "./components/CreateTeamModal";
|
||||
|
|
@ -197,35 +200,35 @@ const TeamManagementPage = (): JSX.Element => {
|
|||
}
|
||||
};
|
||||
|
||||
const NoTeamsComponent = () => {
|
||||
return (
|
||||
<div className={`${noTeamsClass}`}>
|
||||
<div className={`${noTeamsClass}__inner`}>
|
||||
<div className={`${noTeamsClass}__inner-text`}>
|
||||
<h1>Set up team permissions</h1>
|
||||
<p>
|
||||
Keep your organization organized and efficient by ensuring every
|
||||
user has the correct access to the right hosts.
|
||||
</p>
|
||||
<p>
|
||||
Want to learn more?
|
||||
<CustomLink
|
||||
url="https://fleetdm.com/docs/using-fleet/teams"
|
||||
text="Read about teams"
|
||||
newTab
|
||||
/>
|
||||
</p>
|
||||
<Button
|
||||
variant="brand"
|
||||
className={`${noTeamsClass}__create-button`}
|
||||
onClick={toggleCreateTeamModal}
|
||||
>
|
||||
Create team
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
const emptyState = () => {
|
||||
const emptyTeams: IEmptyTableProps = {
|
||||
iconName: "empty-teams",
|
||||
header: "Set up team permissions",
|
||||
info:
|
||||
"Keep your organization organized and efficient by ensuring every user has the correct access to the right hosts.",
|
||||
additionalInfo: (
|
||||
<>
|
||||
{" "}
|
||||
Want to learn more?
|
||||
<CustomLink
|
||||
url="https://fleetdm.com/docs/using-fleet/teams"
|
||||
text="Read about teams"
|
||||
newTab
|
||||
/>
|
||||
</>
|
||||
),
|
||||
primaryButton: (
|
||||
<Button
|
||||
variant="brand"
|
||||
className={`${noTeamsClass}__create-button`}
|
||||
onClick={toggleCreateTeamModal}
|
||||
>
|
||||
Create team
|
||||
</Button>
|
||||
),
|
||||
};
|
||||
|
||||
return emptyTeams;
|
||||
};
|
||||
|
||||
const tableHeaders = generateTableHeaders(onActionSelection);
|
||||
|
|
@ -252,7 +255,15 @@ const TeamManagementPage = (): JSX.Element => {
|
|||
onActionButtonClick={toggleCreateTeamModal}
|
||||
onQueryChange={onQueryChange}
|
||||
resultsTitle={"teams"}
|
||||
emptyComponent={NoTeamsComponent}
|
||||
emptyComponent={() =>
|
||||
EmptyTable({
|
||||
iconName: "empty-teams",
|
||||
header: emptyState().header,
|
||||
info: emptyState().info,
|
||||
additionalInfo: emptyState().additionalInfo,
|
||||
primaryButton: emptyState().primaryButton,
|
||||
})
|
||||
}
|
||||
showMarkAllPages={false}
|
||||
isAllPagesSelected={false}
|
||||
searchable={teams && teams.length > 0 && searchString !== ""}
|
||||
|
|
|
|||
|
|
@ -38,75 +38,3 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.no-teams {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: $pad-xxxlarge;
|
||||
|
||||
h1 {
|
||||
font-size: $large;
|
||||
font-weight: $regular;
|
||||
line-height: normal;
|
||||
letter-spacing: normal;
|
||||
color: $core-fleet-black;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: $small;
|
||||
font-weight: $bold;
|
||||
margin: 0 0 $pad-large;
|
||||
line-height: 20px;
|
||||
color: $core-fleet-black;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: $core-fleet-black;
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
&::before {
|
||||
content: "•";
|
||||
color: $core-vibrant-blue;
|
||||
margin-right: $pad-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__inner {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
h1 {
|
||||
font-size: $small;
|
||||
font-weight: $bold;
|
||||
margin-bottom: $pad-medium;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 176px;
|
||||
margin-right: $pad-xlarge;
|
||||
}
|
||||
|
||||
p {
|
||||
color: $core-fleet-black;
|
||||
font-weight: $regular;
|
||||
font-size: $x-small;
|
||||
margin: 0;
|
||||
margin-bottom: $pad-large;
|
||||
}
|
||||
|
||||
.no-filter-results {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 350px;
|
||||
}
|
||||
}
|
||||
|
||||
&__inner-text {
|
||||
width: 350px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
font-size: $x-small;
|
||||
color: $core-fleet-black;
|
||||
@include sticky-settings-description;
|
||||
padding-bottom: $pad-medium;
|
||||
}
|
||||
|
||||
&__sandbox-demo-message {
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
/**
|
||||
* Component when there is no host results found in a search
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
const baseClass = "empty-users";
|
||||
|
||||
const EmptyUsers = (): JSX.Element => {
|
||||
return (
|
||||
<div className={`${baseClass}`}>
|
||||
<div className={`${baseClass}__inner`}>
|
||||
<div className={`${baseClass}__empty-filter-results`}>
|
||||
<h1>No users match the current criteria.</h1>
|
||||
<p>
|
||||
Expecting to see users? Try again in a few seconds as the system
|
||||
catches up.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmptyUsers;
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
.empty-users {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: $pad-xxxlarge;
|
||||
|
||||
&__inner {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
h1 {
|
||||
font-size: $small;
|
||||
font-weight: $bold;
|
||||
margin-bottom: $pad-medium;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 176px;
|
||||
margin-right: $pad-xlarge;
|
||||
}
|
||||
|
||||
p {
|
||||
color: $core-fleet-black;
|
||||
font-weight: $regular;
|
||||
font-size: $x-small;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__empty-filter-results {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 350px;
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
export { default } from "./EmptyUsers";
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
.selected-teams-form {
|
||||
border: 1px solid $ui-fleet-blue-15;
|
||||
border: 1px solid $ui-fleet-black-10;
|
||||
border-radius: $border-radius;
|
||||
background-color: $ui-off-white;
|
||||
padding: $pad-medium;
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@ import teamsAPI, { ILoadTeamsResponse } from "services/entities/teams";
|
|||
import usersAPI from "services/entities/users";
|
||||
import invitesAPI from "services/entities/invites";
|
||||
|
||||
import { DEFAULT_CREATE_USER_ERRORS } from "utilities/constants";
|
||||
import TableContainer, { ITableQueryData } from "components/TableContainer";
|
||||
import TableDataError from "components/DataError";
|
||||
import Modal from "components/Modal";
|
||||
import { DEFAULT_CREATE_USER_ERRORS } from "utilities/constants";
|
||||
import EmptyUsers from "../EmptyUsers";
|
||||
import EmptyTable from "components/EmptyTable";
|
||||
import { generateTableHeaders, combineDataSets } from "./UsersTableConfig";
|
||||
import DeleteUserModal from "../DeleteUserModal";
|
||||
import ResetPasswordModal from "../ResetPasswordModal";
|
||||
|
|
@ -520,6 +520,12 @@ const UsersTable = ({ router }: IUsersTableProps): JSX.Element => {
|
|||
tableData = combineUsersAndInvites(users, invites, currentUser?.id);
|
||||
}
|
||||
|
||||
const emptyState = {
|
||||
header: "No users match the current criteria.",
|
||||
info:
|
||||
"Expecting to see users? Try again in a few seconds as the system catches up.",
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* TODO: find a way to move these controls into the table component */}
|
||||
|
|
@ -537,7 +543,7 @@ const UsersTable = ({ router }: IUsersTableProps): JSX.Element => {
|
|||
onActionButtonClick={toggleCreateUserModal}
|
||||
onQueryChange={onTableQueryChange}
|
||||
resultsTitle={"users"}
|
||||
emptyComponent={EmptyUsers}
|
||||
emptyComponent={() => EmptyTable(emptyState)}
|
||||
searchable
|
||||
showMarkAllPages={false}
|
||||
isAllPagesSelected={false}
|
||||
|
|
|
|||
|
|
@ -14,11 +14,11 @@ import LinkCell from "components/TableContainer/DataTable/LinkCell/LinkCell";
|
|||
import StatusIndicator from "components/StatusIndicator";
|
||||
import TextCell from "components/TableContainer/DataTable/TextCell/TextCell";
|
||||
import TooltipWrapper from "components/TooltipWrapper";
|
||||
import HumanTimeDiffWithDateTip from "components/HumanTimeDiffWithDateTip";
|
||||
import {
|
||||
humanHostMemory,
|
||||
humanHostLastRestart,
|
||||
humanHostLastSeen,
|
||||
humanHostDetailUpdated,
|
||||
hostTeamName,
|
||||
} from "utilities/helpers";
|
||||
import { IConfig } from "interfaces/config";
|
||||
|
|
@ -361,8 +361,8 @@ const allHostTableHeaders: IDataColumn[] = [
|
|||
accessor: "detail_updated_at",
|
||||
Cell: (cellProps: ICellProps) => (
|
||||
<TextCell
|
||||
value={cellProps.cell.value}
|
||||
formatter={humanHostDetailUpdated}
|
||||
value={{ timeString: cellProps.cell.value }}
|
||||
formatter={HumanTimeDiffWithDateTip}
|
||||
/>
|
||||
),
|
||||
},
|
||||
|
|
@ -387,7 +387,10 @@ const allHostTableHeaders: IDataColumn[] = [
|
|||
},
|
||||
accessor: "seen_time",
|
||||
Cell: (cellProps: ICellProps) => (
|
||||
<TextCell value={cellProps.cell.value} formatter={humanHostLastSeen} />
|
||||
<TextCell
|
||||
value={{ timeString: cellProps.cell.value }}
|
||||
formatter={HumanTimeDiffWithDateTip}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
|
|
@ -414,7 +417,12 @@ const allHostTableHeaders: IDataColumn[] = [
|
|||
const { uptime, detail_updated_at } = cellProps.row.original;
|
||||
|
||||
return (
|
||||
<TextCell value={humanHostLastRestart(detail_updated_at, uptime)} />
|
||||
<TextCell
|
||||
value={{
|
||||
timeString: humanHostLastRestart(detail_updated_at, uptime),
|
||||
}}
|
||||
formatter={HumanTimeDiffWithDateTip}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React, { useState, useContext, useEffect, useCallback } from "react";
|
||||
import { IconNames } from "components/icons";
|
||||
import { useQuery } from "react-query";
|
||||
import { InjectedRouter, Params } from "react-router/lib/Router";
|
||||
import { RouteProps } from "react-router/lib/Route";
|
||||
|
|
@ -42,6 +43,8 @@ import {
|
|||
import { IPolicy, IStoredPolicyResponse } from "interfaces/policy";
|
||||
import { ISoftware } from "interfaces/software";
|
||||
import { ITeam } from "interfaces/team";
|
||||
import { IEmptyTableProps } from "interfaces/empty_table";
|
||||
|
||||
import sortUtils from "utilities/sort";
|
||||
import {
|
||||
HOSTS_SEARCH_BOX_PLACEHOLDER,
|
||||
|
|
@ -59,6 +62,7 @@ import { IActionButtonProps } from "components/TableContainer/DataTable/ActionBu
|
|||
import TeamsDropdown from "components/TeamsDropdown";
|
||||
import Spinner from "components/Spinner";
|
||||
import MainContent from "components/MainContent";
|
||||
import EmptyTable from "components/EmptyTable";
|
||||
|
||||
import { getValidatedTeamId } from "utilities/helpers";
|
||||
import {
|
||||
|
|
@ -78,8 +82,6 @@ import DeleteSecretModal from "../../../components/EnrollSecrets/DeleteSecretMod
|
|||
import SecretEditorModal from "../../../components/EnrollSecrets/SecretEditorModal";
|
||||
import AddHostsModal from "../../../components/AddHostsModal";
|
||||
import EnrollSecretModal from "../../../components/EnrollSecrets/EnrollSecretModal";
|
||||
import NoHosts from "./components/NoHosts";
|
||||
import EmptyHosts from "./components/EmptyHosts";
|
||||
import PoliciesFilter from "./components/PoliciesFilter";
|
||||
// @ts-ignore
|
||||
import EditColumnsModal from "./components/EditColumnsModal/EditColumnsModal";
|
||||
|
|
@ -1679,12 +1681,41 @@ const ManageHostsPage = ({
|
|||
osVersion
|
||||
);
|
||||
|
||||
const emptyState = () => {
|
||||
const emptyHosts: IEmptyTableProps = {
|
||||
iconName: "empty-hosts",
|
||||
header: "Devices will show up here once they’re added to Fleet.",
|
||||
info:
|
||||
"Expecting to see devices? Try again in a few seconds as the system catches up.",
|
||||
};
|
||||
if (includesNameCardFilter) {
|
||||
delete emptyHosts.iconName;
|
||||
emptyHosts.header = "No hosts match the current criteria";
|
||||
emptyHosts.info =
|
||||
"Expecting to see new hosts? Try again in a few seconds as the system catches up.";
|
||||
}
|
||||
if (canEnrollHosts) {
|
||||
emptyHosts.header = "Add your devices to Fleet";
|
||||
emptyHosts.info = "Generate an installer to add your own devices.";
|
||||
emptyHosts.primaryButton = (
|
||||
<Button variant="brand" onClick={toggleAddHostsModal} type="button">
|
||||
Add hosts
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
return emptyHosts;
|
||||
};
|
||||
|
||||
return (
|
||||
<NoHosts
|
||||
toggleAddHostsModal={toggleAddHostsModal}
|
||||
canEnrollHosts={canEnrollHosts}
|
||||
includesNameCardFilter={includesNameCardFilter}
|
||||
/>
|
||||
<>
|
||||
{EmptyTable({
|
||||
iconName: emptyState().iconName,
|
||||
header: emptyState().header,
|
||||
info: emptyState().info,
|
||||
additionalInfo: emptyState().additionalInfo,
|
||||
primaryButton: emptyState().primaryButton,
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -1706,6 +1737,21 @@ const ManageHostsPage = ({
|
|||
currentTeam
|
||||
);
|
||||
|
||||
const emptyState = () => {
|
||||
const emptyHosts: IEmptyTableProps = {
|
||||
header: "No hosts match the current criteria",
|
||||
info:
|
||||
"Expecting to see new hosts? Try again in a few seconds as the system catches up.",
|
||||
};
|
||||
if (isLastPage) {
|
||||
emptyHosts.header = "No more hosts to display";
|
||||
emptyHosts.info =
|
||||
"Expecting to see more hosts? Try again in a few seconds as the system catches up.";
|
||||
}
|
||||
|
||||
return emptyHosts;
|
||||
};
|
||||
|
||||
return (
|
||||
<TableContainer
|
||||
columns={tableColumns}
|
||||
|
|
@ -1716,6 +1762,7 @@ const ManageHostsPage = ({
|
|||
defaultSortDirection={
|
||||
(sortBy[0] && sortBy[0].direction) || DEFAULT_SORT_DIRECTION
|
||||
}
|
||||
defaultSearchQuery={searchQuery}
|
||||
pageSize={100}
|
||||
actionButtonText={"Edit columns"}
|
||||
actionButtonIcon={EditColumnsIcon}
|
||||
|
|
@ -1732,7 +1779,12 @@ const ManageHostsPage = ({
|
|||
searchable
|
||||
renderCount={renderHostCount}
|
||||
searchToolTipText={HOSTS_SEARCH_BOX_TOOLTIP}
|
||||
emptyComponent={EmptyHosts}
|
||||
emptyComponent={() =>
|
||||
EmptyTable({
|
||||
header: emptyState().header,
|
||||
info: emptyState().info,
|
||||
})
|
||||
}
|
||||
customControl={renderCustomControls}
|
||||
onActionButtonClick={toggleEditColumnsModal}
|
||||
onPrimarySelectActionClick={onDeleteHostsClick}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
width: 100%;
|
||||
line-height: 1.5;
|
||||
background-color: $ui-light-grey;
|
||||
border: solid 1px $ui-fleet-blue-15;
|
||||
border: solid 1px $ui-fleet-black-10;
|
||||
border-radius: 4px;
|
||||
font-size: $small;
|
||||
padding: $pad-xsmall 12px $pad-xsmall 42px;
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
/**
|
||||
* Component when there is no host results found in a search
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
const baseClass = "empty-hosts";
|
||||
|
||||
interface IEmptyHostsProps {
|
||||
pageIndex: number;
|
||||
}
|
||||
|
||||
const EmptyHosts = ({ pageIndex }: IEmptyHostsProps): JSX.Element => {
|
||||
return (
|
||||
<div className={`${baseClass}`}>
|
||||
<div className={`${baseClass}__inner`}>
|
||||
<div className={`${baseClass}__empty-filter-results`}>
|
||||
<h1>
|
||||
{pageIndex !== 0
|
||||
? "No more hosts to display"
|
||||
: "No hosts match the current criteria"}
|
||||
</h1>
|
||||
<p>
|
||||
Expecting to see {pageIndex !== 0 ? "more" : "new"} hosts? Try again
|
||||
in a few seconds as the system catches up
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmptyHosts;
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
.empty-hosts {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: $pad-xxxlarge;
|
||||
|
||||
&__inner {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
h1 {
|
||||
font-size: $small;
|
||||
font-weight: $bold;
|
||||
margin-bottom: $pad-medium;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 176px;
|
||||
margin-right: $pad-xlarge;
|
||||
}
|
||||
|
||||
p {
|
||||
color: $core-fleet-black;
|
||||
font-weight: $regular;
|
||||
font-size: $x-small;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__empty-filter-results {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 350px;
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
export { default } from "./EmptyHosts";
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
}
|
||||
|
||||
.label-filter-select__control {
|
||||
border: 1px solid $ui-fleet-blue-15;
|
||||
border: 1px solid $ui-fleet-black-10;
|
||||
background-color: $ui-light-grey;
|
||||
border-radius: $border-radius;
|
||||
height: 40px;
|
||||
|
|
@ -67,6 +67,7 @@
|
|||
width: 300px;
|
||||
margin-top: 0;
|
||||
z-index: 2;
|
||||
animation: fade-in 150ms ease-out;
|
||||
}
|
||||
|
||||
.label-filter-select__menu-list {
|
||||
|
|
|
|||
|
|
@ -1,70 +0,0 @@
|
|||
/**
|
||||
* Component when there is no hosts set up in fleet
|
||||
*/
|
||||
import React from "react";
|
||||
import Button from "components/buttons/Button";
|
||||
|
||||
import RoboDogImage from "../../../../../../assets/images/robo-dog-176x144@2x.png";
|
||||
|
||||
interface INoHostsProps {
|
||||
toggleAddHostsModal: () => void;
|
||||
canEnrollHosts?: boolean;
|
||||
includesNameCardFilter?: boolean;
|
||||
}
|
||||
|
||||
const baseClass = "no-hosts";
|
||||
|
||||
const NoHosts = ({
|
||||
toggleAddHostsModal,
|
||||
canEnrollHosts,
|
||||
includesNameCardFilter,
|
||||
}: INoHostsProps): JSX.Element => {
|
||||
const renderContent = () => {
|
||||
if (includesNameCardFilter) {
|
||||
return (
|
||||
<div>
|
||||
<h1>No hosts match the current criteria</h1>
|
||||
<p>
|
||||
Expecting to see new hosts? Try again in a few seconds as the system
|
||||
catches up.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (canEnrollHosts) {
|
||||
return (
|
||||
<div>
|
||||
<h2>Add your devices to Fleet</h2>
|
||||
<p>Generate an installer to add your own devices.</p>
|
||||
<div className={`${baseClass}__no-hosts-button`}>
|
||||
<Button variant="brand" onClick={toggleAddHostsModal} type="button">
|
||||
Add hosts
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>Devices will show up here once they’re added to Fleet.</h2>
|
||||
<p>
|
||||
Expecting to see devices? Try again in a few seconds as the system
|
||||
catches up.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`${baseClass}`}>
|
||||
<div className={`${baseClass}__inner`}>
|
||||
{!includesNameCardFilter && <img src={RoboDogImage} alt="No Hosts" />}
|
||||
{renderContent()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NoHosts;
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
.no-hosts {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: $pad-xxxlarge;
|
||||
|
||||
h1 {
|
||||
font-size: $large;
|
||||
font-weight: $regular;
|
||||
line-height: normal;
|
||||
letter-spacing: normal;
|
||||
color: $core-fleet-black;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: $x-small;
|
||||
font-weight: $bold;
|
||||
margin: 0 0 $pad-large;
|
||||
line-height: 20px;
|
||||
color: $core-fleet-black;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: $core-fleet-black;
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
&::before {
|
||||
content: "•";
|
||||
color: $core-vibrant-blue;
|
||||
margin-right: $pad-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
color: $core-fleet-black;
|
||||
font-weight: $regular;
|
||||
font-size: $x-small;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__inner {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
h1 {
|
||||
font-size: $small;
|
||||
font-weight: $bold;
|
||||
margin-bottom: $pad-medium;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 176px;
|
||||
margin-right: $pad-xlarge;
|
||||
}
|
||||
|
||||
.no-filter-results {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 350px;
|
||||
}
|
||||
}
|
||||
|
||||
.host-pagination__pager-wrap {
|
||||
margin-top: $pad-medium;
|
||||
}
|
||||
|
||||
&__no-hosts-contact {
|
||||
text-align: left;
|
||||
margin-top: $pad-large;
|
||||
}
|
||||
|
||||
&__no-hosts-button {
|
||||
margin-top: $pad-large;
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
export { default } from "./NoHosts";
|
||||
|
|
@ -30,7 +30,7 @@
|
|||
flex-direction: column;
|
||||
background-color: $core-white;
|
||||
border-radius: 16px;
|
||||
border: 1px solid $ui-fleet-blue-15;
|
||||
border: 1px solid $ui-fleet-black-10;
|
||||
padding: $pad-xxlarge;
|
||||
box-shadow: 0px 3px 0px rgba(226, 228, 234, 0.4);
|
||||
|
||||
|
|
@ -335,7 +335,7 @@
|
|||
}
|
||||
|
||||
&__wrapper {
|
||||
border: solid 1px $ui-fleet-blue-15;
|
||||
border: solid 1px $ui-fleet-black-10;
|
||||
border-radius: 6px;
|
||||
margin-top: $pad-small;
|
||||
overflow: scroll;
|
||||
|
|
|
|||
|
|
@ -682,9 +682,7 @@ const HostDetailsPage = ({
|
|||
<SoftwareCard
|
||||
isLoading={isLoadingHost}
|
||||
software={hostSoftware}
|
||||
softwareInventoryEnabled={
|
||||
featuresConfig?.enable_software_inventory
|
||||
}
|
||||
isSoftwareEnabled={featuresConfig?.enable_software_inventory}
|
||||
deviceType={host?.platform === "darwin" ? "macos" : ""}
|
||||
router={router}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
flex-direction: column;
|
||||
background-color: $core-white;
|
||||
border-radius: 16px;
|
||||
border: 1px solid $ui-fleet-blue-15;
|
||||
border: 1px solid $ui-fleet-black-10;
|
||||
padding: $pad-xxlarge;
|
||||
box-shadow: 0px 3px 0px rgba(226, 228, 234, 0.4);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import React from "react";
|
||||
|
||||
import ReactTooltip from "react-tooltip";
|
||||
import HumanTimeDiffWithDateTip from "components/HumanTimeDiffWithDateTip";
|
||||
|
||||
import { IHostMdmData, IMunkiData, IDeviceUser } from "interfaces/host";
|
||||
import { humanHostLastRestart, humanHostEnrolled } from "utilities/helpers";
|
||||
import { humanHostLastRestart } from "utilities/helpers";
|
||||
|
||||
interface IAboutProps {
|
||||
aboutData: { [key: string]: any };
|
||||
|
|
@ -18,7 +19,6 @@ const About = ({
|
|||
deviceMapping,
|
||||
munki,
|
||||
mdm,
|
||||
wrapFleetHelper,
|
||||
}: IAboutProps): JSX.Element => {
|
||||
const renderSerialAndIPs = () => {
|
||||
return (
|
||||
|
|
@ -143,7 +143,6 @@ const About = ({
|
|||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="section about">
|
||||
<p className="section__header">About</p>
|
||||
|
|
@ -151,16 +150,20 @@ const About = ({
|
|||
<div className="info-grid__block">
|
||||
<span className="info-grid__header">Added to Fleet</span>
|
||||
<span className="info-grid__data">
|
||||
{wrapFleetHelper(humanHostEnrolled, aboutData.last_enrolled_at)}
|
||||
<HumanTimeDiffWithDateTip
|
||||
timeString={aboutData.last_enrolled_at ?? "Unavailable"}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div className="info-grid__block">
|
||||
<span className="info-grid__header">Last restarted</span>
|
||||
<span className="info-grid__data">
|
||||
{humanHostLastRestart(
|
||||
aboutData.detail_updated_at,
|
||||
aboutData.uptime
|
||||
)}
|
||||
<HumanTimeDiffWithDateTip
|
||||
timeString={humanHostLastRestart(
|
||||
aboutData.detail_updated_at,
|
||||
aboutData.uptime
|
||||
)}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div className="info-grid__block">
|
||||
|
|
|
|||
|
|
@ -5,11 +5,8 @@ import TooltipWrapper from "components/TooltipWrapper";
|
|||
|
||||
import Button from "components/buttons/Button";
|
||||
import DiskSpaceGraph from "components/DiskSpaceGraph";
|
||||
import {
|
||||
humanHostMemory,
|
||||
humanHostDetailUpdated,
|
||||
wrapFleetHelper,
|
||||
} from "utilities/helpers";
|
||||
import HumanTimeDiffWithDateTip from "components/HumanTimeDiffWithDateTip";
|
||||
import { humanHostMemory, wrapFleetHelper } from "utilities/helpers";
|
||||
import getHostStatusTooltipText from "pages/hosts/helpers";
|
||||
import StatusIndicator from "components/StatusIndicator";
|
||||
import IssueIcon from "../../../../../../assets/images/icon-issue-fleet-black-50-16x16@2x.png";
|
||||
|
|
@ -203,6 +200,12 @@ const HostSummary = ({
|
|||
);
|
||||
};
|
||||
|
||||
const lastFetched = titleData.detail_updated_at ? (
|
||||
<HumanTimeDiffWithDateTip timeString={titleData.detail_updated_at} />
|
||||
) : (
|
||||
": unavailable"
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="header title">
|
||||
|
|
@ -211,10 +214,9 @@ const HostSummary = ({
|
|||
<h1 className="display-name">
|
||||
{deviceUser ? "My device" : titleData.display_name || "---"}
|
||||
</h1>
|
||||
|
||||
<p className="last-fetched">
|
||||
{`Last fetched ${humanHostDetailUpdated(
|
||||
titleData.detail_updated_at
|
||||
)}`}
|
||||
{"Last fetched"} {lastFetched}
|
||||
|
||||
</p>
|
||||
{renderRefetch()}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ interface ISoftwareTableProps {
|
|||
software: ISoftware[];
|
||||
deviceUser?: boolean;
|
||||
deviceType?: string;
|
||||
softwareInventoryEnabled?: boolean;
|
||||
isSoftwareEnabled?: boolean;
|
||||
router?: InjectedRouter;
|
||||
}
|
||||
|
||||
|
|
@ -39,6 +39,7 @@ interface IRowProps extends Row {
|
|||
original: {
|
||||
id?: number;
|
||||
};
|
||||
isSoftwareEnabled?: boolean;
|
||||
}
|
||||
|
||||
const SoftwareTable = ({
|
||||
|
|
@ -46,7 +47,7 @@ const SoftwareTable = ({
|
|||
software,
|
||||
deviceUser,
|
||||
deviceType,
|
||||
softwareInventoryEnabled,
|
||||
isSoftwareEnabled,
|
||||
router,
|
||||
}: ISoftwareTableProps): JSX.Element => {
|
||||
const [searchString, setSearchString] = useState("");
|
||||
|
|
@ -109,15 +110,6 @@ const SoftwareTable = ({
|
|||
<EmptyState title="software" reason="empty-search" />
|
||||
);
|
||||
|
||||
if (softwareInventoryEnabled === false) {
|
||||
return (
|
||||
<div className="section section--software">
|
||||
<p className="section__header">Software</p>
|
||||
<EmptyState title="software" reason="disabled" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="section section--software">
|
||||
<p className="section__header">Software</p>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
import React, { useCallback, useEffect, useState } from "react";
|
||||
|
||||
import { IPack } from "interfaces/pack";
|
||||
import { IEmptyTableProps } from "interfaces/empty_table";
|
||||
import Button from "components/buttons/Button";
|
||||
|
||||
import TableContainer from "components/TableContainer";
|
||||
import EmptyTable from "components/EmptyTable";
|
||||
import { IActionButtonProps } from "components/TableContainer/DataTable/ActionButton";
|
||||
import { generateTableHeaders, generateDataSet } from "./PacksTableConfig";
|
||||
|
||||
const baseClass = "packs-table";
|
||||
const noPacksClass = "no-packs";
|
||||
|
||||
interface IPacksTableProps {
|
||||
onDeletePackClick: (selectedTablePackIds: number[]) => void;
|
||||
|
|
@ -54,40 +55,32 @@ const PacksTable = ({
|
|||
[setSearchString]
|
||||
);
|
||||
|
||||
const NoPacksComponent = useCallback(() => {
|
||||
return (
|
||||
<div className={`${noPacksClass}`}>
|
||||
<div className={`${noPacksClass}__inner`}>
|
||||
<div className={`${noPacksClass}__inner-text`}>
|
||||
{searchString ? (
|
||||
<>
|
||||
<h2>No packs match the current search criteria.</h2>
|
||||
<p>
|
||||
Expecting to see packs? Try again in a few seconds as the
|
||||
system catches up.
|
||||
</p>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<h2>You don't have any packs</h2>
|
||||
<p>
|
||||
Query packs allow you to schedule recurring queries for your
|
||||
hosts.
|
||||
</p>
|
||||
<Button
|
||||
variant="brand"
|
||||
className={`${baseClass}__create-button`}
|
||||
onClick={onCreatePackClick}
|
||||
>
|
||||
Create new pack
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}, [searchString]);
|
||||
// TODO: useCallback search string
|
||||
const emptyState = () => {
|
||||
const emptyPacks: IEmptyTableProps = {
|
||||
iconName: "empty-packs",
|
||||
header: "You don't have any packs",
|
||||
info:
|
||||
"Query packs allow you to schedule recurring queries for your hosts.",
|
||||
primaryButton: (
|
||||
<Button
|
||||
variant="brand"
|
||||
className={`${baseClass}__create-button`}
|
||||
onClick={onCreatePackClick}
|
||||
>
|
||||
Create new pack
|
||||
</Button>
|
||||
),
|
||||
};
|
||||
if (searchString) {
|
||||
delete emptyPacks.iconName;
|
||||
emptyPacks.header = "No packs match the current search criteria.";
|
||||
emptyPacks.info =
|
||||
"Expecting to see packs? Try again in a few seconds as the system catches up.";
|
||||
delete emptyPacks.primaryButton;
|
||||
}
|
||||
return emptyPacks;
|
||||
};
|
||||
|
||||
const tableHeaders = generateTableHeaders();
|
||||
|
||||
|
|
@ -127,7 +120,14 @@ const PacksTable = ({
|
|||
primarySelectActionButtonIcon="delete"
|
||||
primarySelectActionButtonText={"Delete"}
|
||||
secondarySelectActions={secondarySelectActions}
|
||||
emptyComponent={NoPacksComponent}
|
||||
emptyComponent={() =>
|
||||
EmptyTable({
|
||||
iconName: emptyState().iconName,
|
||||
header: emptyState().header,
|
||||
info: emptyState().info,
|
||||
primaryButton: emptyState().primaryButton,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue