mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Added a quick start guide for new users
This commit is contained in:
@@ -3,8 +3,9 @@ import { useEffect, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import {
|
||||
faBookOpen,
|
||||
faGear,
|
||||
faHouse,
|
||||
faKey,
|
||||
faMobile,
|
||||
faPlug,
|
||||
faUser,
|
||||
@@ -114,7 +115,7 @@ export default function Layout({ children }) {
|
||||
href:
|
||||
"/dashboard/" + workspaceMapping[workspaceSelected] + "?Development",
|
||||
title: "Secrets",
|
||||
emoji: <FontAwesomeIcon icon={faHouse} />,
|
||||
emoji: <FontAwesomeIcon icon={faKey} />,
|
||||
},
|
||||
{
|
||||
href: "/users/" + workspaceMapping[workspaceSelected],
|
||||
@@ -214,66 +215,88 @@ export default function Layout({ children }) {
|
||||
<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">
|
||||
<nav>
|
||||
<div className="py-6"></div>
|
||||
<div className="flex justify-center w-full mt-7 mb-8 bg-bunker-600 w-full h-full flex flex-col items-center px-4">
|
||||
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide">
|
||||
PROJECT
|
||||
<nav className="flex flex-col justify-between items-between h-full">
|
||||
{/* <div className="py-6"></div> */}
|
||||
<div>
|
||||
<div className="flex justify-center w-full mt-[4.5rem] mb-6 bg-bunker-600 w-full h-20 flex flex-col items-center px-4">
|
||||
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide">
|
||||
PROJECT
|
||||
</div>
|
||||
{workspaceList.length > 0 ? (
|
||||
<Listbox
|
||||
selected={workspaceSelected}
|
||||
onChange={setWorkspaceSelected}
|
||||
data={workspaceList}
|
||||
buttonAction={openModal}
|
||||
text=""
|
||||
workspaceMapping={workspaceMapping}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
text="Add Project"
|
||||
onButtonPressed={openModal}
|
||||
color="mineshaft"
|
||||
size="md"
|
||||
icon={faPlus}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{workspaceList.length > 0 ? (
|
||||
<Listbox
|
||||
selected={workspaceSelected}
|
||||
onChange={setWorkspaceSelected}
|
||||
data={workspaceList}
|
||||
buttonAction={openModal}
|
||||
text=""
|
||||
workspaceMapping={workspaceMapping}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
text="Add Project"
|
||||
onButtonPressed={openModal}
|
||||
color="mineshaft"
|
||||
size="md"
|
||||
icon={faPlus}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<ul>
|
||||
{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("/")[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`}
|
||||
>
|
||||
<div className="absolute top-0 my-1 ml-1 inset-0 bg-primary w-1 rounded-xl mr-1"></div>
|
||||
<p className="w-6 ml-4 mr-2 flex items-center justify-center text-lg">{emoji}</p>
|
||||
{title}
|
||||
</div>
|
||||
) : router.asPath == "/noprojects" ? (
|
||||
<div className={`flex p-2.5 text-white text-sm rounded`}>
|
||||
<p className="w-10 flex items-center justify-center text-lg">{emoji}</p>
|
||||
{title}
|
||||
</div>
|
||||
) : (
|
||||
<Link href={href}>
|
||||
<ul>
|
||||
{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("/")[2] === href.split("/")[2]
|
||||
: true) ? (
|
||||
<div
|
||||
className={`flex p-2.5 text-white text-sm rounded cursor-pointer hover:bg-primary-50/5`}
|
||||
className={`flex relative px-0.5 py-2.5 text-white text-sm rounded cursor-pointer bg-primary-50/10`}
|
||||
>
|
||||
<div className="absolute top-0 my-1 ml-1 inset-0 bg-primary w-1 rounded-xl mr-1"></div>
|
||||
<p className="w-6 ml-4 mr-2 flex items-center justify-center text-lg">{emoji}</p>
|
||||
{title}
|
||||
</div>
|
||||
) : router.asPath == "/noprojects" ? (
|
||||
<div className={`flex p-2.5 text-white text-sm rounded`}>
|
||||
<p className="w-10 flex items-center justify-center text-lg">{emoji}</p>
|
||||
{title}
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<Link href={href}>
|
||||
<div
|
||||
className={`flex p-2.5 text-white text-sm rounded cursor-pointer hover:bg-primary-50/5`}
|
||||
>
|
||||
<p className="w-10 flex items-center justify-center text-lg">{emoji}</p>
|
||||
{title}
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="w-full mt-40 mb-4 px-2">
|
||||
{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`}
|
||||
>
|
||||
<div className="absolute top-0 my-1 ml-1 inset-0 bg-primary w-1 rounded-xl mr-1"></div>
|
||||
<p className="w-6 ml-4 mr-2 flex items-center justify-center text-lg"><FontAwesomeIcon icon={faBookOpen}/></p>
|
||||
Infisical Guide
|
||||
</div>
|
||||
) : (
|
||||
<Link href={`/home/` + workspaceMapping[workspaceSelected]}>
|
||||
<div
|
||||
className={`flex p-2.5 text-white text-sm rounded cursor-pointer hover:bg-primary-50/5 mt-max border border-dashed border-bunker-400`}
|
||||
>
|
||||
<p className="w-10 flex items-center justify-center text-lg"><FontAwesomeIcon icon={faBookOpen}/></p>
|
||||
Infisical Guide
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</nav>
|
||||
</aside>
|
||||
<AddWorkspaceDialog
|
||||
|
||||
@@ -61,7 +61,7 @@ export default function Plan({ plan }) {
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<a href="/pricing" target='_blank rel="noopener"'>
|
||||
<a href="https://infisical.com/pricing" target='_blank rel="noopener"'>
|
||||
<div className="relative z-10 text-gray-400 font-semibold hover:text-primary duration-200 cursor-pointer mb-0.5">
|
||||
{plan.buttonTextSecondary}
|
||||
</div>
|
||||
|
||||
@@ -2,13 +2,13 @@ import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
/**
|
||||
* This route lets us get all the users in an org.
|
||||
* @param {*} req
|
||||
* @param {*} res
|
||||
* @param {object} obj
|
||||
* @param {string} obj.orgId - organization Id
|
||||
* @returns
|
||||
*/
|
||||
const getOrganizationUsers = (req, res) => {
|
||||
const getOrganizationUsers = ({ orgId }) => {
|
||||
return SecurityClient.fetchCall(
|
||||
"/api/v1/organization/" + req.orgId + "/users",
|
||||
"/api/v1/organization/" + orgId + "/users",
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
|
||||
135
frontend/pages/home/[id].tsx
Normal file
135
frontend/pages/home/[id].tsx
Normal file
@@ -0,0 +1,135 @@
|
||||
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, 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";
|
||||
|
||||
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 => {
|
||||
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
|
||||
})
|
||||
}
|
||||
}}
|
||||
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>}
|
||||
<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>
|
||||
{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 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>}
|
||||
<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>
|
||||
{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
|
||||
* for the new users
|
||||
*/
|
||||
export default function Home() {
|
||||
const router = useRouter();
|
||||
const [hasUserClickedSlack, setHasUserClickedSlack] = useState(false);
|
||||
const [hasUserClickedIntro, setHasUserClickedIntro] = useState(false);
|
||||
const [usersInOrg, setUsersInOrg] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const checkUserActionsFunction = async () => {
|
||||
let userActionSlack = await checkUserAction({
|
||||
action: "slack_cta_clicked",
|
||||
});
|
||||
setHasUserClickedSlack(userActionSlack ? true : false);
|
||||
|
||||
let userActionIntro = await checkUserAction({
|
||||
action: "intro_cta_clicked",
|
||||
});
|
||||
setHasUserClickedIntro(userActionIntro ? true : false);
|
||||
|
||||
let orgId = localStorage.getItem("orgData.id");
|
||||
const orgUsers = await getOrganizationUsers({
|
||||
orgId: orgId ? orgId : "",
|
||||
});
|
||||
setUsersInOrg(orgUsers.length > 1)
|
||||
};
|
||||
checkUserActionsFunction();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="mx-6 lg:mx-0 w-full overflow-y-scroll pt-28 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">
|
||||
<div className="text-3xl font-bold text-left w-full pt-6">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: "12 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" })}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Home.requireAuth = true;
|
||||
@@ -495,7 +495,9 @@ export default function SignUp() {
|
||||
setBackupKeyError,
|
||||
setBackupKeyIssued,
|
||||
});
|
||||
router.push("/dashboard/");
|
||||
const userWorkspaces = await getWorkspaces();
|
||||
let userWorkspace = userWorkspaces[0]._id;
|
||||
router.push("/home/" + userWorkspace);
|
||||
}}
|
||||
size="lg"
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user