mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 23:47:06 +00:00
Merge branch 'main' into inf-compare-secrets
This commit is contained in:
@@ -187,10 +187,10 @@ const Layout = ({ children }: LayoutProps) => {
|
||||
});
|
||||
const userWorkspaces = orgUserProjects;
|
||||
if (
|
||||
userWorkspaces.length === 0 &&
|
||||
router.asPath !== '/noprojects' &&
|
||||
!router.asPath.includes('home') &&
|
||||
!router.asPath.includes('settings') ||
|
||||
(userWorkspaces.length === 0 &&
|
||||
router.asPath !== '/noprojects' &&
|
||||
!router.asPath.includes('home') &&
|
||||
!router.asPath.includes('settings')) ||
|
||||
router.asPath === '/dashboard/undefined'
|
||||
) {
|
||||
router.push('/noprojects');
|
||||
@@ -246,16 +246,15 @@ const Layout = ({ children }: LayoutProps) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="fixed w-full md:block flex flex-col h-screen">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/alpinejs/3.2.2/cdn.js" defer />
|
||||
<div className="flex h-screen w-full flex-col overflow-x-hidden">
|
||||
<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 className="flex flex-col justify-between items-between h-full">
|
||||
<div className="flex flex-grow flex-col overflow-y-hidden md:flex-row">
|
||||
<aside className="w-full border-r border-mineshaft-500 bg-bunker-600 md:w-60">
|
||||
<nav className="items-between flex h-full flex-col justify-between">
|
||||
{/* <div className="py-6"></div> */}
|
||||
<div>
|
||||
<div className="flex justify-center w-full mt-[4.5rem] mb-6 bg-bunker-600 h-20 flex-col items-center px-4">
|
||||
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide">
|
||||
<div className="mt-6 mb-6 flex h-20 w-full flex-col items-center justify-center bg-bunker-600 px-4">
|
||||
<div className="ml-1 mb-1 self-start text-xs font-semibold tracking-wide text-gray-400">
|
||||
{t('nav:menu.project')}
|
||||
</div>
|
||||
{Object.keys(workspaceMapping).length > 0 ? (
|
||||
@@ -279,29 +278,29 @@ const Layout = ({ children }: LayoutProps) => {
|
||||
<ul>
|
||||
{Object.keys(workspaceMapping).length > 0 &&
|
||||
menuItems.map(({ href, title, emoji }) => (
|
||||
<li className="mt-0.5 mx-2" key={title}>
|
||||
<li className="mx-2 mt-0.5" 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" />
|
||||
<p className="w-6 ml-4 mr-2 flex items-center justify-center text-lg">
|
||||
<div className="relative flex cursor-pointer rounded bg-primary-50/10 px-0.5 py-2.5 text-sm text-white">
|
||||
<div className="absolute inset-0 top-0 my-1 ml-1 mr-1 w-1 rounded-xl bg-primary" />
|
||||
<p className="ml-4 mr-2 flex w-6 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>
|
||||
<div className="flex rounded p-2.5 text-sm text-white">
|
||||
<p className="flex w-10 items-center justify-center text-lg">{emoji}</p>
|
||||
{title}
|
||||
</div>
|
||||
) : (
|
||||
<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">
|
||||
<div className="flex cursor-pointer rounded p-2.5 text-sm text-white hover:bg-primary-50/5">
|
||||
<p className="flex w-10 items-center justify-center text-lg">
|
||||
{emoji}
|
||||
</p>
|
||||
{title}
|
||||
@@ -312,11 +311,11 @@ const Layout = ({ children }: LayoutProps) => {
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="w-full mt-40 mb-4 px-2">
|
||||
<div className="mt-40 mb-4 w-full 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" />
|
||||
<p className="w-6 ml-4 mr-2 flex items-center justify-center text-lg">
|
||||
<div className="relative flex cursor-pointer rounded bg-primary-50/10 px-0.5 py-2.5 text-sm text-white">
|
||||
<div className="absolute inset-0 top-0 my-1 ml-1 mr-1 w-1 rounded-xl bg-primary" />
|
||||
<p className="ml-4 mr-2 flex w-6 items-center justify-center text-lg">
|
||||
<FontAwesomeIcon icon={faBookOpen} />
|
||||
</p>
|
||||
Infisical Guide
|
||||
@@ -336,8 +335,8 @@ const Layout = ({ children }: LayoutProps) => {
|
||||
</div>
|
||||
) : (
|
||||
<Link href={`/home/${workspaceMapping[workspaceSelected as any]}`}>
|
||||
<div className="relative flex p-2.5 overflow-visible text-white h-10 text-sm rounded cursor-pointer bg-white/10 hover:bg-primary-50/[0.15] mt-max">
|
||||
<p className="w-10 flex items-center justify-center text-lg">
|
||||
<div className="mt-max relative flex h-10 cursor-pointer overflow-visible rounded bg-white/10 p-2.5 text-sm text-white hover:bg-primary-50/[0.15]">
|
||||
<p className="flex w-10 items-center justify-center text-lg">
|
||||
<FontAwesomeIcon icon={faBookOpen} />
|
||||
</p>
|
||||
Infisical Guide
|
||||
@@ -369,12 +368,12 @@ const Layout = ({ children }: LayoutProps) => {
|
||||
error={error}
|
||||
loading={loading}
|
||||
/>
|
||||
<main className="flex-1 bg-bunker-800">{children}</main>
|
||||
<main className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800">{children}</main>
|
||||
</div>
|
||||
</div>
|
||||
<div className="md:hidden bg-bunker-800 w-screen h-screen flex flex-col justify-center items-center">
|
||||
<FontAwesomeIcon icon={faMobile} className="text-gray-300 text-7xl mb-8" />
|
||||
<p className="text-gray-200 px-6 text-center text-lg max-w-sm">
|
||||
<div className="flex h-screen w-screen flex-col items-center justify-center bg-bunker-800 md:hidden">
|
||||
<FontAwesomeIcon icon={faMobile} className="mb-8 text-7xl text-gray-300" />
|
||||
<p className="max-w-sm px-6 text-center text-lg text-gray-200">
|
||||
{` ${t('common:no-mobile')} `}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -41,7 +41,6 @@ const EnvironmentTable = ({ data = [], onCreateEnv, onDeleteEnv, onUpdateEnv }:
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
|
||||
const onEnvCreateCB = async (env: Env) => {
|
||||
try {
|
||||
await onCreateEnv(env);
|
||||
@@ -71,14 +70,14 @@ const EnvironmentTable = ({ data = [], onCreateEnv, onDeleteEnv, onUpdateEnv }:
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-row justify-between w-full">
|
||||
<div className="flex flex-col w-full">
|
||||
<p className="text-xl font-semibold mb-3">Project Environments</p>
|
||||
<p className="text-base text-gray-400 mb-4">
|
||||
<div className="flex w-full flex-row justify-between">
|
||||
<div className="flex w-full flex-col">
|
||||
<p className="mb-3 text-xl font-semibold">Project Environments</p>
|
||||
<p className="mb-4 text-base text-gray-400">
|
||||
Choose which environments will show up in your dashboard like development, staging,
|
||||
production
|
||||
</p>
|
||||
<p className="text-sm mr-1 text-gray-500 self-start">
|
||||
<p className="mr-1 self-start text-sm text-gray-500">
|
||||
Note: the text in slugs shows how these environmant should be accessed in CLI.
|
||||
</p>
|
||||
</div>
|
||||
@@ -86,10 +85,10 @@ const EnvironmentTable = ({ data = [], onCreateEnv, onDeleteEnv, onUpdateEnv }:
|
||||
<Button
|
||||
text="Add New Env"
|
||||
onButtonPressed={() => {
|
||||
if (plan !== plans.starter || host !== "https://app.infisical.com") {
|
||||
handlePopUpOpen('createUpdateEnv')
|
||||
if (plan !== plans.starter || host !== 'https://app.infisical.com') {
|
||||
handlePopUpOpen('createUpdateEnv');
|
||||
} else {
|
||||
handlePopUpOpen('upgradePlan')
|
||||
handlePopUpOpen('upgradePlan');
|
||||
}
|
||||
}}
|
||||
color="mineshaft"
|
||||
@@ -98,32 +97,30 @@ const EnvironmentTable = ({ data = [], onCreateEnv, onDeleteEnv, onUpdateEnv }:
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="table-container w-full bg-bunker rounded-md mb-6 border border-mineshaft-700 relative mt-1">
|
||||
<div className="absolute rounded-t-md w-full h-12 bg-white/5" />
|
||||
<table className="w-full my-1">
|
||||
<div className="table-container relative mb-6 mt-1 w-full rounded-md border border-mineshaft-700 bg-bunker">
|
||||
<div className="absolute h-12 w-full rounded-t-md bg-white/5" />
|
||||
<table className="my-1 w-full">
|
||||
<thead className="text-bunker-300">
|
||||
<tr>
|
||||
<th className="text-left pl-6 pt-2.5 pb-2">Name</th>
|
||||
<th className="text-left pl-6 pt-2.5 pb-2">Slug</th>
|
||||
<th className="pl-6 pt-2.5 pb-2 text-left">Name</th>
|
||||
<th className="pl-6 pt-2.5 pb-2 text-left">Slug</th>
|
||||
<th aria-label="buttons" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data?.length > 0 ? (
|
||||
data.map(({ name, slug }) => (
|
||||
<tr key={name} className="bg-bunker-800 hover:bg-bunker-800/5 duration-100">
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{name}
|
||||
</td>
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">{slug}</td>
|
||||
<td className="py-2 border-mineshaft-700 border-t flex justify-end">
|
||||
<div className="hover:opacity-100 duration-200 flex items-center mr-2">
|
||||
<tr key={name} className="bg-bunker-800 duration-100 hover:bg-bunker-800/5">
|
||||
<td className="border-t border-mineshaft-700 py-2 pl-6 text-gray-300">{name}</td>
|
||||
<td className="border-t border-mineshaft-700 py-2 pl-6 text-gray-300">{slug}</td>
|
||||
<td className="flex justify-end border-t border-mineshaft-700 py-2">
|
||||
<div className="mr-2 flex items-center duration-200 hover:opacity-100">
|
||||
<Button
|
||||
onButtonPressed={() => {
|
||||
if (plan !== plans.starter || host !== "https://app.infisical.com") {
|
||||
handlePopUpOpen('createUpdateEnv', { name, slug })
|
||||
if (plan !== plans.starter || host !== 'https://app.infisical.com') {
|
||||
handlePopUpOpen('createUpdateEnv', { name, slug });
|
||||
} else {
|
||||
handlePopUpOpen('upgradePlan')
|
||||
handlePopUpOpen('upgradePlan');
|
||||
}
|
||||
}}
|
||||
color="mineshaft"
|
||||
@@ -131,13 +128,13 @@ const EnvironmentTable = ({ data = [], onCreateEnv, onDeleteEnv, onUpdateEnv }:
|
||||
icon={faPencil}
|
||||
/>
|
||||
</div>
|
||||
<div className="opacity-50 hover:opacity-100 duration-200 flex items-center mr-6">
|
||||
<div className="mr-6 flex items-center opacity-50 duration-200 hover:opacity-100">
|
||||
<Button
|
||||
onButtonPressed={() => {
|
||||
if (plan !== plans.starter || host !== "https://app.infisical.com") {
|
||||
handlePopUpOpen('deleteEnv', { name, slug })
|
||||
if (plan !== plans.starter || host !== 'https://app.infisical.com') {
|
||||
handlePopUpOpen('deleteEnv', { name, slug });
|
||||
} else {
|
||||
handlePopUpOpen('upgradePlan')
|
||||
handlePopUpOpen('upgradePlan');
|
||||
}
|
||||
}}
|
||||
color="red"
|
||||
@@ -150,8 +147,8 @@ const EnvironmentTable = ({ data = [], onCreateEnv, onDeleteEnv, onUpdateEnv }:
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan={4} className="text-center pt-7 pb-4 text-bunker-400">
|
||||
No environmants found
|
||||
<td colSpan={4} className="pt-7 pb-4 text-center text-bunker-400">
|
||||
No environments found
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
@@ -174,7 +171,7 @@ const EnvironmentTable = ({ data = [], onCreateEnv, onDeleteEnv, onUpdateEnv }:
|
||||
onCreateSubmit={onEnvCreateCB}
|
||||
onEditSubmit={onEnvUpdateCB}
|
||||
/>
|
||||
<UpgradePlanModal
|
||||
<UpgradePlanModal
|
||||
isOpen={popUp.upgradePlan.isOpen}
|
||||
onClose={() => handlePopUpClose('upgradePlan')}
|
||||
text="You can add custom environments if you switch to Infisical's Team plan."
|
||||
|
||||
@@ -81,7 +81,7 @@ const SideBar = ({
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="absolute border-l border-mineshaft-500 bg-bunker h-full w-96 top-14 right-0 z-40 shadow-xl flex flex-col justify-between">
|
||||
<div className="absolute border-l border-mineshaft-500 bg-bunker h-full w-96 right-0 z-40 shadow-xl flex flex-col justify-between">
|
||||
{isLoading ? (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<Image
|
||||
|
||||
@@ -93,7 +93,7 @@ export default function Navbar() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="absolute flex flex-row justify-between w-full bg-bunker text-white border-b border-mineshaft-500 z-50">
|
||||
<div className="flex flex-row justify-between w-full bg-bunker text-white border-b border-mineshaft-500 z-50">
|
||||
<div className="m-auto flex justify-start items-center mx-4">
|
||||
<div className="flex flex-row items-center">
|
||||
<div className="flex justify-center py-4">
|
||||
|
||||
@@ -3,8 +3,8 @@ import { useRouter } from 'next/router';
|
||||
import { faAngleRight } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
|
||||
import { useWorkspace } from '@app/context';
|
||||
import getOrganization from '@app/pages/api/organization/GetOrg';
|
||||
import getProjectInfo from '@app/pages/api/workspace/getProjectInfo';
|
||||
|
||||
/**
|
||||
* This is the component at the top of almost every page.
|
||||
@@ -23,9 +23,9 @@ export default function NavHeader({
|
||||
isProjectRelated?: boolean;
|
||||
}): JSX.Element {
|
||||
const [orgName, setOrgName] = useState('');
|
||||
const [workspaceName, setWorkspaceName] = useState('');
|
||||
const router = useRouter();
|
||||
const projectId = String(router.query.id);
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
@@ -34,29 +34,24 @@ export default function NavHeader({
|
||||
orgId: orgId || ''
|
||||
});
|
||||
setOrgName(org.name);
|
||||
|
||||
const workspace = await getProjectInfo({
|
||||
projectId
|
||||
});
|
||||
setWorkspaceName(workspace.name);
|
||||
})();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [projectId]);
|
||||
|
||||
return (
|
||||
<div className="pt-20 ml-6 flex flex-row items-center">
|
||||
<div className="bg-primary-900 h-6 w-6 rounded-md flex items-center justify-center text-mineshaft-100 mr-2">
|
||||
<div className="ml-6 flex flex-row items-center pt-8">
|
||||
<div className="mr-2 flex h-6 w-6 items-center justify-center rounded-md bg-primary-900 text-mineshaft-100">
|
||||
{orgName?.charAt(0)}
|
||||
</div>
|
||||
<div className="text-primary text-sm font-semibold">{orgName}</div>
|
||||
<div className="text-sm font-semibold text-primary">{orgName}</div>
|
||||
{isProjectRelated && (
|
||||
<>
|
||||
<FontAwesomeIcon icon={faAngleRight} className="ml-3 text-sm text-gray-400 mr-3" />
|
||||
<div className="font-semibold text-primary text-sm">{workspaceName}</div>
|
||||
<FontAwesomeIcon icon={faAngleRight} className="ml-3 mr-3 text-sm text-gray-400" />
|
||||
<div className="text-sm font-semibold text-primary">{currentWorkspace?.name}</div>
|
||||
</>
|
||||
)}
|
||||
<FontAwesomeIcon icon={faAngleRight} className="ml-3 text-sm text-gray-400 mr-3" />
|
||||
<div className="text-gray-400 text-sm">{pageName}</div>
|
||||
<FontAwesomeIcon icon={faAngleRight} className="ml-3 mr-3 text-sm text-gray-400" />
|
||||
<div className="text-sm text-gray-400">{pageName}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,14 +18,15 @@ const buttonVariants = cva(
|
||||
'font-inter font-medium',
|
||||
'cursor-pointer',
|
||||
'inline-flex items-center justify-center',
|
||||
'relative'
|
||||
'relative',
|
||||
'whitespace-nowrap'
|
||||
],
|
||||
{
|
||||
variants: {
|
||||
colorSchema: {
|
||||
primary: ['bg-primary', 'text-black', 'border-primary hover:bg-opacity-80'],
|
||||
secondary: ['bg-mineshaft', 'text-gray-300', 'border-mineshaft hover:bg-opacity-80'],
|
||||
danger: ['bg-red', 'text-white', 'border-red']
|
||||
danger: ['bg-red', 'text-white', 'border-red hover:bg-opacity-90']
|
||||
},
|
||||
variant: {
|
||||
solid: '',
|
||||
@@ -33,7 +34,7 @@ const buttonVariants = cva(
|
||||
plain: ''
|
||||
},
|
||||
isDisabled: {
|
||||
true: 'bg-opacity-70 cursor-not-allowed',
|
||||
true: 'bg-mineshaft opacity-40 cursor-not-allowed',
|
||||
false: ''
|
||||
},
|
||||
isFullWidth: {
|
||||
@@ -45,9 +46,9 @@ const buttonVariants = cva(
|
||||
false: ''
|
||||
},
|
||||
size: {
|
||||
xs: ['text-xs', 'py-1', 'px-2'],
|
||||
sm: ['text-sm', 'py-2', 'px-4'],
|
||||
md: ['text-md', 'py-2', 'px-6'],
|
||||
xs: ['text-xs', 'py-1', 'px-1'],
|
||||
sm: ['text-sm', 'py-2', 'px-2'],
|
||||
md: ['text-md', 'py-2', 'px-4'],
|
||||
lg: ['text-lg', 'py-2', 'px-8']
|
||||
}
|
||||
},
|
||||
@@ -75,7 +76,7 @@ const buttonVariants = cva(
|
||||
{
|
||||
colorSchema: 'secondary',
|
||||
variant: 'plain',
|
||||
className: 'text-mineshaft'
|
||||
className: 'text-mineshaft-300'
|
||||
},
|
||||
{
|
||||
colorSchema: 'danger',
|
||||
@@ -101,7 +102,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
children,
|
||||
isDisabled = false,
|
||||
className = '',
|
||||
size = 'md',
|
||||
size = 'sm',
|
||||
variant = 'solid',
|
||||
isFullWidth,
|
||||
isRounded = true,
|
||||
@@ -144,9 +145,10 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
)}
|
||||
<span
|
||||
className={twMerge(
|
||||
'transition-all shrink-0 cursor-pointer',
|
||||
'shrink-0 cursor-pointer transition-all',
|
||||
loadingToggleClass,
|
||||
size === 'xs' ? 'mr-1' : 'mr-2'
|
||||
leftIcon && 'ml-2',
|
||||
size === 'xs' ? 'mr-1' : 'mr-3'
|
||||
)}
|
||||
>
|
||||
{leftIcon}
|
||||
@@ -154,7 +156,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
<span className={twMerge('transition-all', loadingToggleClass)}>{children}</span>
|
||||
<span
|
||||
className={twMerge(
|
||||
'transition-all shrink-0 cursor-pointer',
|
||||
'shrink-0 cursor-pointer transition-all',
|
||||
loadingToggleClass,
|
||||
size === 'xs' ? 'ml-1' : 'ml-2'
|
||||
)}
|
||||
|
||||
@@ -8,7 +8,7 @@ export type CardTitleProps = {
|
||||
};
|
||||
|
||||
export const CardTitle = ({ children, className, subTitle }: CardTitleProps) => (
|
||||
<div className={twMerge('p-6 pb-4 font-sans text-xl font-medium', className)}>
|
||||
<div className={twMerge('px-6 py-4 font-sans text-xl font-medium', className)}>
|
||||
{children}
|
||||
{subTitle && <p className="py-1 text-sm font-normal text-gray-400">{subTitle}</p>}
|
||||
</div>
|
||||
@@ -29,7 +29,7 @@ export type CardBodyProps = {
|
||||
};
|
||||
|
||||
export const CardBody = ({ children, className }: CardBodyProps) => (
|
||||
<div className={twMerge('p-6 pt-0', className)}>{children}</div>
|
||||
<div className={twMerge('px-6 pb-6 pt-0', className)}>{children}</div>
|
||||
);
|
||||
|
||||
export type CardProps = {
|
||||
@@ -47,7 +47,7 @@ export const Card = forwardRef<HTMLDivElement, CardProps>(
|
||||
<div
|
||||
ref={ref}
|
||||
className={twMerge(
|
||||
'flex flex-col w-full font-inter text-gray-200 bg-mineshaft-700 shadow-md',
|
||||
'flex w-full flex-col bg-mineshaft-800 font-inter text-gray-200 shadow-md',
|
||||
isFullHeight && 'h-full',
|
||||
isRounded && 'rounded-md',
|
||||
isPlain && 'shadow-none',
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { useToggle } from '@app/hooks';
|
||||
|
||||
import { Button } from '../Button';
|
||||
import { FormControl } from '../FormControl';
|
||||
import { Input } from '../Input';
|
||||
import { Modal, ModalContent } from '../Modal';
|
||||
|
||||
type Props = {
|
||||
isOpen?: boolean;
|
||||
onClose?: () => void;
|
||||
onChange?: (isOpen: boolean) => void;
|
||||
deleteKey: string;
|
||||
title: string;
|
||||
onDeleteApproved: () => Promise<void>;
|
||||
};
|
||||
|
||||
export const DeleteActionModal = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
onChange,
|
||||
deleteKey,
|
||||
onDeleteApproved,
|
||||
title
|
||||
}: Props): JSX.Element => {
|
||||
const [inputData, setInputData] = useState('');
|
||||
const [isLoading, setIsLoading] = useToggle();
|
||||
|
||||
useEffect(() => {
|
||||
setInputData('');
|
||||
}, [isOpen]);
|
||||
|
||||
const onDelete = async () => {
|
||||
setIsLoading.on();
|
||||
try {
|
||||
await onDeleteApproved();
|
||||
} catch {
|
||||
setIsLoading.off();
|
||||
} finally {
|
||||
setIsLoading.off();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onOpenChange={(isOpenState) => {
|
||||
setInputData('');
|
||||
if (onChange) onChange(isOpenState);
|
||||
}}
|
||||
>
|
||||
<ModalContent
|
||||
title={title}
|
||||
subTitle="This action is irreversible!!!"
|
||||
footerContent={
|
||||
<div className="flex items-center">
|
||||
<Button
|
||||
className="mr-4"
|
||||
colorSchema="danger"
|
||||
isDisabled={!(deleteKey === inputData) || isLoading}
|
||||
onClick={onDelete}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
<Button variant="plain" colorSchema="secondary" onClick={onClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
onClose={onClose}
|
||||
>
|
||||
<form>
|
||||
<FormControl
|
||||
label={
|
||||
<div className="pb-2 text-sm">
|
||||
Type <span className="font-bold">{deleteKey}</span> to delete the resource
|
||||
</div>
|
||||
}
|
||||
className="mb-4"
|
||||
>
|
||||
<Input value={inputData} onChange={(e) => setInputData(e.target.value)} />
|
||||
</FormControl>
|
||||
</form>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { DeleteActionModal } from './DeleteActionModal';
|
||||
@@ -11,7 +11,7 @@ export type FormLabelProps = {
|
||||
};
|
||||
|
||||
export const FormLabel = ({ id, label, isRequired }: FormLabelProps) => (
|
||||
<Label.Root className="text-mineshaft-300 text-sm font-medium block mb-1 ml-0.5" htmlFor={id}>
|
||||
<Label.Root className="mb-1 ml-0.5 block text-sm font-medium text-mineshaft-300" htmlFor={id}>
|
||||
{label}
|
||||
{isRequired && <span className="ml-1 text-red">*</span>}
|
||||
</Label.Root>
|
||||
@@ -25,7 +25,7 @@ export type FormHelperTextProps = {
|
||||
export const FormHelperText = ({ isError, text }: FormHelperTextProps) => (
|
||||
<div
|
||||
className={twMerge(
|
||||
'text-xs font-inter flex items-center opacity-90 text-mineshaft-300 mt-2',
|
||||
'mt-2 flex items-center font-inter text-xs text-mineshaft-300 opacity-90',
|
||||
isError && 'text-red-600'
|
||||
)}
|
||||
>
|
||||
@@ -46,6 +46,7 @@ export type FormControlProps = {
|
||||
helperText?: ReactNode;
|
||||
errorText?: ReactNode;
|
||||
children: JSX.Element;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const FormControl = ({
|
||||
@@ -55,10 +56,11 @@ export const FormControl = ({
|
||||
helperText,
|
||||
errorText,
|
||||
id,
|
||||
isError
|
||||
isError,
|
||||
className
|
||||
}: FormControlProps): JSX.Element => {
|
||||
return (
|
||||
<div>
|
||||
<div className={twMerge('mb-4', className)}>
|
||||
{typeof label === 'string' ? (
|
||||
<FormLabel label={label} isRequired={isRequired} id={id} />
|
||||
) : (
|
||||
|
||||
@@ -14,7 +14,7 @@ const iconButtonVariants = cva(
|
||||
[
|
||||
'button',
|
||||
'transition-all',
|
||||
'font-inter font-medium',
|
||||
'font-inter font-medium user-select-none',
|
||||
'cursor-pointer',
|
||||
'inline-flex items-center justify-center',
|
||||
'relative'
|
||||
@@ -22,9 +22,9 @@ const iconButtonVariants = cva(
|
||||
{
|
||||
variants: {
|
||||
colorSchema: {
|
||||
primary: ['bg-primary', 'text-black', 'border-primary hover:bg-opacity-80'],
|
||||
secondary: ['bg-mineshaft', 'text-gray-300', 'border-mineshaft hover:bg-opacity-80'],
|
||||
danger: ['bg-red', 'text-white', 'border-red']
|
||||
primary: ['bg-primary', 'text-black', 'border-primary hover:opacity-80'],
|
||||
secondary: ['bg-mineshaft', 'text-gray-300', 'border-mineshaft hover:bg-bunker-400'],
|
||||
danger: ['bg-[#973939] hover:bg-red', 'text-white', 'border-red']
|
||||
},
|
||||
variant: {
|
||||
solid: '',
|
||||
@@ -40,10 +40,10 @@ const iconButtonVariants = cva(
|
||||
false: ''
|
||||
},
|
||||
size: {
|
||||
xs: ['text-xs', 'py-1', 'px-2'],
|
||||
sm: ['text-sm', 'py-2', 'px-3'],
|
||||
md: ['text-md', 'py-3', 'px-4'],
|
||||
lg: ['text-lg', 'py-3', 'px-6']
|
||||
xs: ['text-xs', 'py-2', 'px-2'],
|
||||
sm: ['text-sm', 'py-3', 'px-3'],
|
||||
md: ['text-md', 'py-4', 'px-4'],
|
||||
lg: ['text-lg', 'py-6', 'px-6']
|
||||
}
|
||||
},
|
||||
compoundVariants: [
|
||||
@@ -97,7 +97,7 @@ export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
|
||||
ariaLabel,
|
||||
isDisabled = false,
|
||||
className,
|
||||
size = 'md',
|
||||
size = 'sm',
|
||||
variant = 'solid',
|
||||
isRounded = true,
|
||||
colorSchema = 'primary',
|
||||
|
||||
@@ -8,10 +8,11 @@ type Props = {
|
||||
isRequired?: boolean;
|
||||
leftIcon?: ReactNode;
|
||||
rightIcon?: ReactNode;
|
||||
isDisabled?: boolean;
|
||||
};
|
||||
|
||||
const inputVariants = cva(
|
||||
'input w-full py-2 text-gray-400 placeholder-gray-500 placeholder-opacity-50',
|
||||
'input w-full py-2 text-gray-400 placeholder-gray-500 placeholder-opacity-50 outline-none focus:ring-2',
|
||||
{
|
||||
variants: {
|
||||
size: {
|
||||
@@ -46,7 +47,7 @@ const inputParentContainerVariants = cva('inline-flex font-inter items-center bo
|
||||
},
|
||||
isError: {
|
||||
true: 'border-red',
|
||||
false: 'border-mineshaft-400'
|
||||
false: 'border-mineshaft-500'
|
||||
},
|
||||
isFullWidth: {
|
||||
true: 'w-full',
|
||||
@@ -70,6 +71,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
className,
|
||||
isRounded = true,
|
||||
isFullWidth = true,
|
||||
isDisabled,
|
||||
isError = false,
|
||||
isRequired,
|
||||
leftIcon,
|
||||
@@ -87,9 +89,10 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
{...props}
|
||||
required={isRequired}
|
||||
ref={ref}
|
||||
disabled={isDisabled}
|
||||
className={twMerge(
|
||||
leftIcon ? 'pl-9' : 'pl-4',
|
||||
rightIcon ? 'pr-9' : 'pr-4',
|
||||
leftIcon ? 'pl-9' : 'pl-2.5',
|
||||
rightIcon ? 'pr-9' : 'pr-2.5',
|
||||
inputVariants({ className, isError, size, isRounded, variant })
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -11,33 +11,34 @@ export type ModalContentProps = DialogPrimitive.DialogContentProps & {
|
||||
title?: ReactNode;
|
||||
subTitle?: string;
|
||||
footerContent?: ReactNode;
|
||||
onClose?: () => void;
|
||||
};
|
||||
|
||||
export const ModalContent = forwardRef<HTMLDivElement, ModalContentProps>(
|
||||
({ children, title, subTitle, className, footerContent, ...props }, forwardedRef) => (
|
||||
({ children, title, subTitle, className, footerContent, onClose, ...props }, forwardedRef) => (
|
||||
<DialogPrimitive.Portal>
|
||||
<DialogPrimitive.Overlay
|
||||
className="fixed inset-0 w-full h-full animate-fadeIn"
|
||||
style={{ backgroundColor: 'rgba(0, 0, 0, 0.5)' }}
|
||||
className="fixed inset-0 h-full w-full animate-fadeIn"
|
||||
style={{ backgroundColor: 'rgba(0, 0, 0, 0.7)' }}
|
||||
/>
|
||||
<DialogPrimitive.Content {...props} ref={forwardedRef}>
|
||||
<Card
|
||||
isRounded
|
||||
className={twMerge(
|
||||
'fixed max-w-md animate-popIn top-1/2 left-1/2 -translate-y-2/4 -translate-x-2/4',
|
||||
'fixed top-1/2 left-1/2 max-w-lg -translate-y-2/4 -translate-x-2/4 animate-popIn drop-shadow-md',
|
||||
className
|
||||
)}
|
||||
>
|
||||
{title && <CardTitle subTitle={subTitle}>{title}</CardTitle>}
|
||||
<CardBody>{children}</CardBody>
|
||||
{footerContent && <CardFooter>{footerContent}</CardFooter>}
|
||||
<DialogPrimitive.Close aria-label="Close" asChild>
|
||||
<DialogPrimitive.Close aria-label="Close" asChild onClick={onClose}>
|
||||
<IconButton
|
||||
variant="plain"
|
||||
ariaLabel="close"
|
||||
className="absolute top-2.5 right-2.5 text-white hover:bg-gray-600 rounded"
|
||||
className="absolute top-3 right-3 rounded text-white hover:bg-gray-600"
|
||||
>
|
||||
<FontAwesomeIcon icon={faTimes} size="sm" className="cursor-pointer" />
|
||||
<FontAwesomeIcon icon={faTimes} size="lg" className="cursor-pointer" />
|
||||
</IconButton>
|
||||
</DialogPrimitive.Close>
|
||||
</Card>
|
||||
@@ -55,3 +56,6 @@ export const Modal = ({ isOpen, ...props }: ModalProps) => (
|
||||
|
||||
export const ModalTrigger = DialogPrimitive.Trigger;
|
||||
export type ModalTriggerProps = DialogPrimitive.DialogTriggerProps;
|
||||
|
||||
export const ModalClose = DialogPrimitive.Close;
|
||||
export type ModalCloseProps = DialogPrimitive.DialogCloseProps;
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export type { ModalContentProps, ModalProps, ModalTriggerProps } from './Modal';
|
||||
export { Modal, ModalContent, ModalTrigger } from './Modal';
|
||||
export type { ModalCloseProps, ModalContentProps, ModalProps, ModalTriggerProps } from './Modal';
|
||||
export { Modal, ModalClose, ModalContent, ModalTrigger } from './Modal';
|
||||
|
||||
@@ -10,34 +10,40 @@ type Props = {
|
||||
children: ReactNode;
|
||||
placeholder?: string;
|
||||
className?: string;
|
||||
dropdownContainerClassName?: string;
|
||||
isLoading?: boolean;
|
||||
};
|
||||
|
||||
export type SelectProps = SelectPrimitive.SelectProps & Props;
|
||||
|
||||
export const Select = forwardRef<HTMLButtonElement, SelectProps>(
|
||||
({ children, placeholder, className, isLoading, ...props }, ref): JSX.Element => {
|
||||
(
|
||||
{ children, placeholder, className, isLoading, dropdownContainerClassName, ...props },
|
||||
ref
|
||||
): JSX.Element => {
|
||||
return (
|
||||
<SelectPrimitive.Root {...props}>
|
||||
<SelectPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={twMerge(
|
||||
`inline-flex items-center justify-between data-[placeholder]:text-gray-500
|
||||
px-3 py-2 font-inter text-sm text-bunker-200 font-normal rounded-md bg-mineshaft-800 outline-none`,
|
||||
`inline-flex items-center justify-between rounded-md
|
||||
bg-bunker-800 px-3 py-2 font-inter text-sm font-normal text-bunker-200 outline-none data-[placeholder]:text-gray-500`,
|
||||
className
|
||||
)}
|
||||
>
|
||||
<SelectPrimitive.Value placeholder={placeholder} />
|
||||
{!props.disabled && <SelectPrimitive.Icon className="ml-3">
|
||||
<FontAwesomeIcon icon={faChevronDown} size="sm" />
|
||||
</SelectPrimitive.Icon>}
|
||||
{!props.disabled && (
|
||||
<SelectPrimitive.Icon className="ml-3">
|
||||
<FontAwesomeIcon icon={faChevronDown} size="sm" />
|
||||
</SelectPrimitive.Icon>
|
||||
)}
|
||||
</SelectPrimitive.Trigger>
|
||||
<SelectPrimitive.Portal>
|
||||
<SelectPrimitive.Content
|
||||
// position="popper"
|
||||
sideOffset={4}
|
||||
className="overflow-hidden text-bunker-100 rounded-md shadow-md font-inter bg-mineshaft-800"
|
||||
style={{ width: 'var(--radix-select-trigger-width) + 6' }}
|
||||
className={twMerge(
|
||||
'relative left-4 top-1 overflow-hidden rounded-md bg-bunker-800 font-inter text-bunker-100 shadow-md',
|
||||
dropdownContainerClassName
|
||||
)}
|
||||
>
|
||||
<SelectPrimitive.ScrollUpButton>
|
||||
<FontAwesomeIcon icon={faChevronUp} size="sm" />
|
||||
@@ -75,11 +81,11 @@ export const SelectItem = forwardRef<HTMLDivElement, SelectItemProps>(
|
||||
<SelectPrimitive.Item
|
||||
{...props}
|
||||
className={twMerge(
|
||||
`text-sm rounded-sm transition-all hover:bg-mineshaft-500
|
||||
flex items-center pl-10 pr-4 py-2 cursor-pointer rounded-md
|
||||
select-none outline-none relative`,
|
||||
`relative flex cursor-pointer
|
||||
select-none items-center rounded-md py-2 pl-10 pr-4 text-sm
|
||||
outline-none transition-all hover:bg-mineshaft-500`,
|
||||
isSelected && 'bg-primary',
|
||||
isDisabled && 'text-gray-600 hover:bg-transparent cursor-not-allowed hover:text-gray-600',
|
||||
isDisabled && 'cursor-not-allowed text-gray-600 hover:bg-transparent hover:text-gray-600',
|
||||
className
|
||||
)}
|
||||
ref={forwardedRef}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { HTMLAttributes, ReactNode, TdHTMLAttributes } from 'react';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export type TableContainerProps = {
|
||||
@@ -14,7 +14,7 @@ export const TableContainer = ({
|
||||
}: TableContainerProps): JSX.Element => (
|
||||
<div
|
||||
className={twMerge(
|
||||
'overflow-x-auto font-inter shadow-md relative border border-solid border-mineshaft-700',
|
||||
'relative w-full overflow-x-auto border border-solid border-mineshaft-700 font-inter shadow-md',
|
||||
isRounded && 'rounded-md',
|
||||
className
|
||||
)}
|
||||
@@ -32,7 +32,7 @@ export type TableProps = {
|
||||
export const Table = ({ children, className }: TableProps): JSX.Element => (
|
||||
<table
|
||||
className={twMerge(
|
||||
'bg-bunker-800 p-2 roun text-gray-300 w-full text-sm text-left rounded-md',
|
||||
'w-full rounded rounded-md bg-bunker-800 p-2 text-left text-sm text-gray-300',
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -47,7 +47,7 @@ export type THeadProps = {
|
||||
};
|
||||
|
||||
export const THead = ({ children, className }: THeadProps): JSX.Element => (
|
||||
<thead className={twMerge('text-xs bg-bunker text-bunker-300 uppercase', className)}>
|
||||
<thead className={twMerge('bg-bunker text-xs uppercase text-bunker-300', className)}>
|
||||
{children}
|
||||
</thead>
|
||||
);
|
||||
@@ -56,20 +56,22 @@ export const THead = ({ children, className }: THeadProps): JSX.Element => (
|
||||
export type TrProps = {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
} & HTMLAttributes<HTMLTableRowElement>;
|
||||
|
||||
export const Tr = ({ children, className }: TrProps): JSX.Element => (
|
||||
<tr className={twMerge('border border-solid border-mineshaft-700', className)}>{children}</tr>
|
||||
export const Tr = ({ children, className, ...props }: TrProps): JSX.Element => (
|
||||
<tr className={twMerge('border border-solid border-mineshaft-700 hover:bg-bunker-700', className)} {...props}>
|
||||
{children}
|
||||
</tr>
|
||||
);
|
||||
|
||||
// table head columns
|
||||
export type ThProps = {
|
||||
children: ReactNode;
|
||||
children?: ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const Th = ({ children, className }: ThProps): JSX.Element => (
|
||||
<th className={twMerge('px-6 py-3 font-medium', className)}>{children}</th>
|
||||
<th className={twMerge('px-5 pt-4 pb-3.5 font-medium font-semibold bg-bunker-500', className)}>{children}</th>
|
||||
);
|
||||
|
||||
// table body
|
||||
@@ -84,10 +86,12 @@ export const TBody = ({ children, className }: TBodyProps): JSX.Element => (
|
||||
|
||||
// table body columns
|
||||
export type TdProps = {
|
||||
children: ReactNode;
|
||||
children?: ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
} & TdHTMLAttributes<HTMLTableCellElement>;
|
||||
|
||||
export const Td = ({ children, className }: TdProps): JSX.Element => (
|
||||
<td className={twMerge('text-left px-6 py-3', className)}>{children}</td>
|
||||
export const Td = ({ children, className, ...props }: TdProps): JSX.Element => (
|
||||
<td className={twMerge('px-5 py-2 text-left', className)} {...props}>
|
||||
{children}
|
||||
</td>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
import { Button } from '../Button';
|
||||
import { Modal, ModalClose, ModalContent } from '../Modal';
|
||||
|
||||
type Props = {
|
||||
isOpen?: boolean;
|
||||
onOpenChange?: (isOpen: boolean) => void;
|
||||
text: string;
|
||||
};
|
||||
|
||||
export const UpgradePlanModal = ({ text, isOpen, onOpenChange }: Props): JSX.Element => (
|
||||
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
|
||||
<ModalContent
|
||||
title="Unleash Infisical's Full Power"
|
||||
footerContent={[
|
||||
<Link
|
||||
href={`/settings/billing/${localStorage.getItem('projectData.id') as string}`}
|
||||
key="upgrade-plan"
|
||||
>
|
||||
<Button className="mr-4">Upgrade Plan</Button>
|
||||
</Link>,
|
||||
<ModalClose asChild key="upgrade-plan-cancel">
|
||||
<Button colorSchema="secondary" variant="plain">
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalClose>
|
||||
]}
|
||||
>
|
||||
<p className="mb-4 text-bunker-300">{text}</p>
|
||||
<p className="font-medium text-bunker-300">
|
||||
Upgrade and get access to this, as well as to other powerful enhancements.
|
||||
</p>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
@@ -0,0 +1 @@
|
||||
export { UpgradePlanModal } from './UpgradePlanModal';
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from './Button';
|
||||
export * from './Card';
|
||||
export * from './Checkbox';
|
||||
export * from './DeleteActionModal';
|
||||
export * from './Dropdown';
|
||||
export * from './FormControl';
|
||||
export * from './IconButton';
|
||||
@@ -12,3 +13,4 @@ export * from './Spinner';
|
||||
export * from './Switch';
|
||||
export * from './Table';
|
||||
export * from './TextArea';
|
||||
export * from './UpgradePlanModal';
|
||||
|
||||
Reference in New Issue
Block a user