diff --git a/apps/api/src/common/workspace.ts b/apps/api/src/common/workspace.ts index 45d2d152f..01c17400d 100644 --- a/apps/api/src/common/workspace.ts +++ b/apps/api/src/common/workspace.ts @@ -64,7 +64,7 @@ export const createWorkspace = async ( slug: workspaceAdminRoleSlug, authorities: [Authority.WORKSPACE_ADMIN], hasAdminAuthority: true, - colorCode: '#FF0000' + colorCode: '#FB2C36' } ] } diff --git a/apps/platform/package.json b/apps/platform/package.json index 48852fea2..6f4bea06b 100644 --- a/apps/platform/package.json +++ b/apps/platform/package.json @@ -28,7 +28,7 @@ "@radix-ui/react-select": "^2.1.2", "@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-slider": "^1.3.5", - "@radix-ui/react-slot": "^1.2.0", + "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-switch": "^1.0.3", "@radix-ui/react-tabs": "^1.1.12", "@radix-ui/react-tooltip": "^1.1.2", @@ -41,6 +41,7 @@ "dayjs": "^1.11.11", "dotenv": "^16.4.7", "eccrypto": "^1.1.6", + "emblor": "^1.4.8", "emoji-picker-react": "^4.12.0", "env-cmd": "^10.1.0", "framer-motion": "^11.1.7", @@ -57,7 +58,7 @@ "next-themes": "^0.4.6", "posthog-js": "^1.240.5", "posthog-node": "^4.17.1", - "radix-ui": "^1.4.2", + "radix-ui": "^1.4.3", "react": "18.3.1", "react-dom": "18.3.1", "sonner": "^1.4.41", diff --git a/apps/platform/public/svg/roles/index.ts b/apps/platform/public/svg/roles/index.ts new file mode 100644 index 000000000..cd52d7ddf --- /dev/null +++ b/apps/platform/public/svg/roles/index.ts @@ -0,0 +1,3 @@ +import PermissionBadgeSVG from './permissionBadge.svg' + +export { PermissionBadgeSVG } diff --git a/apps/platform/public/svg/roles/permissionBadge.svg b/apps/platform/public/svg/roles/permissionBadge.svg new file mode 100644 index 000000000..6d8d785f4 --- /dev/null +++ b/apps/platform/public/svg/roles/permissionBadge.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/apps/platform/src/app/(main)/(project)/[workspace]/[project]/layout.tsx b/apps/platform/src/app/(main)/(project)/[workspace]/[project]/layout.tsx index 6cb4b52bd..1251fa8e5 100644 --- a/apps/platform/src/app/(main)/(project)/[workspace]/[project]/layout.tsx +++ b/apps/platform/src/app/(main)/(project)/[workspace]/[project]/layout.tsx @@ -2,10 +2,10 @@ import { useEffect } from 'react' import { useParams, useSearchParams } from 'next/navigation' import { useAtom, useAtomValue, useSetAtom } from 'jotai' -import VariablePage from './@variable/page' -import SecretPage from './@secret/page' -import EnvironmentPage from './@environment/page' import OverviewPage from './@overview/page' +import EnvironmentPage from './@environment/page' +import SecretPage from './@secret/page' +import VariablePage from './@variable/page' import ControllerInstance from '@/lib/controller-instance' import AddSecretDialog from '@/components/dashboard/secret/addSecretDialogue' import { diff --git a/apps/platform/src/app/(main)/layout.tsx b/apps/platform/src/app/(main)/layout.tsx index 8f40bfa56..d2ac8b252 100644 --- a/apps/platform/src/app/(main)/layout.tsx +++ b/apps/platform/src/app/(main)/layout.tsx @@ -10,7 +10,7 @@ export default function AppLayout({ return (
-
+
{children}
diff --git a/apps/platform/src/app/(main)/members/layout.tsx b/apps/platform/src/app/(main)/members/layout.tsx index 28eb518aa..0c8b6e8ae 100644 --- a/apps/platform/src/app/(main)/members/layout.tsx +++ b/apps/platform/src/app/(main)/members/layout.tsx @@ -2,8 +2,8 @@ import React, { useEffect, Suspense } from 'react' import { useAtomValue, useSetAtom } from 'jotai' import { useSearchParams } from 'next/navigation' -import JoinedMemberPage from './@joined/page' import InvitedMemberPage from './@invited/page' +import JoinedMemberPage from './@joined/page' import MembersHeader from '@/components/members/membersHeader' import ControllerInstance from '@/lib/controller-instance' import { useHttp } from '@/hooks/use-http' diff --git a/apps/platform/src/app/(main)/roles/page.tsx b/apps/platform/src/app/(main)/roles/page.tsx index 0be21bf32..7a3bca32d 100644 --- a/apps/platform/src/app/(main)/roles/page.tsx +++ b/apps/platform/src/app/(main)/roles/page.tsx @@ -6,7 +6,11 @@ import CreateRoleDialog from '@/components/roles/createRoleDialog' import RoleList from '@/components/roles/rolesList' import ConfirmDeleteRole from '@/components/roles/confirmDeleteRole' import EditRoleSheet from '@/components/roles/editRoleSheet' -import { editRoleOpenAtom, selectedRoleAtom, selectedWorkspaceAtom } from '@/store' +import { + editRoleOpenAtom, + selectedRoleAtom, + selectedWorkspaceAtom +} from '@/store' import { PageTitle } from '@/components/common/page-title' function RolesPage(): React.JSX.Element { @@ -14,6 +18,8 @@ function RolesPage(): React.JSX.Element { const selectedRole = useAtomValue(selectedRoleAtom) const isEditRolesOpen = useAtomValue(editRoleOpenAtom) + const shouldShowEditRoleSheet = isEditRolesOpen && selectedRole + return (
@@ -25,7 +31,7 @@ function RolesPage(): React.JSX.Element { {/* Edit role sheet */} - {isEditRolesOpen && selectedRole ? : null} + {shouldShowEditRoleSheet ? : null}
) } diff --git a/apps/platform/src/app/global.css b/apps/platform/src/app/global.css index 1a0ba36e1..85a99b776 100644 --- a/apps/platform/src/app/global.css +++ b/apps/platform/src/app/global.css @@ -3,7 +3,7 @@ @tailwind utilities; body { - @apply bg-[#0b0d0f] text-white; + @apply bg-[#000203] text-slate-200; } /* For WebKit Browsers such as Chrome, Safari */ @@ -52,7 +52,8 @@ body { } @keyframes highlight { - 0%, 100% { + 0%, + 100% { border: transparent; } 50% { @@ -62,4 +63,4 @@ body { .animate-highlight { animation: highlight 1s ease-in-out 2; -} \ No newline at end of file +} diff --git a/apps/platform/src/components/auth/account-details/onboarding-stepper.tsx b/apps/platform/src/components/auth/account-details/onboarding-stepper.tsx index 89f83fa22..a1637fcff 100644 --- a/apps/platform/src/components/auth/account-details/onboarding-stepper.tsx +++ b/apps/platform/src/components/auth/account-details/onboarding-stepper.tsx @@ -5,10 +5,10 @@ import { toast } from 'sonner' import { posthog } from 'posthog-js' import { z } from 'zod' import { LoadingSVG } from '@public/svg/shared' -import ReferralDetailsForm from './onboarding slides/referral-details-form' -import ProfileDetailsForm from './onboarding slides/profile-details-form' -import TeamOverviewForm from './onboarding slides/team-overview-form' import RoleIndustryForm from './onboarding slides/role-industry-form' +import TeamOverviewForm from './onboarding slides/team-overview-form' +import ProfileDetailsForm from './onboarding slides/profile-details-form' +import ReferralDetailsForm from './onboarding slides/referral-details-form' import { userAtom } from '@/store' import { Button } from '@/components/ui/button' import ControllerInstance from '@/lib/controller-instance' @@ -153,7 +153,9 @@ export default function OnboardingStepper() {
- diff --git a/apps/platform/src/components/dashboard/overview/ImportEnv/index.tsx b/apps/platform/src/components/dashboard/overview/ImportEnv/index.tsx index c17bddcc2..72e352cef 100644 --- a/apps/platform/src/components/dashboard/overview/ImportEnv/index.tsx +++ b/apps/platform/src/components/dashboard/overview/ImportEnv/index.tsx @@ -39,7 +39,7 @@ function ImportEnvButton({ projectSlug }): React.JSX.Element { className="flex w-fit px-3 py-4 text-sm" onClick={handleOpenModal} type="button" - variant="secondary" + variant="primary" > Import diff --git a/apps/platform/src/components/dashboard/overview/LocalKeySetup/index.tsx b/apps/platform/src/components/dashboard/overview/LocalKeySetup/index.tsx index 331c68eaa..8f13975d2 100644 --- a/apps/platform/src/components/dashboard/overview/LocalKeySetup/index.tsx +++ b/apps/platform/src/components/dashboard/overview/LocalKeySetup/index.tsx @@ -27,10 +27,10 @@ function LocalKeySetup({ return (
-

+

Do you wanna setup private key?{' '} diff --git a/apps/platform/src/components/dashboard/overview/RegenerateKeySetup/index.tsx b/apps/platform/src/components/dashboard/overview/RegenerateKeySetup/index.tsx index 17a60fcfc..112cc94cb 100644 --- a/apps/platform/src/components/dashboard/overview/RegenerateKeySetup/index.tsx +++ b/apps/platform/src/components/dashboard/overview/RegenerateKeySetup/index.tsx @@ -71,9 +71,9 @@ function RegenerateKeySetup({ }, [projectKeys, onRegenerated]) return ( -
+
-

+

Do you wanna regenerate private key?{' '} diff --git a/apps/platform/src/components/dashboard/overview/ServerKeySetup/index.tsx b/apps/platform/src/components/dashboard/overview/ServerKeySetup/index.tsx index d3a5aa912..b35e047ae 100644 --- a/apps/platform/src/components/dashboard/overview/ServerKeySetup/index.tsx +++ b/apps/platform/src/components/dashboard/overview/ServerKeySetup/index.tsx @@ -55,10 +55,10 @@ function ServerKeySetup({ return (
-

+

Share project private key with us?{' '} diff --git a/apps/platform/src/components/dashboard/project/createProjectDialogue/index.tsx b/apps/platform/src/components/dashboard/project/createProjectDialogue/index.tsx index ad15f876b..b2dd2c209 100644 --- a/apps/platform/src/components/dashboard/project/createProjectDialogue/index.tsx +++ b/apps/platform/src/components/dashboard/project/createProjectDialogue/index.tsx @@ -3,11 +3,11 @@ import { useAtom, useAtomValue } from 'jotai' import { Plus } from 'lucide-react' import { AddSVG } from '@public/svg/shared' import ViewAndDownloadProjectKeysDialog from '../viewAndDownloadKeysDialog' -import CreateProjectName from './create-project-name' -import CreateProjectDescription from './create-project-description' -import CreateProjectAccessLevel from './create-project-access-level' -import CreateProjectStorePrivateKey from './create-project-store-private-key' import CreateProjectEnvironmentList from './create-project-environment-list' +import CreateProjectStorePrivateKey from './create-project-store-private-key' +import CreateProjectAccessLevel from './create-project-access-level' +import CreateProjectDescription from './create-project-description' +import CreateProjectName from './create-project-name' import { Button } from '@/components/ui/button' import { Dialog, @@ -103,8 +103,9 @@ export default function CreateProjectDialogue(): JSX.Element { )} diff --git a/apps/platform/src/components/dashboard/project/exportProjectConfigurations/index.tsx b/apps/platform/src/components/dashboard/project/exportProjectConfigurations/index.tsx index d19a257b8..1da6b1577 100644 --- a/apps/platform/src/components/dashboard/project/exportProjectConfigurations/index.tsx +++ b/apps/platform/src/components/dashboard/project/exportProjectConfigurations/index.tsx @@ -1,6 +1,6 @@ -import ExportProjectFormatInput from './export-project-format-input' -import ExportProjectEnvironmentInput from './export-project-environment-input' import ExportProjectPrivateKeyInput from './export-project-private-key-input' +import ExportProjectEnvironmentInput from './export-project-environment-input' +import ExportProjectFormatInput from './export-project-format-input' import { Dialog, DialogContent, diff --git a/apps/platform/src/components/dashboard/secret/addSecretDialogue/index.tsx b/apps/platform/src/components/dashboard/secret/addSecretDialogue/index.tsx index c5a75eb6f..914376eff 100644 --- a/apps/platform/src/components/dashboard/secret/addSecretDialogue/index.tsx +++ b/apps/platform/src/components/dashboard/secret/addSecretDialogue/index.tsx @@ -2,8 +2,6 @@ import React, { useCallback, useState } from 'react' import { toast } from 'sonner' import { useAtom, useAtomValue, useSetAtom } from 'jotai' import { AddSVG } from '@public/svg/shared' -import { Input } from '../../../ui/input' -import { Button } from '../../../ui/button' import { Dialog, DialogContent, @@ -12,6 +10,8 @@ import { DialogTitle, DialogTrigger } from '../../../ui/dialog' +import { Button } from '../../../ui/button' +import { Input } from '../../../ui/input' import ControllerInstance from '@/lib/controller-instance' import { createSecretOpenAtom, diff --git a/apps/platform/src/components/dashboard/secret/emptySecretListSection/index.tsx b/apps/platform/src/components/dashboard/secret/emptySecretListSection/index.tsx index cc73c7596..33bce7977 100644 --- a/apps/platform/src/components/dashboard/secret/emptySecretListSection/index.tsx +++ b/apps/platform/src/components/dashboard/secret/emptySecretListSection/index.tsx @@ -23,9 +23,10 @@ export default function EmptySecretListContent(): React.JSX.Element {

diff --git a/apps/platform/src/components/integrations/IntegrationDetails/index.tsx b/apps/platform/src/components/integrations/IntegrationDetails/index.tsx index 5c99d5193..2f0acf7b4 100644 --- a/apps/platform/src/components/integrations/IntegrationDetails/index.tsx +++ b/apps/platform/src/components/integrations/IntegrationDetails/index.tsx @@ -1,7 +1,7 @@ import React, { useCallback } from 'react' -import { EditTwoSVG, TrashWhiteSVG } from '@public/svg/shared' import type { Integration } from '@keyshade/schema' import { useSetAtom } from 'jotai' +import { EditTwoSVG, TrashWhiteSVG } from '@public/svg/shared' import IntegrationIcon from '../integrationIcon' import { formatDate, formatText } from '@/lib/utils' import { Button } from '@/components/ui/button' diff --git a/apps/platform/src/components/integrations/createIntegration/index.tsx b/apps/platform/src/components/integrations/createIntegration/index.tsx index 28f5dc158..9ecfafd69 100644 --- a/apps/platform/src/components/integrations/createIntegration/index.tsx +++ b/apps/platform/src/components/integrations/createIntegration/index.tsx @@ -4,8 +4,8 @@ import { useAtomValue, useSetAtom } from 'jotai' import { ArrowLeftRight, CheckSquare2 } from 'lucide-react' import { Integrations } from '@keyshade/common' import { KeyshadeBigSVG } from '@public/svg/auth' -import IntegrationIcon from '../integrationIcon' import SetupIntegration from '../integrationSetup' +import IntegrationIcon from '../integrationIcon' import { Button } from '@/components/ui/button' import { Dialog, diff --git a/apps/platform/src/components/integrations/integrationList/index.tsx b/apps/platform/src/components/integrations/integrationList/index.tsx index aa3ae8706..7498794df 100644 --- a/apps/platform/src/components/integrations/integrationList/index.tsx +++ b/apps/platform/src/components/integrations/integrationList/index.tsx @@ -2,8 +2,8 @@ import { useAtom, useAtomValue, useSetAtom } from 'jotai' import React, { useCallback, useEffect, useState } from 'react' import type { Integration } from '@keyshade/schema' import { useRouter } from 'next/navigation' -import IntegrationIcon from '../integrationIcon' import EmptyIntegration from '../emptyIntegration' +import IntegrationIcon from '../integrationIcon' import { integrationsOfWorkspaceAtom, selectedIntegrationAtom, diff --git a/apps/platform/src/components/integrations/integrationServices/index.tsx b/apps/platform/src/components/integrations/integrationServices/index.tsx index bc6d9362e..b45a5e10d 100644 --- a/apps/platform/src/components/integrations/integrationServices/index.tsx +++ b/apps/platform/src/components/integrations/integrationServices/index.tsx @@ -3,8 +3,8 @@ import React, { useCallback, useMemo } from 'react' import { Integrations } from '@keyshade/common' import { useAtom, useAtomValue, useSetAtom } from 'jotai' import type { Integration } from '@keyshade/schema' -import IntegrationIcon from '../integrationIcon' import CreateIntegration from '../createIntegration' +import IntegrationIcon from '../integrationIcon' import { Button } from '@/components/ui/button' import { createIntegrationOpenAtom, diff --git a/apps/platform/src/components/integrations/integrationSetup/index.tsx b/apps/platform/src/components/integrations/integrationSetup/index.tsx index ff6a378b4..95dcf1afa 100644 --- a/apps/platform/src/components/integrations/integrationSetup/index.tsx +++ b/apps/platform/src/components/integrations/integrationSetup/index.tsx @@ -3,8 +3,8 @@ import React, { useState } from 'react' import type { IntegrationTypeEnum } from '@keyshade/schema' import { toast } from 'sonner' import { useSetAtom } from 'jotai' -import ProjectEnvironmentInput from '../projectEnvironmentInput' import ProjectEnvironmentSelect from '../projectEnvironmentSelect' +import ProjectEnvironmentInput from '../projectEnvironmentInput' import { Input } from '@/components/ui/input' import { Button } from '@/components/ui/button' import IntegrationMetadata from '@/components/integrations/integrationMetadata' diff --git a/apps/platform/src/components/integrations/updateIntegrationSheet/index.tsx b/apps/platform/src/components/integrations/updateIntegrationSheet/index.tsx index b470f2fa4..b704ab097 100644 --- a/apps/platform/src/components/integrations/updateIntegrationSheet/index.tsx +++ b/apps/platform/src/components/integrations/updateIntegrationSheet/index.tsx @@ -5,9 +5,9 @@ import { useAtom } from 'jotai' import type { EventTypeEnum } from '@keyshade/schema' import type { VercelEnvironmentMapping } from '@keyshade/common' import { Integrations } from '@keyshade/common' -import ProjectEnvironmentInput from '../projectEnvironmentInput' -import UpdateKeyMapping from '../updateKeymapping' import UpdateEnvironment from '../updateEnvironment' +import UpdateKeyMapping from '../updateKeymapping' +import ProjectEnvironmentInput from '../projectEnvironmentInput' import { Input } from '@/components/ui/input' import { Button } from '@/components/ui/button' import { diff --git a/apps/platform/src/components/main/emptyProjectState/index.tsx b/apps/platform/src/components/main/emptyProjectState/index.tsx index 86a675ed9..9c2a4ebf3 100644 --- a/apps/platform/src/components/main/emptyProjectState/index.tsx +++ b/apps/platform/src/components/main/emptyProjectState/index.tsx @@ -1,6 +1,6 @@ -import { FolderIconSVG } from '@public/svg/dashboard' import { useSetAtom } from 'jotai' import React from 'react' +import { FolderIconSVG } from '@public/svg/dashboard' import { createProjectOpenAtom } from '@/store' import { Button } from '@/components/ui/button' diff --git a/apps/platform/src/components/members/memberRow/index.tsx b/apps/platform/src/components/members/memberRow/index.tsx index 87306c17c..fd7548e0f 100644 --- a/apps/platform/src/components/members/memberRow/index.tsx +++ b/apps/platform/src/components/members/memberRow/index.tsx @@ -17,6 +17,7 @@ import { } from '@/components/ui/tooltip' import AvatarComponent from '@/components/common/avatar' import { Button } from '@/components/ui/button' +import RoleBadge from '@/components/common/role-badge' function MemberRow({ member, @@ -74,10 +75,14 @@ function MemberRow({ {dayjs(member.user.joinedOn).format('MMM D, YYYY')} - -
- {member.roles[0].role.name} -
+ + {member.roles.map((role) => { + return ( + + {role.role.name} + + ) + })}
diff --git a/apps/platform/src/components/members/membersHeader/index.tsx b/apps/platform/src/components/members/membersHeader/index.tsx index 0392015e0..f2a5e2765 100644 --- a/apps/platform/src/components/members/membersHeader/index.tsx +++ b/apps/platform/src/components/members/membersHeader/index.tsx @@ -4,6 +4,7 @@ import { ChevronDown } from 'lucide-react' import { useAtomValue, useSetAtom } from 'jotai' import { toast } from 'sonner' import { AddSVG } from '@public/svg/shared' +import type { WorkspaceMember } from '@keyshade/schema' import { Button } from '@/components/ui/button' import { Dialog, @@ -22,11 +23,9 @@ import { } from '@/store' import { useHttp } from '@/hooks/use-http' import ControllerInstance from '@/lib/controller-instance' +import RoleBadge from '@/components/common/role-badge' -interface SelectedRoles { - name: string - roleSlug: string -} +type SelectedRoles = WorkspaceMember['roles'][number]['role'] export default function MembersHeader(): React.JSX.Element { const [email, setEmail] = useState('') @@ -44,9 +43,9 @@ export default function MembersHeader(): React.JSX.Element { const toggleRole = (role: SelectedRoles): void => { setSelectedRoles((prev) => { - const isSelected = prev.some((r) => r.roleSlug === role.roleSlug) + const isSelected = prev.some((r) => r.slug === role.slug) if (isSelected) { - return prev.filter((r) => r.roleSlug !== role.roleSlug) + return prev.filter((r) => r.slug !== role.slug) } return [...prev, role] }) @@ -58,7 +57,7 @@ export default function MembersHeader(): React.JSX.Element { members: [ { email, - roleSlugs: selectedRoles.map((role) => role.roleSlug) + roleSlugs: selectedRoles.map((role) => role.slug) } ] }) @@ -72,7 +71,7 @@ export default function MembersHeader(): React.JSX.Element { }, []) const handleInviteMembers = useCallback(async () => { - if (email.trim() === '') { + if (email.length === 0) { toast.error('Email is required') return } @@ -133,10 +132,11 @@ export default function MembersHeader(): React.JSX.Element { type="email" value={email} /> + {/* */} @@ -159,12 +159,12 @@ export default function MembersHeader(): React.JSX.Element { ) : ( <> {selectedRoles.slice(0, 2).map((role) => ( - {role.name} - + ))} {selectedRoles.length > 2 && ( @@ -189,13 +189,17 @@ export default function MembersHeader(): React.JSX.Element { > r.roleSlug === role.slug + (r) => r.slug === role.slug )} className="mr-2 rounded-sm border-none bg-gray-400 data-[state=checked]:border-none data-[state=checked]:bg-white data-[state=checked]:text-black" onCheckedChange={() => toggleRole({ + id: role.id, + colorCode: role.colorCode, name: role.name, - roleSlug: role.slug + slug: role.slug, + description: role.description, + authorities: role.authorities }) } /> diff --git a/apps/platform/src/components/roles/createRoleDialog/index.tsx b/apps/platform/src/components/roles/createRoleDialog/index.tsx index fbb9ed2b1..0f9513ba0 100644 --- a/apps/platform/src/components/roles/createRoleDialog/index.tsx +++ b/apps/platform/src/components/roles/createRoleDialog/index.tsx @@ -3,8 +3,8 @@ import React, { useCallback, useState } from 'react' import type { AuthorityEnum } from '@keyshade/schema' import { toast } from 'sonner' import { AddSVG } from '@public/svg/shared' -import type { ProjectEnvironmentComboType } from '../projectEnvironmentSelector' import ProjectEnvironmentSelector from '../projectEnvironmentSelector' +import type { ProjectEnvironmentComboType } from '../projectEnvironmentSelector' import { Button } from '@/components/ui/button' import { Dialog, diff --git a/apps/platform/src/components/roles/editRoleSheet/index.tsx b/apps/platform/src/components/roles/editRoleSheet/index.tsx index c78b2542a..e3db5808e 100644 --- a/apps/platform/src/components/roles/editRoleSheet/index.tsx +++ b/apps/platform/src/components/roles/editRoleSheet/index.tsx @@ -2,8 +2,8 @@ import { useAtom, useSetAtom } from 'jotai' import React, { useCallback, useEffect, useState } from 'react' import type { AuthorityEnum } from '@keyshade/schema' import { toast } from 'sonner' -import type { ProjectEnvironmentComboType } from '../projectEnvironmentSelector' import ProjectEnvironmentSelector from '../projectEnvironmentSelector' +import type { ProjectEnvironmentComboType } from '../projectEnvironmentSelector' import { Button } from '@/components/ui/button' import { Sheet, diff --git a/apps/platform/src/components/roles/roleCard/index.tsx b/apps/platform/src/components/roles/roleCard/index.tsx index 55564cc8f..085dd6138 100644 --- a/apps/platform/src/components/roles/roleCard/index.tsx +++ b/apps/platform/src/components/roles/roleCard/index.tsx @@ -1,270 +1,29 @@ -'use client' - -import type { AuthorityEnum, WorkspaceRole } from '@keyshade/schema' -import dayjs from 'dayjs' -import { Copy, Pen } from 'lucide-react' -import { useCallback, useState } from 'react' -import { useSetAtom } from 'jotai' -import { TrashWhiteSVG } from '@public/svg/shared' -import { NoteIconSVG } from '@public/svg/secret' -import AvatarComponent from '@/components/common/avatar' -import { TableCell, TableRow } from '@/components/ui/table' -import { - Tooltip, - TooltipArrow, - TooltipContent, - TooltipProvider, - TooltipTrigger -} from '@/components/ui/tooltip' -import { deleteRoleOpenAtom, editRoleOpenAtom, selectedRoleAtom } from '@/store' -import { Button } from '@/components/ui/button' -import { copyToClipboard } from '@/lib/clipboard' +import type { WorkspaceRole } from '@keyshade/schema' +import RoleNameCell from './role-name-cell' +import RoleMembersCell from './role-members-cell' +import RoleAuthoritiesCell from './role-authorities-cell' +import RoleProjectEnvironmentCell from './role-project-environment-cell' +import RoleActionCell from './role-action-cell' +import { TableRow } from '@/components/ui/table' interface RoleListItemProps { role: WorkspaceRole } -function AuthorityTile({ authority }: { authority: AuthorityEnum }) { - const formattedAuthority = authority - .split('_') - .map((word) => word.charAt(0) + word.slice(1).toLowerCase()) - .join(' ') - - return ( -
- {formattedAuthority} -
- ) -} - -function ProjectsAndEnvironmentsTooltip({ - projectsAndEnvironments -}: { - projectsAndEnvironments: WorkspaceRole['projects'] -}) { - return projectsAndEnvironments.length > 0 ? ( -
- {projectsAndEnvironments.map(({ project, environments }) => ( -
  • - {project.name}{' '} - {environments.length > 0 - ? `(${environments.map((env) => env.name).join(', ')})` - : ''} -
  • - ))} -
    - ) : ( - - No projects and environments associated with this role - - ) -} - export default function RoleCard({ role }: RoleListItemProps): React.JSX.Element { - const AUTHORITY_DISPLAY_LIMIT = 5 - const hasAuthorities = role.authorities.length > 0 - - const [showAllAuthorities, setShowAllAuthorities] = useState(false) - - const setSelectedRole = useSetAtom(selectedRoleAtom) - const setIsDeleteRoleOpen = useSetAtom(deleteRoleOpenAtom) - const setIsEditRoleOpen = useSetAtom(editRoleOpenAtom) - - const isAuthorisedToEditRole = role.entitlements.canUpdate - const isAuthorisedToDeleteRole = role.entitlements.canDelete - - const handleDeleteRole = useCallback(() => { - setSelectedRole(role) - setIsDeleteRoleOpen(true) - }, [role, setIsDeleteRoleOpen, setSelectedRole]) - - const handleEditRole = useCallback(() => { - setSelectedRole(role) - setIsEditRoleOpen(true) - }, [role, setIsEditRoleOpen, setSelectedRole]) - - const isAdminRole = role.authorities.some( - (authority) => authority === 'WORKSPACE_ADMIN' - ) - return ( - - -
    - {role.name} - {role.description ? ( - - - - - - -

    {role.description}

    -
    -
    -
    - ) : null} - - -
    - {role.members.map((member) => { - const isInvited = !member.invitationAccepted - return ( - - - - - - - -
    - {member.name ? ( -
    {member.name}
    - ) : null} -
    {member.email}
    -
    -
    -
    - {isInvited ? 'Invited' : 'Joined'} -
    -
    - {dayjs(String(member.memberSince)).format( - 'MMM D, YYYY' - )} -
    -
    - -
    -
    -
    - ) - })} -
    -
    - -
    - {hasAuthorities ? ( - <> - {role.authorities - .slice(0, showAllAuthorities ? role.authorities.length : 5) - .map((authority) => ( - - ))} - {role.authorities.length > AUTHORITY_DISPLAY_LIMIT ? ( - - ) : null} - - ) : ( - - No authorities available - - )} -
    -
    - - - - - {role.projects.length} projects,{' '} - {role.projects.reduce((a, b) => a + b.environments.length, 0)}{' '} - environments - - - - - - - - - - - - - - - - Copy the role slug to your clipboard. - - - - - - - - - - - - {isAdminRole ? ( - - You can not delete a workspace admin role - - - ) : null} - - - + + + + + + ) } diff --git a/apps/platform/src/components/roles/roleCard/role-action-cell.tsx b/apps/platform/src/components/roles/roleCard/role-action-cell.tsx new file mode 100644 index 000000000..2df942da4 --- /dev/null +++ b/apps/platform/src/components/roles/roleCard/role-action-cell.tsx @@ -0,0 +1,110 @@ +'use client' +import { TrashWhiteSVG } from '@public/svg/shared' +import { Copy, Pen } from 'lucide-react' +import React, { useCallback } from 'react' +import { useSetAtom } from 'jotai' +import type { WorkspaceRole } from '@keyshade/schema' +import { Button } from '@/components/ui/button' +import { TableCell } from '@/components/ui/table' +import { + Tooltip, + TooltipArrow, + TooltipContent, + TooltipProvider, + TooltipTrigger +} from '@/components/ui/tooltip' +import { copyToClipboard } from '@/lib/clipboard' +import { deleteRoleOpenAtom, editRoleOpenAtom, selectedRoleAtom } from '@/store' + +interface RoleActionCellProps { + role: WorkspaceRole +} + +function RoleActionCell({ role }: RoleActionCellProps) { + const setSelectedRole = useSetAtom(selectedRoleAtom) + const setIsDeleteRoleOpen = useSetAtom(deleteRoleOpenAtom) + const setIsEditRoleOpen = useSetAtom(editRoleOpenAtom) + + const isAuthorisedToEditRole = role.entitlements.canUpdate + const isAuthorisedToDeleteRole = role.entitlements.canDelete + + const handleDeleteRole = useCallback(() => { + setSelectedRole(role) + setIsDeleteRoleOpen(true) + }, [role, setIsDeleteRoleOpen, setSelectedRole]) + + const handleEditRole = useCallback(() => { + setSelectedRole(role) + setIsEditRoleOpen(true) + }, [role, setIsEditRoleOpen, setSelectedRole]) + + const isAdminRole = role.authorities.some( + (authority) => authority === 'WORKSPACE_ADMIN' + ) + + return ( + + + + + + + + Copy the role slug to your clipboard. + + + + + + + + + + + + {isAdminRole ? ( + + You can not delete a workspace admin role + + + ) : null} + + + + ) +} + +export default RoleActionCell diff --git a/apps/platform/src/components/roles/roleCard/role-authorities-cell.tsx b/apps/platform/src/components/roles/roleCard/role-authorities-cell.tsx new file mode 100644 index 000000000..56a8d3ac7 --- /dev/null +++ b/apps/platform/src/components/roles/roleCard/role-authorities-cell.tsx @@ -0,0 +1,60 @@ +import React, { useState } from 'react' +import type { AuthorityEnum, WorkspaceRole } from '@keyshade/schema' +import { Button } from '@/components/ui/button' +import { TableCell } from '@/components/ui/table' + +function AuthorityTile({ authority }: { authority: AuthorityEnum }) { + const formattedAuthority = authority + .split('_') + .map((word) => word.charAt(0) + word.slice(1).toLowerCase()) + .join(' ') + + return ( +
    + {formattedAuthority} +
    + ) +} + +interface RoleAuthoritiesCellProps { + authorities: WorkspaceRole['authorities'] +} + +const AUTHORITY_DISPLAY_LIMIT = 5 + +function RoleAuthoritiesCell({ authorities }: RoleAuthoritiesCellProps) { + const [showAllAuthorities, setShowAllAuthorities] = useState(false) + const hasAuthorities = authorities.length > 0 + + return ( + +
    + {hasAuthorities ? ( + <> + {authorities + .slice(0, showAllAuthorities ? authorities.length : 5) + .map((authority) => ( + + ))} + {authorities.length > AUTHORITY_DISPLAY_LIMIT ? ( + + ) : null} + + ) : ( + + No authorities available + + )} +
    +
    + ) +} + +export default RoleAuthoritiesCell diff --git a/apps/platform/src/components/roles/roleCard/role-members-cell.tsx b/apps/platform/src/components/roles/roleCard/role-members-cell.tsx new file mode 100644 index 000000000..37d980063 --- /dev/null +++ b/apps/platform/src/components/roles/roleCard/role-members-cell.tsx @@ -0,0 +1,68 @@ +import dayjs from 'dayjs' +import React from 'react' +import type { WorkspaceRole } from '@keyshade/schema' +import AvatarComponent from '@/components/common/avatar' +import { TableCell } from '@/components/ui/table' +import { + Tooltip, + TooltipArrow, + TooltipContent, + TooltipProvider, + TooltipTrigger +} from '@/components/ui/tooltip' + +interface RoleMembersCellProps { + members: WorkspaceRole['members'] +} + +function RoleMembersCell({ members }: RoleMembersCellProps) { + return ( + +
    + {members.map((member) => { + const isInvited = !member.invitationAccepted + return ( + + + + + + + +
    + {member.name ? ( +
    {member.name}
    + ) : null} +
    {member.email}
    +
    +
    +
    + {isInvited ? 'Invited' : 'Joined'} +
    +
    + {dayjs(String(member.memberSince)).format('MMM D, YYYY')} +
    +
    + +
    +
    +
    + ) + })} +
    +
    + ) +} + +export default RoleMembersCell diff --git a/apps/platform/src/components/roles/roleCard/role-name-cell.tsx b/apps/platform/src/components/roles/roleCard/role-name-cell.tsx new file mode 100644 index 000000000..fd03e332c --- /dev/null +++ b/apps/platform/src/components/roles/roleCard/role-name-cell.tsx @@ -0,0 +1,40 @@ +import { PermissionBadgeSVG } from '@public/svg/roles' +import { NoteIconSVG } from '@public/svg/secret' +import React from 'react' +import type { WorkspaceRole } from '@keyshade/schema' +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger +} from '@/components/ui/tooltip' +import { TableCell } from '@/components/ui/table' + +interface RoleNameCellProps { + colorCode: WorkspaceRole['colorCode'] + name: WorkspaceRole['name'] + description?: WorkspaceRole['description'] +} + +function RoleNameCell({ colorCode, name, description }: RoleNameCellProps) { + return ( + + + {name} + {description ? ( + + + + + + +

    {description}

    +
    +
    +
    + ) : null} +
    + ) +} + +export default RoleNameCell diff --git a/apps/platform/src/components/roles/roleCard/role-project-environment-cell.tsx b/apps/platform/src/components/roles/roleCard/role-project-environment-cell.tsx new file mode 100644 index 000000000..fdb99fbf2 --- /dev/null +++ b/apps/platform/src/components/roles/roleCard/role-project-environment-cell.tsx @@ -0,0 +1,66 @@ +import React from 'react' +import type { WorkspaceRole } from '@keyshade/schema' +import { TableCell } from '@/components/ui/table' +import { + Tooltip, + TooltipArrow, + TooltipContent, + TooltipProvider, + TooltipTrigger +} from '@/components/ui/tooltip' + +function ProjectsAndEnvironmentsTooltip({ + projectsAndEnvironments +}: { + projectsAndEnvironments: WorkspaceRole['projects'] +}) { + return projectsAndEnvironments.length > 0 ? ( +
    + {projectsAndEnvironments.map(({ project, environments }) => ( +
  • + {project.name}{' '} + {environments.length > 0 + ? `(${environments.map((env) => env.name).join(', ')})` + : ''} +
  • + ))} +
    + ) : ( + + No projects and environments associated with this role + + ) +} + +interface RoleProjectEnvironmentCellProps { + projects: WorkspaceRole['projects'] +} + +function RoleProjectEnvironmentCell({ + projects +}: RoleProjectEnvironmentCellProps) { + return ( + + + + + {projects.length} projects,{' '} + {projects.reduce((a, b) => a + b.environments.length, 0)}{' '} + environments + + + + + + + + + ) +} + +export default RoleProjectEnvironmentCell diff --git a/apps/platform/src/components/roles/rolesList/index.tsx b/apps/platform/src/components/roles/rolesList/index.tsx index 508e9017e..e7a4f94fb 100644 --- a/apps/platform/src/components/roles/rolesList/index.tsx +++ b/apps/platform/src/components/roles/rolesList/index.tsx @@ -15,7 +15,7 @@ import ErrorCard from '@/components/shared/error-card' function RoleListItemSkeleton(): React.JSX.Element { return ( -
    +
    diff --git a/apps/platform/src/components/shared/add-workspace-dialog.tsx b/apps/platform/src/components/shared/add-workspace-dialog.tsx index bd20bcbcf..773af450c 100644 --- a/apps/platform/src/components/shared/add-workspace-dialog.tsx +++ b/apps/platform/src/components/shared/add-workspace-dialog.tsx @@ -14,13 +14,13 @@ import { import { Button } from '../ui/button' import { Input } from '../ui/input' import { Label } from '../ui/label' +import { useHttp } from '@/hooks/use-http' +import ControllerInstance from '@/lib/controller-instance' import { allWorkspacesAtom, selectedWorkspaceAtom, globalSearchDataAtom } from '@/store' -import ControllerInstance from '@/lib/controller-instance' -import { useHttp } from '@/hooks/use-http' export interface AddWorkspaceDialogProps { trigger?: React.ReactNode diff --git a/apps/platform/src/components/shared/navbar/index.tsx b/apps/platform/src/components/shared/navbar/index.tsx index f93769d28..2b66cfeed 100644 --- a/apps/platform/src/components/shared/navbar/index.tsx +++ b/apps/platform/src/components/shared/navbar/index.tsx @@ -11,7 +11,7 @@ function Navbar(): React.JSX.Element { return ( <> -