mirror of
https://github.com/Infisical/infisical.git
synced 2025-03-25 14:05:03 +00:00
Updated Infisical onboarding guide
This commit is contained in:
@ -1,37 +1,38 @@
|
||||
/* eslint-disable no-unexpected-multiline */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { useEffect, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect, useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import {
|
||||
faBookOpen,
|
||||
faGear,
|
||||
faKey,
|
||||
faMobile,
|
||||
faPlug,
|
||||
faUser,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
faUser
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
import { faPlus } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
|
||||
import getOrganizations from "~/pages/api/organization/getOrgs";
|
||||
import getOrganizationUserProjects from "~/pages/api/organization/GetOrgUserProjects";
|
||||
import getOrganizationUsers from "~/pages/api/organization/GetOrgUsers";
|
||||
import addUserToWorkspace from "~/pages/api/workspace/addUserToWorkspace";
|
||||
import createWorkspace from "~/pages/api/workspace/createWorkspace";
|
||||
import getWorkspaces from "~/pages/api/workspace/getWorkspaces";
|
||||
import uploadKeys from "~/pages/api/workspace/uploadKeys";
|
||||
import checkUserAction from "~/pages/api/userActions/checkUserAction";
|
||||
import getOrganizations from '~/pages/api/organization/getOrgs';
|
||||
import getOrganizationUserProjects from '~/pages/api/organization/GetOrgUserProjects';
|
||||
import getOrganizationUsers from '~/pages/api/organization/GetOrgUsers';
|
||||
import checkUserAction from '~/pages/api/userActions/checkUserAction';
|
||||
import addUserToWorkspace from '~/pages/api/workspace/addUserToWorkspace';
|
||||
import createWorkspace from '~/pages/api/workspace/createWorkspace';
|
||||
import getWorkspaces from '~/pages/api/workspace/getWorkspaces';
|
||||
import uploadKeys from '~/pages/api/workspace/uploadKeys';
|
||||
|
||||
import NavBarDashboard from "../navigation/NavBarDashboard";
|
||||
import { tempLocalStorage } from "../utilities/checks/tempLocalStorage";
|
||||
import NavBarDashboard from '../navigation/NavBarDashboard';
|
||||
import onboardingCheck from '../utilities/checks/OnboardingCheck';
|
||||
import { tempLocalStorage } from '../utilities/checks/tempLocalStorage';
|
||||
import {
|
||||
decryptAssymmetric,
|
||||
encryptAssymmetric,
|
||||
} from "../utilities/cryptography/crypto";
|
||||
import Button from "./buttons/Button";
|
||||
import AddWorkspaceDialog from "./dialog/AddWorkspaceDialog";
|
||||
import Listbox from "./Listbox";
|
||||
encryptAssymmetric
|
||||
} from '../utilities/cryptography/crypto';
|
||||
import Button from './buttons/Button';
|
||||
import AddWorkspaceDialog from './dialog/AddWorkspaceDialog';
|
||||
import Listbox from './Listbox';
|
||||
|
||||
interface LayoutProps {
|
||||
children: React.ReactNode;
|
||||
@ -41,16 +42,13 @@ export default function Layout({ children }: LayoutProps) {
|
||||
const router = useRouter();
|
||||
const [workspaceList, setWorkspaceList] = useState([]);
|
||||
const [workspaceMapping, setWorkspaceMapping] = useState([{ 1: 2 }]);
|
||||
const [workspaceSelected, setWorkspaceSelected] = useState("∞");
|
||||
const [newWorkspaceName, setNewWorkspaceName] = useState("");
|
||||
const [workspaceSelected, setWorkspaceSelected] = useState('∞');
|
||||
const [newWorkspaceName, setNewWorkspaceName] = useState('');
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
const [hasUserClickedSlack, setHasUserClickedSlack] = useState(false);
|
||||
const [hasUserClickedIntro, setHasUserClickedIntro] = useState(false);
|
||||
const [hasUserStarred, setHasUserStarred] = useState(false);
|
||||
const [usersInOrg, setUsersInOrg] = useState(false);
|
||||
const [totalOnboardingActionsDone, setTotalOnboardingActionsDone] = useState(0);
|
||||
const [totalOnboardingActionsDone, setTotalOnboardingActionsDone] =
|
||||
useState(0);
|
||||
|
||||
function closeModal() {
|
||||
setIsOpen(false);
|
||||
@ -77,35 +75,35 @@ export default function Layout({ children }: LayoutProps) {
|
||||
if (!currentWorkspaces.includes(workspaceName)) {
|
||||
const newWorkspace = await createWorkspace({
|
||||
workspaceName,
|
||||
organizationId: tempLocalStorage("orgData.id"),
|
||||
organizationId: tempLocalStorage('orgData.id')
|
||||
});
|
||||
const newWorkspaceId = newWorkspace._id;
|
||||
|
||||
if (addAllUsers) {
|
||||
const orgUsers = await getOrganizationUsers({
|
||||
orgId: tempLocalStorage("orgData.id"),
|
||||
orgId: tempLocalStorage('orgData.id')
|
||||
});
|
||||
orgUsers.map(async (user: any) => {
|
||||
if (user.status == "accepted") {
|
||||
if (user.status == 'accepted') {
|
||||
const result = await addUserToWorkspace(
|
||||
user.user.email,
|
||||
newWorkspaceId
|
||||
);
|
||||
if (result?.invitee && result?.latestKey) {
|
||||
const PRIVATE_KEY = tempLocalStorage("PRIVATE_KEY");
|
||||
const PRIVATE_KEY = tempLocalStorage('PRIVATE_KEY');
|
||||
|
||||
// assymmetrically decrypt symmetric key with local private key
|
||||
const key = decryptAssymmetric({
|
||||
ciphertext: result.latestKey.encryptedKey,
|
||||
nonce: result.latestKey.nonce,
|
||||
publicKey: result.latestKey.sender.publicKey,
|
||||
privateKey: PRIVATE_KEY,
|
||||
privateKey: PRIVATE_KEY
|
||||
});
|
||||
|
||||
const { ciphertext, nonce } = encryptAssymmetric({
|
||||
plaintext: key,
|
||||
publicKey: result.invitee.publicKey,
|
||||
privateKey: PRIVATE_KEY,
|
||||
privateKey: PRIVATE_KEY
|
||||
}) as { ciphertext: string; nonce: string };
|
||||
|
||||
uploadKeys(
|
||||
@ -118,11 +116,11 @@ export default function Layout({ children }: LayoutProps) {
|
||||
}
|
||||
});
|
||||
}
|
||||
router.push("/dashboard/" + newWorkspaceId + "?Development");
|
||||
router.push('/dashboard/' + newWorkspaceId + '?Development');
|
||||
setIsOpen(false);
|
||||
setNewWorkspaceName("");
|
||||
setNewWorkspaceName('');
|
||||
} else {
|
||||
console.error("A project with this name already exists.");
|
||||
console.error('A project with this name already exists.');
|
||||
setError(true);
|
||||
setLoading(false);
|
||||
}
|
||||
@ -136,59 +134,59 @@ export default function Layout({ children }: LayoutProps) {
|
||||
const menuItems = [
|
||||
{
|
||||
href:
|
||||
"/dashboard/" +
|
||||
'/dashboard/' +
|
||||
workspaceMapping[workspaceSelected as any] +
|
||||
"?Development",
|
||||
title: "Secrets",
|
||||
emoji: <FontAwesomeIcon icon={faKey} />,
|
||||
'?Development',
|
||||
title: 'Secrets',
|
||||
emoji: <FontAwesomeIcon icon={faKey} />
|
||||
},
|
||||
{
|
||||
href: "/users/" + workspaceMapping[workspaceSelected as any],
|
||||
title: "Members",
|
||||
emoji: <FontAwesomeIcon icon={faUser} />,
|
||||
href: '/users/' + workspaceMapping[workspaceSelected as any],
|
||||
title: 'Members',
|
||||
emoji: <FontAwesomeIcon icon={faUser} />
|
||||
},
|
||||
{
|
||||
href: "/integrations/" + workspaceMapping[workspaceSelected as any],
|
||||
title: "Integrations",
|
||||
emoji: <FontAwesomeIcon icon={faPlug} />,
|
||||
href: '/integrations/' + workspaceMapping[workspaceSelected as any],
|
||||
title: 'Integrations',
|
||||
emoji: <FontAwesomeIcon icon={faPlug} />
|
||||
},
|
||||
{
|
||||
href: "/settings/project/" + workspaceMapping[workspaceSelected as any],
|
||||
title: "Project Settings",
|
||||
emoji: <FontAwesomeIcon icon={faGear} />,
|
||||
},
|
||||
href: '/settings/project/' + workspaceMapping[workspaceSelected as any],
|
||||
title: 'Project Settings',
|
||||
emoji: <FontAwesomeIcon icon={faGear} />
|
||||
}
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
// Put a user in a workspace if they're not in one yet
|
||||
const putUserInWorkSpace = async () => {
|
||||
if (tempLocalStorage("orgData.id") === "") {
|
||||
if (tempLocalStorage('orgData.id') === '') {
|
||||
const userOrgs = await getOrganizations();
|
||||
localStorage.setItem("orgData.id", userOrgs[0]._id);
|
||||
localStorage.setItem('orgData.id', userOrgs[0]._id);
|
||||
}
|
||||
|
||||
const orgUserProjects = await getOrganizationUserProjects({
|
||||
orgId: tempLocalStorage("orgData.id"),
|
||||
orgId: tempLocalStorage('orgData.id')
|
||||
});
|
||||
const userWorkspaces = orgUserProjects;
|
||||
if (
|
||||
userWorkspaces.length == 0 &&
|
||||
router.asPath != "/noprojects" &&
|
||||
!router.asPath.includes("settings")
|
||||
router.asPath != '/noprojects' &&
|
||||
!router.asPath.includes('settings')
|
||||
) {
|
||||
router.push("/noprojects");
|
||||
} else if (router.asPath != "/noprojects") {
|
||||
router.push('/noprojects');
|
||||
} else if (router.asPath != '/noprojects') {
|
||||
const intendedWorkspaceId = router.asPath
|
||||
.split("/")
|
||||
[router.asPath.split("/").length - 1].split("?")[0];
|
||||
.split('/')
|
||||
[router.asPath.split('/').length - 1].split('?')[0];
|
||||
// If a user is not a member of a workspace they are trying to access, just push them to one of theirs
|
||||
if (
|
||||
intendedWorkspaceId != "heroku" &&
|
||||
intendedWorkspaceId != 'heroku' &&
|
||||
!userWorkspaces
|
||||
.map((workspace: { _id: string }) => workspace._id)
|
||||
.includes(intendedWorkspaceId)
|
||||
) {
|
||||
router.push("/dashboard/" + userWorkspaces[0]._id + "?Development");
|
||||
router.push('/dashboard/' + userWorkspaces[0]._id + '?Development');
|
||||
} else {
|
||||
setWorkspaceList(
|
||||
userWorkspaces.map((workspace: any) => workspace.name)
|
||||
@ -197,7 +195,7 @@ export default function Layout({ children }: LayoutProps) {
|
||||
Object.fromEntries(
|
||||
userWorkspaces.map((workspace: any) => [
|
||||
workspace.name,
|
||||
workspace._id,
|
||||
workspace._id
|
||||
])
|
||||
) as any
|
||||
);
|
||||
@ -205,58 +203,19 @@ export default function Layout({ children }: LayoutProps) {
|
||||
Object.fromEntries(
|
||||
userWorkspaces.map((workspace: any) => [
|
||||
workspace._id,
|
||||
workspace.name,
|
||||
workspace.name
|
||||
])
|
||||
)[
|
||||
router.asPath
|
||||
.split("/")
|
||||
[router.asPath.split("/").length - 1].split("?")[0]
|
||||
.split('/')
|
||||
[router.asPath.split('/').length - 1].split('?')[0]
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
putUserInWorkSpace();
|
||||
|
||||
const checkUserActionsFunction = async () => {
|
||||
let countActions = 0;
|
||||
const userActionSlack = await checkUserAction({
|
||||
action: "slack_cta_clicked",
|
||||
});
|
||||
setHasUserClickedSlack(userActionSlack ? true : false);
|
||||
if (userActionSlack) {
|
||||
countActions = countActions + 1;
|
||||
}
|
||||
|
||||
const userActionIntro = await checkUserAction({
|
||||
action: "intro_cta_clicked",
|
||||
});
|
||||
setHasUserClickedIntro(userActionIntro ? true : false);
|
||||
if (userActionIntro) {
|
||||
countActions = countActions + 1;
|
||||
}
|
||||
|
||||
const userActionStar = await checkUserAction({
|
||||
action: "star_cta_clicked",
|
||||
});
|
||||
setHasUserStarred(userActionStar ? true : false);
|
||||
if (userActionStar) {
|
||||
countActions = countActions + 1;
|
||||
}
|
||||
|
||||
const orgId = localStorage.getItem("orgData.id");
|
||||
const orgUsers = await getOrganizationUsers({
|
||||
orgId: orgId ? orgId : "",
|
||||
});
|
||||
setUsersInOrg(orgUsers.length > 1)
|
||||
if (orgUsers.length > 1) {
|
||||
countActions = countActions + 1;
|
||||
}
|
||||
console.log(123, countActions)
|
||||
setTotalOnboardingActionsDone(countActions);
|
||||
};
|
||||
console.log(`images/progress-${totalOnboardingActionsDone == 0 ? "0" : ""}${totalOnboardingActionsDone == 1 ? "14" : ""}${totalOnboardingActionsDone == 1 ? "28" : ""}${totalOnboardingActionsDone == 3 ? "43" : ""}${totalOnboardingActionsDone == 4 ? "57" : ""}.svg`)
|
||||
checkUserActionsFunction();
|
||||
onboardingCheck({ setTotalOnboardingActionsDone });
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@ -265,16 +224,16 @@ export default function Layout({ children }: LayoutProps) {
|
||||
workspaceMapping[Number(workspaceSelected)] &&
|
||||
`${workspaceMapping[Number(workspaceSelected)]}` !==
|
||||
router.asPath
|
||||
.split("/")
|
||||
[router.asPath.split("/").length - 1].split("?")[0]
|
||||
.split('/')
|
||||
[router.asPath.split('/').length - 1].split('?')[0]
|
||||
) {
|
||||
router.push(
|
||||
"/dashboard/" +
|
||||
'/dashboard/' +
|
||||
workspaceMapping[Number(workspaceSelected)] +
|
||||
"?Development"
|
||||
'?Development'
|
||||
);
|
||||
localStorage.setItem(
|
||||
"projectData.id",
|
||||
'projectData.id',
|
||||
`${workspaceMapping[Number(workspaceSelected)]}`
|
||||
);
|
||||
}
|
||||
@ -286,7 +245,10 @@ export default function Layout({ children }: LayoutProps) {
|
||||
return (
|
||||
<>
|
||||
<div className="fixed w-full hidden md:block flex flex-col h-screen">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/alpinejs/3.2.2/cdn.js" defer></script>
|
||||
<script
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/alpinejs/3.2.2/cdn.js"
|
||||
defer
|
||||
></script>
|
||||
<NavBarDashboard />
|
||||
<div className="flex flex-col md:flex-row flex-1">
|
||||
<aside className="bg-bunker-600 border-r border-mineshaft-500 w-full md:w-60 h-screen">
|
||||
@ -320,11 +282,11 @@ export default function Layout({ children }: LayoutProps) {
|
||||
{workspaceList.length > 0 &&
|
||||
menuItems.map(({ href, title, emoji }) => (
|
||||
<li className="mt-0.5 mx-2" key={title}>
|
||||
{router.asPath.split("/")[1] === href.split("/")[1] &&
|
||||
(["project", "billing", "org", "personal"].includes(
|
||||
router.asPath.split("/")[2]
|
||||
{router.asPath.split('/')[1] === href.split('/')[1] &&
|
||||
(['project', 'billing', 'org', 'personal'].includes(
|
||||
router.asPath.split('/')[2]
|
||||
)
|
||||
? router.asPath.split("/")[2] === href.split("/")[2]
|
||||
? router.asPath.split('/')[2] === href.split('/')[2]
|
||||
: true) ? (
|
||||
<div
|
||||
className={`flex relative px-0.5 py-2.5 text-white text-sm rounded cursor-pointer bg-primary-50/10`}
|
||||
@ -335,7 +297,7 @@ export default function Layout({ children }: LayoutProps) {
|
||||
</p>
|
||||
{title}
|
||||
</div>
|
||||
) : router.asPath == "/noprojects" ? (
|
||||
) : router.asPath == '/noprojects' ? (
|
||||
<div
|
||||
className={`flex p-2.5 text-white text-sm rounded`}
|
||||
>
|
||||
@ -361,7 +323,7 @@ export default function Layout({ children }: LayoutProps) {
|
||||
</ul>
|
||||
</div>
|
||||
<div className="w-full mt-40 mb-4 px-2">
|
||||
{router.asPath.split("/")[1] === "home" ? (
|
||||
{router.asPath.split('/')[1] === 'home' ? (
|
||||
<div
|
||||
className={`flex relative px-0.5 py-2.5 text-white text-sm rounded cursor-pointer bg-primary-50/10`}
|
||||
>
|
||||
@ -371,7 +333,13 @@ export default function Layout({ children }: LayoutProps) {
|
||||
</p>
|
||||
Infisical Guide
|
||||
<img
|
||||
src={`/images/progress-${totalOnboardingActionsDone == 0 ? "0" : ""}${totalOnboardingActionsDone == 1 ? "14" : ""}${totalOnboardingActionsDone == 1 ? "28" : ""}${totalOnboardingActionsDone == 3 ? "43" : ""}${totalOnboardingActionsDone == 4 ? "57" : ""}.svg`}
|
||||
src={`/images/progress-${
|
||||
totalOnboardingActionsDone == 0 ? '0' : ''
|
||||
}${totalOnboardingActionsDone == 1 ? '14' : ''}${
|
||||
totalOnboardingActionsDone == 2 ? '28' : ''
|
||||
}${totalOnboardingActionsDone == 3 ? '43' : ''}${
|
||||
totalOnboardingActionsDone == 4 ? '57' : ''
|
||||
}${totalOnboardingActionsDone == 5 ? '71' : ''}.svg`}
|
||||
height={58}
|
||||
width={58}
|
||||
alt="progress bar"
|
||||
@ -390,7 +358,13 @@ export default function Layout({ children }: LayoutProps) {
|
||||
</p>
|
||||
Infisical Guide
|
||||
<img
|
||||
src="/images/progress-75.svg"
|
||||
src={`/images/progress-${
|
||||
totalOnboardingActionsDone == 0 ? '0' : ''
|
||||
}${totalOnboardingActionsDone == 1 ? '14' : ''}${
|
||||
totalOnboardingActionsDone == 2 ? '28' : ''
|
||||
}${totalOnboardingActionsDone == 3 ? '43' : ''}${
|
||||
totalOnboardingActionsDone == 4 ? '57' : ''
|
||||
}${totalOnboardingActionsDone == 5 ? '71' : ''}.svg`}
|
||||
height={58}
|
||||
width={58}
|
||||
alt="progress bar"
|
||||
@ -420,9 +394,9 @@ export default function Layout({ children }: LayoutProps) {
|
||||
className="text-gray-300 text-7xl mb-8"
|
||||
/>
|
||||
<p className="text-gray-200 px-6 text-center text-lg max-w-sm">
|
||||
{" "}
|
||||
{' '}
|
||||
To use Infisical, please log in through a device with larger
|
||||
dimensions.{" "}
|
||||
dimensions.{' '}
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
|
71
frontend/components/utilities/checks/OnboardingCheck.ts
Normal file
71
frontend/components/utilities/checks/OnboardingCheck.ts
Normal file
@ -0,0 +1,71 @@
|
||||
import getOrganizationUsers from '~/pages/api/organization/GetOrgUsers';
|
||||
import checkUserAction from '~/pages/api/userActions/checkUserAction';
|
||||
|
||||
interface OnboardingCheckProps {
|
||||
setTotalOnboardingActionsDone?: (value: number) => void;
|
||||
setHasUserClickedSlack?: (value: boolean) => void;
|
||||
setHasUserClickedIntro?: (value: boolean) => void;
|
||||
setHasUserStarred?: (value: boolean) => void;
|
||||
setHasUserPushedSecrets?: (value: boolean) => void;
|
||||
setUsersInOrg?: (value: boolean) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function checks which onboarding steps a user has already finished.
|
||||
*/
|
||||
const onboardingCheck = async ({
|
||||
setTotalOnboardingActionsDone,
|
||||
setHasUserClickedSlack,
|
||||
setHasUserClickedIntro,
|
||||
setHasUserStarred,
|
||||
setHasUserPushedSecrets,
|
||||
setUsersInOrg
|
||||
}: OnboardingCheckProps) => {
|
||||
let countActions = 0;
|
||||
const userActionSlack = await checkUserAction({
|
||||
action: 'slack_cta_clicked'
|
||||
});
|
||||
if (userActionSlack) {
|
||||
countActions = countActions + 1;
|
||||
}
|
||||
setHasUserClickedSlack &&
|
||||
setHasUserClickedSlack(userActionSlack ? true : false);
|
||||
|
||||
const userActionSecrets = await checkUserAction({
|
||||
action: 'first_time_secrets_pushed'
|
||||
});
|
||||
if (userActionSecrets) {
|
||||
countActions = countActions + 1;
|
||||
}
|
||||
setHasUserPushedSecrets &&
|
||||
setHasUserPushedSecrets(userActionSecrets ? true : false);
|
||||
|
||||
const userActionIntro = await checkUserAction({
|
||||
action: 'intro_cta_clicked'
|
||||
});
|
||||
if (userActionIntro) {
|
||||
countActions = countActions + 1;
|
||||
}
|
||||
setHasUserClickedIntro &&
|
||||
setHasUserClickedIntro(userActionIntro ? true : false);
|
||||
|
||||
const userActionStar = await checkUserAction({
|
||||
action: 'star_cta_clicked'
|
||||
});
|
||||
if (userActionStar) {
|
||||
countActions = countActions + 1;
|
||||
}
|
||||
setHasUserStarred && setHasUserStarred(userActionStar ? true : false);
|
||||
|
||||
const orgId = localStorage.getItem('orgData.id');
|
||||
const orgUsers = await getOrganizationUsers({
|
||||
orgId: orgId ? orgId : ''
|
||||
});
|
||||
if (orgUsers.length > 1) {
|
||||
countActions = countActions + 1;
|
||||
}
|
||||
setUsersInOrg && setUsersInOrg(orgUsers.length > 1);
|
||||
setTotalOnboardingActionsDone && setTotalOnboardingActionsDone(countActions);
|
||||
};
|
||||
|
||||
export default onboardingCheck;
|
@ -1,92 +1,134 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { IconProp } from "@fortawesome/fontawesome-svg-core";
|
||||
import { faSlack } from "@fortawesome/free-brands-svg-icons";
|
||||
import { faCheckCircle, faHandPeace, faNetworkWired, faPlug, faPlus, faStar, faUserPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import { IconProp } from '@fortawesome/fontawesome-svg-core';
|
||||
import { faSlack } from '@fortawesome/free-brands-svg-icons';
|
||||
import {
|
||||
faCheckCircle,
|
||||
faHandPeace,
|
||||
faNetworkWired,
|
||||
faPlug,
|
||||
faPlus,
|
||||
faStar,
|
||||
faUserPlus
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
|
||||
import getOrganizationUsers from "../api/organization/GetOrgUsers";
|
||||
import checkUserAction from "../api/userActions/checkUserAction";
|
||||
import registerUserAction from "../api/userActions/registerUserAction";
|
||||
import onboardingCheck from '~/components/utilities/checks/OnboardingCheck';
|
||||
|
||||
type ItemProps = {
|
||||
text: string;
|
||||
subText: string;
|
||||
complete: boolean;
|
||||
icon: IconProp;
|
||||
time: string;
|
||||
import registerUserAction from '../api/userActions/registerUserAction';
|
||||
|
||||
type ItemProps = {
|
||||
text: string;
|
||||
subText: string;
|
||||
complete: boolean;
|
||||
icon: IconProp;
|
||||
time: string;
|
||||
userAction?: string;
|
||||
link?: string;
|
||||
};
|
||||
|
||||
const learningItem = ({ text, subText, complete, icon, time, userAction, link }: ItemProps): JSX.Element => {
|
||||
const learningItem = ({
|
||||
text,
|
||||
subText,
|
||||
complete,
|
||||
icon,
|
||||
time,
|
||||
userAction,
|
||||
link
|
||||
}: ItemProps): JSX.Element => {
|
||||
if (link) {
|
||||
return (
|
||||
<Link href={link}>
|
||||
<a target={`${link.includes("https") ? "_blank" : "_self"}`} rel="noopener noreferrer" className="w-full">
|
||||
<div
|
||||
onClick={async () => {
|
||||
if (userAction) {
|
||||
await registerUserAction({
|
||||
action: userAction
|
||||
})
|
||||
<a
|
||||
target={`${link.includes('https') ? '_blank' : '_self'}`}
|
||||
rel="noopener noreferrer"
|
||||
className="w-full"
|
||||
>
|
||||
<div
|
||||
onClick={async () => {
|
||||
if (userAction) {
|
||||
await registerUserAction({
|
||||
action: userAction
|
||||
});
|
||||
}
|
||||
}}
|
||||
className="relative bg-bunker-700 hover:bg-bunker-500 shadow-xl duration-200 rounded-md border border-dashed border-bunker-400 pl-2 pr-6 py-2 h-[5.5rem] w-full flex items-center justify-between overflow-hidden my-1.5 cursor-pointer">
|
||||
className="relative bg-bunker-700 hover:bg-bunker-500 shadow-xl duration-200 rounded-md border border-dashed border-bunker-400 pl-2 pr-6 py-2 h-[5.5rem] w-full flex items-center justify-between overflow-hidden my-1.5 cursor-pointer"
|
||||
>
|
||||
<div className="flex flex-row items-center mr-4">
|
||||
<FontAwesomeIcon icon={icon} className="text-4xl mx-2 w-16" />
|
||||
{complete &&
|
||||
<div className="bg-bunker-700 w-7 h-7 rounded-full absolute left-11 top-10 p-2 flex items-center justify-center">
|
||||
<FontAwesomeIcon icon={faCheckCircle} className="text-4xl w-5 h-5 text-green" />
|
||||
</div>}
|
||||
{complete && (
|
||||
<div className="bg-bunker-700 w-7 h-7 rounded-full absolute left-12 top-10 p-2 flex items-center justify-center">
|
||||
<FontAwesomeIcon
|
||||
icon={faCheckCircle}
|
||||
className="text-4xl w-5 h-5 text-green"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-col items-start">
|
||||
<div className="text-xl font-semibold mt-0.5">{text}</div>
|
||||
<div className="text-sm font-normal">{subText}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={`pr-4 font-semibold text-sm w-28 text-right ${complete && "text-green"}`}>
|
||||
{complete ? "Complete!" : "About " + time}
|
||||
<div
|
||||
className={`pr-4 font-semibold text-sm w-28 text-right ${
|
||||
complete && 'text-green'
|
||||
}`}
|
||||
>
|
||||
{complete ? 'Complete!' : 'About ' + time}
|
||||
</div>
|
||||
{complete && <div className="absolute bottom-0 left-0 h-1 w-full bg-green"></div>}
|
||||
{complete && (
|
||||
<div className="absolute bottom-0 left-0 h-1 w-full bg-green"></div>
|
||||
)}
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div
|
||||
onClick={async () => {
|
||||
if (userAction) {
|
||||
await registerUserAction({
|
||||
action: userAction
|
||||
})
|
||||
}
|
||||
}}
|
||||
className="relative bg-bunker-700 hover:bg-bunker-500 shadow-xl duration-200 rounded-md border border-dashed border-bunker-400 pl-2 pr-6 py-2 h-[5.5rem] w-full flex items-center justify-between overflow-hidden my-1.5 cursor-pointer">
|
||||
<div
|
||||
onClick={async () => {
|
||||
if (userAction) {
|
||||
await registerUserAction({
|
||||
action: userAction
|
||||
});
|
||||
}
|
||||
}}
|
||||
className="relative bg-bunker-700 hover:bg-bunker-500 shadow-xl duration-200 rounded-md border border-dashed border-bunker-400 pl-2 pr-6 py-2 h-[5.5rem] w-full flex items-center justify-between overflow-hidden my-1.5 cursor-pointer"
|
||||
>
|
||||
<div className="flex flex-row items-center mr-4">
|
||||
<FontAwesomeIcon icon={icon} className="text-4xl mx-2 w-16" />
|
||||
{complete &&
|
||||
<div className="bg-bunker-700 w-7 h-7 rounded-full absolute left-11 top-10">
|
||||
<FontAwesomeIcon icon={faCheckCircle} className="absolute text-4xl left-12 top-16 w-5 h-5 text-green" />
|
||||
</div>}
|
||||
{complete && (
|
||||
<div className="bg-bunker-700 w-7 h-7 rounded-full absolute left-11 top-10">
|
||||
<FontAwesomeIcon
|
||||
icon={faCheckCircle}
|
||||
className="absolute text-4xl left-12 top-16 w-5 h-5 text-green"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-col items-start">
|
||||
<div className="text-xl font-semibold mt-0.5">{text}</div>
|
||||
<div className="text-sm font-normal mt-0.5">{subText}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={`pr-4 font-semibold text-sm w-28 text-right ${complete && "text-green"}`}>
|
||||
{complete ? "Complete!" : "About " + time}
|
||||
<div
|
||||
className={`pr-4 font-semibold text-sm w-28 text-right ${
|
||||
complete && 'text-green'
|
||||
}`}
|
||||
>
|
||||
{complete ? 'Complete!' : 'About ' + time}
|
||||
</div>
|
||||
{complete && <div className="absolute bottom-0 left-0 h-1 w-full bg-green"></div>}
|
||||
{complete && (
|
||||
<div className="absolute bottom-0 left-0 h-1 w-full bg-green"></div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This tab is called Home because in the future it will include some company news,
|
||||
* updates, roadmap, relavant blogs, etc. Currently it only has the setup instruction
|
||||
* This tab is called Home because in the future it will include some company news,
|
||||
* updates, roadmap, relavant blogs, etc. Currently it only has the setup instruction
|
||||
* for the new users
|
||||
*/
|
||||
export default function Home() {
|
||||
@ -94,46 +136,90 @@ export default function Home() {
|
||||
const [hasUserClickedSlack, setHasUserClickedSlack] = useState(false);
|
||||
const [hasUserClickedIntro, setHasUserClickedIntro] = useState(false);
|
||||
const [hasUserStarred, setHasUserStarred] = useState(false);
|
||||
const [hasUserPushedSecrets, setHasUserPushedSecrets] = useState(false);
|
||||
const [usersInOrg, setUsersInOrg] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const checkUserActionsFunction = async () => {
|
||||
const userActionSlack = await checkUserAction({
|
||||
action: "slack_cta_clicked",
|
||||
});
|
||||
setHasUserClickedSlack(userActionSlack ? true : false);
|
||||
|
||||
const userActionIntro = await checkUserAction({
|
||||
action: "intro_cta_clicked",
|
||||
});
|
||||
setHasUserClickedIntro(userActionIntro ? true : false);
|
||||
|
||||
const userActionStar = await checkUserAction({
|
||||
action: "star_cta_clicked",
|
||||
});
|
||||
setHasUserStarred(userActionStar ? true : false);
|
||||
|
||||
const orgId = localStorage.getItem("orgData.id");
|
||||
const orgUsers = await getOrganizationUsers({
|
||||
orgId: orgId ? orgId : "",
|
||||
});
|
||||
setUsersInOrg(orgUsers.length > 1)
|
||||
};
|
||||
checkUserActionsFunction();
|
||||
onboardingCheck({
|
||||
setHasUserClickedIntro,
|
||||
setHasUserClickedSlack,
|
||||
setHasUserPushedSecrets,
|
||||
setHasUserStarred,
|
||||
setUsersInOrg
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="mx-6 lg:mx-0 w-full overflow-y-scroll pt-20 h-screen">
|
||||
<div className="flex flex-col items-center text-gray-300 text-lg mx-auto max-w-2xl lg:max-w-3xl xl:max-w-4xl py-6">
|
||||
<div className="text-3xl font-bold text-left w-full">Your quick start guide</div>
|
||||
<div className="text-md text-left w-full pt-2 pb-4 text-bunker-300">Click on the items below and follow the instructions.</div>
|
||||
{learningItem({ text: "Get to know Infisical", subText: "", complete: hasUserClickedIntro, icon: faHandPeace, time: "3 min", userAction: "intro_cta_clicked", link: "https://www.youtube.com/watch?v=JS3OKYU2078" })}
|
||||
{learningItem({ text: "Add your secrets", subText: "Click to see example secrets, and add your own.", complete: false, icon: faPlus, time: "2 min", userAction: "first_time_secrets_pushed", link: "/dashboard/" + router.query.id })}
|
||||
{learningItem({ text: "Inject secrets locally", subText: "Replace .env files with a more secure an efficient alternative.", complete: false, icon: faNetworkWired, time: "8 min", link: "https://infisical.com/docs/getting-started/quickstart" })}
|
||||
{learningItem({ text: "Integrate Infisical with your infrastructure", subText: "Only a few integrations are currently available. Many more coming soon!", complete: false, icon: faPlug, time: "15 min", link: "https://infisical.com/docs/integrations/overview" })}
|
||||
{learningItem({ text: "Invite your teammates", subText: "", complete: usersInOrg, icon: faUserPlus, time: "2 min", link: "/settings/org/" + router.query.id + "?invite" })}
|
||||
{learningItem({ text: "Join Infisical Slack", subText: "Have any questions? Ask us!", complete: hasUserClickedSlack, icon: faSlack, time: "1 min", userAction: "slack_cta_clicked", link: "https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g" })}
|
||||
{learningItem({ text: "Star Infisical on GitHub", subText: "Like what we're doing? You know what to do! :)", complete: hasUserStarred, icon: faStar, time: "1 min", userAction: "star_cta_clicked", link: "https://github.com/Infisical/infisical" })}
|
||||
<div className="text-3xl font-bold text-left w-full">
|
||||
Your quick start guide
|
||||
</div>
|
||||
<div className="text-md text-left w-full pt-2 pb-4 text-bunker-300">
|
||||
Click on the items below and follow the instructions.
|
||||
</div>
|
||||
{learningItem({
|
||||
text: 'Get to know Infisical',
|
||||
subText: '',
|
||||
complete: hasUserClickedIntro,
|
||||
icon: faHandPeace,
|
||||
time: '3 min',
|
||||
userAction: 'intro_cta_clicked',
|
||||
link: 'https://www.youtube.com/watch?v=JS3OKYU2078'
|
||||
})}
|
||||
{learningItem({
|
||||
text: 'Add your secrets',
|
||||
subText: 'Click to see example secrets, and add your own.',
|
||||
complete: hasUserPushedSecrets,
|
||||
icon: faPlus,
|
||||
time: '2 min',
|
||||
userAction: 'first_time_secrets_pushed',
|
||||
link: '/dashboard/' + router.query.id
|
||||
})}
|
||||
{learningItem({
|
||||
text: 'Inject secrets locally',
|
||||
subText:
|
||||
'Replace .env files with a more secure an efficient alternative.',
|
||||
complete: false,
|
||||
icon: faNetworkWired,
|
||||
time: '8 min',
|
||||
link: 'https://infisical.com/docs/getting-started/quickstart'
|
||||
})}
|
||||
{learningItem({
|
||||
text: 'Integrate Infisical with your infrastructure',
|
||||
subText:
|
||||
'Only a few integrations are currently available. Many more coming soon!',
|
||||
complete: false,
|
||||
icon: faPlug,
|
||||
time: '15 min',
|
||||
link: 'https://infisical.com/docs/integrations/overview'
|
||||
})}
|
||||
{learningItem({
|
||||
text: 'Invite your teammates',
|
||||
subText: '',
|
||||
complete: usersInOrg,
|
||||
icon: faUserPlus,
|
||||
time: '2 min',
|
||||
link: '/settings/org/' + router.query.id + '?invite'
|
||||
})}
|
||||
{learningItem({
|
||||
text: 'Join Infisical Slack',
|
||||
subText: 'Have any questions? Ask us!',
|
||||
complete: hasUserClickedSlack,
|
||||
icon: faSlack,
|
||||
time: '1 min',
|
||||
userAction: 'slack_cta_clicked',
|
||||
link: 'https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g'
|
||||
})}
|
||||
{learningItem({
|
||||
text: 'Star Infisical on GitHub',
|
||||
subText: "Like what we're doing? You know what to do! :)",
|
||||
complete: hasUserStarred,
|
||||
icon: faStar,
|
||||
time: '1 min',
|
||||
userAction: 'star_cta_clicked',
|
||||
link: 'https://github.com/Infisical/infisical'
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
5
frontend/public/images/progress-71.svg
Normal file
5
frontend/public/images/progress-71.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="464" height="464" viewBox="0 0 464 464" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M432 232C432 271.556 420.27 310.224 398.294 343.114C376.318 376.004 345.082 401.638 308.537 416.776C271.992 431.913 231.778 435.874 192.982 428.157C154.186 420.44 118.549 401.392 90.5788 373.422C62.6083 345.451 43.5601 309.815 35.843 271.018C28.1259 232.222 32.0865 192.009 47.2239 155.464C62.3614 118.918 87.9958 87.6827 120.886 65.7063C153.775 43.73 192.443 32.0001 231.999 32" stroke="#67704D" stroke-width="64"/>
|
||||
<path d="M232 32C269.593 32 306.424 42.5951 338.271 62.5703C370.118 82.5454 395.69 111.092 412.055 144.936C428.42 178.779 434.915 216.55 430.796 253.917C426.676 291.283 412.108 326.732 388.764 356.198C365.419 385.664 334.242 407.954 298.809 420.511C263.376 433.069 225.12 435.386 188.43 427.197C151.741 419.007 118.102 400.643 91.3713 374.21C64.641 347.776 45.9018 314.345 37.3027 277.749" stroke="#BADC58" stroke-width="64" stroke-linecap="round"/>
|
||||
<path d="M159.444 261L182.91 214.523V214.153H155.466V202.818H197.455V214.239L173.904 261H159.444ZM232.079 202.818V261H218.045V215.943H217.704L204.692 223.898V211.739L219.039 202.818H232.079ZM277.526 250.091V247.023C277.526 244.636 278.037 242.43 279.06 240.403C280.102 238.377 281.607 236.748 283.577 235.517C285.547 234.286 287.952 233.67 290.793 233.67C293.691 233.67 296.125 234.286 298.094 235.517C300.083 236.729 301.579 238.348 302.583 240.375C303.606 242.383 304.117 244.598 304.117 247.023V250.091C304.117 252.477 303.606 254.684 302.583 256.71C301.56 258.737 300.054 260.366 298.066 261.597C296.077 262.828 293.653 263.443 290.793 263.443C287.914 263.443 285.49 262.828 283.52 261.597C281.551 260.366 280.054 258.737 279.032 256.71C278.028 254.684 277.526 252.477 277.526 250.091ZM286.589 247.023V250.091C286.589 251.303 286.882 252.468 287.469 253.585C288.075 254.703 289.183 255.261 290.793 255.261C292.441 255.261 293.549 254.722 294.117 253.642C294.685 252.544 294.969 251.36 294.969 250.091V247.023C294.969 245.754 294.695 244.561 294.145 243.443C293.615 242.307 292.498 241.739 290.793 241.739C289.202 241.739 288.104 242.307 287.498 243.443C286.892 244.561 286.589 245.754 286.589 247.023ZM247.356 216.795V213.727C247.356 211.303 247.876 209.087 248.918 207.08C249.979 205.053 251.494 203.434 253.464 202.222C255.433 200.991 257.82 200.375 260.623 200.375C263.52 200.375 265.954 200.991 267.924 202.222C269.912 203.434 271.418 205.053 272.441 207.08C273.464 209.087 273.975 211.303 273.975 213.727V216.795C273.975 219.22 273.454 221.436 272.412 223.443C271.39 225.451 269.884 227.051 267.895 228.244C265.907 229.437 263.482 230.034 260.623 230.034C257.763 230.034 255.339 229.428 253.35 228.216C251.38 227.004 249.884 225.394 248.861 223.386C247.857 221.379 247.356 219.182 247.356 216.795ZM256.503 213.727V216.795C256.503 218.064 256.797 219.248 257.384 220.347C257.99 221.426 259.07 221.966 260.623 221.966C262.289 221.966 263.397 221.426 263.947 220.347C264.515 219.248 264.799 218.064 264.799 216.795V213.727C264.799 212.458 264.534 211.265 264.003 210.148C263.473 209.011 262.346 208.443 260.623 208.443C259.032 208.443 257.943 209.021 257.356 210.176C256.787 211.331 256.503 212.515 256.503 213.727ZM251.645 261L291.645 202.818H299.401L259.401 261H251.645Z" fill="#BADC58"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.2 KiB |
Reference in New Issue
Block a user