Skip to content

Commit

Permalink
feat(platform): Workspace integrate (keyshade-xyz#241)
Browse files Browse the repository at this point in the history
  • Loading branch information
kriptonian1 authored May 27, 2024
1 parent 5d094d5 commit 6107e7d
Show file tree
Hide file tree
Showing 11 changed files with 9,172 additions and 11,066 deletions.
1 change: 1 addition & 0 deletions apps/platform/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
"@tanstack/react-table": "^8.16.0",
"avvvatars-react": "^0.4.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"cmdk": "^1.0.0",
Expand Down
220 changes: 188 additions & 32 deletions apps/platform/src/app/(main)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
'use client'
import { useState } from 'react'
import { useEffect, useState } from 'react'
import { z } from 'zod'
import { toast } from 'sonner'
import { useRouter } from 'next/navigation'
import { AddSVG } from '@public/svg/shared'
import ProjectCard from '@/components/dashboard/projectCard'
import {
Sheet,
Expand All @@ -15,45 +19,197 @@ import { Button } from '@/components/ui/button'
import { Label } from '@/components/ui/label'
import { Input } from '@/components/ui/input'
import { Switch } from '@/components/ui/switch'
import { apiClient } from '@/lib/api-client'
import type { NewProject, ProjectWithoutKeys, Workspace } from '@/types'
import { zProjectWithoutKeys } from '@/types'
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTrigger
} from '@/components/ui/dialog'

async function getProjects(
currentWorkspaceID: string
): Promise<ProjectWithoutKeys[] | [] | undefined> {
try {
const projectData = await apiClient.get<ProjectWithoutKeys[] | []>(
`/project/all/${currentWorkspaceID}`
)
const zProjectWithoutKeysArray = z.array(zProjectWithoutKeys)
const { success, data } = zProjectWithoutKeysArray.safeParse(projectData)
if (!success) {
throw new Error('Invalid data')
}
return data
} catch (error) {
// eslint-disable-next-line no-console -- we need to log the error
console.error(error)
}
}

const details = [
{
title: 'backend-server',
description: 'This a description for your project',
environment: 2,
config: 10,
secret: 5
},
{
title: 'frontend-server',
description: 'This a description for your project',
environment: 2,
config: 10,
secret: 5
async function createProject(
newProjectData: NewProject,
currentWorkspaceID: string
): Promise<void> {
try {
await apiClient.post<NewProject>(`/project/${currentWorkspaceID}`, {
newProjectData
})
} catch (error) {
// eslint-disable-next-line no-console -- we need to log the error
console.error(error)
}
]
}

export default function Index(): JSX.Element {
const [isSheetOpen, setIsSheetOpen] = useState<boolean>(false)
const [projects, setProjects] = useState<ProjectWithoutKeys[] | []>([])
const [newProjectData, setNewProjectData] = useState<NewProject>({
name: '',
description: '',
storePrivateKey: false,
environments: [
{
name: 'Dev',
description: 'Development environment',
isDefault: true
},
{
name: 'Stage',
description: 'Staging environment',
isDefault: false
},
{
name: 'Prod',
description: 'Production environment',
isDefault: false
}
]
})

const router = useRouter()

const currentWorkspace = JSON.parse(
localStorage.getItem('currentWorkspace') ?? '{}'
) as Workspace

useEffect(() => {
getProjects(currentWorkspace.id)
.then((data: ProjectWithoutKeys[] | [] | undefined) => {
if (data) {
setProjects(data)
}
})
.catch((error) => {
// eslint-disable-next-line no-console -- we need to log the error
console.error(error)
})
}, [currentWorkspace.id])

return (
<div className="flex flex-col gap-4">
<h1 className="text-[1.75rem] font-semibold ">My Projects</h1>
<div className="grid h-[70vh] gap-6 overflow-y-auto scroll-smooth p-2 md:grid-cols-2 2xl:grid-cols-3">
{details.map((projectDetails, index) => {
return (
<ProjectCard
config={projectDetails.config}
description={projectDetails.description}
environment={projectDetails.environment}
// eslint-disable-next-line react/no-array-index-key -- key is not used as a prop
key={index}
secret={projectDetails.secret}
setIsSheetOpen={setIsSheetOpen}
title={projectDetails.title}
/>
)
})}
<div className="flex items-center justify-between">
<h1 className="text-[1.75rem] font-semibold ">My Projects</h1>

<Dialog>
<DialogTrigger>
<Button>
{' '}
<AddSVG /> Create a new Project
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>Create a new project</DialogHeader>
<DialogDescription>
Fill in the details to create a new project
</DialogDescription>
<div className="flex flex-col gap-y-8">
<div className="flex w-full flex-col gap-y-4">
<div className="flex flex-col items-start gap-4">
<Label className="text-right" htmlFor="name">
Name
</Label>
<Input
className="col-span-3"
id="name"
onChange={(e) => {
setNewProjectData((prev) => ({
...prev,
name: e.target.value
}))
}}
placeholder="Enter the name"
/>
</div>
<div className="flex flex-col items-start gap-4">
<Label className="text-right" htmlFor="name">
Description
</Label>
<Input
className="col-span-3"
id="name"
onChange={(e) => {
setNewProjectData((prev) => ({
...prev,
description: e.target.value
}))
}}
placeholder="Enter the name"
/>
</div>
{/* {isNameEmpty ? (
<span className="ml-[3.5rem] mt-1 text-red-500">
Name cannot be empty
</span>
) : null} */}
</div>
</div>
<div className="flex w-full justify-end">
<Button
onClick={() => {
createProject(newProjectData, currentWorkspace.id)
.then(() => {
toast.success('New project added successfully')
router.refresh()
})
.catch(() => {
toast.error('Failed to add new project')
})
}}
variant="secondary"
>
Add project
</Button>
</div>
</DialogContent>
</Dialog>
</div>

{projects.length !== 0 ? (
<div className="grid h-[70vh] gap-6 overflow-y-auto scroll-smooth p-2 md:grid-cols-2 2xl:grid-cols-3">
{projects.map((project: ProjectWithoutKeys) => {
return (
<ProjectCard
config={10}
description={project.description ?? ''}
environment={2}
idForImage={project.id}
key={project.id}
secret={5}
setIsSheetOpen={setIsSheetOpen}
title={project.name}
/>
)
})}
</div>
) : (
<div className="mt-[10vh] flex justify-center">
<div>No projects yet? Get started by creating a new project.</div>
</div>
)}

<Sheet
onOpenChange={(open) => {
setIsSheetOpen(open)
Expand Down
9 changes: 5 additions & 4 deletions apps/platform/src/app/(main)/settings/@profile/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,11 @@ function ProfilePage(): React.JSX.Element {
<InputLoading />
) : (
<Input
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setIsModified(true)
setUserData((prev) => ({ ...prev, email: e.target.value }))
}}
disabled
// onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
// setIsModified(true)
// setUserData((prev) => ({ ...prev, email: e.target.value }))
// }}
placeholder="email"
value={userData.email}
/>
Expand Down
23 changes: 20 additions & 3 deletions apps/platform/src/app/auth/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,29 @@ export default function AuthPage(): React.JSX.Element {
{isLoading ? <LoadingSVG className="w-10" /> : 'Get Started'}
</Button>
</form>
<Button className="w-full" variant="outline">
{/* <Button className="w-full" variant="outline">
Already have an account? Sign In
</Button>
</Button> */}
<div className="text-center text-xs text-[#808080]">
By continueing, you acknowledge and agree to our <br />
Legal Terms and Privacy Policy.
<a
className="underline"
href="https://keyshade.xyz/terms_and_condition"
rel="noopener noreferrer"
target="_blank"
>
Legal Terms
</a>{' '}
and{' '}
<a
className="underline"
href="https://keyshade.xyz/privacy"
rel="noopener noreferrer"
target="_blank"
>
Privacy Policy
</a>{' '}
.
</div>
</div>
</main>
Expand Down
10 changes: 7 additions & 3 deletions apps/platform/src/components/dashboard/projectCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import Link from 'next/link'
import type { Dispatch, SetStateAction } from 'react'
import { toast } from 'sonner'
import Avvvatars from 'avvvatars-react'
import { ConfigSVG, EnvironmentSVG, SecretSVG } from '@public/svg/dashboard'
import {
ContextMenu,
Expand All @@ -22,7 +23,8 @@ import {
// } from '@/components/ui/menubar'

interface ProjectCardProps {
key: number
idForImage: string
key: number | string
title: string
description: string
environment: number
Expand All @@ -32,6 +34,7 @@ interface ProjectCardProps {
}

function ProjectCard({
idForImage,
key,
title,
description,
Expand Down Expand Up @@ -74,14 +77,15 @@ function ProjectCard({

return (
<ContextMenu>
<ContextMenuTrigger>
<ContextMenuTrigger className="flex h-[7rem]">
<Link
className="flex h-[7rem] max-w-[30.25rem] justify-between rounded-xl bg-white/5 px-5 py-4 shadow-lg hover:bg-white/10"
href={`/project/${title}`}
key={key}
>
<div className="flex items-center gap-x-5">
<div className="aspect-square h-14 w-14 rounded-full bg-white/35" />
{/* <div className="aspect-square h-14 w-14 rounded-full bg-white/35" /> */}
<Avvvatars size={56} style="shape" value={idForImage} />
<div>
<div className="font-semibold">{title}</div>
<span className="text-xs font-semibold text-white/60">
Expand Down
11 changes: 8 additions & 3 deletions apps/platform/src/components/shared/navbar/searchModel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,20 +55,25 @@ function SearchModel({
<CommandGroup heading="Settings">
<CommandItem
onSelect={() => {
router.push('/profile')
router.push('/settings?tab=profile')
setIsOpen(false)
}}
>
<User className="mr-2 h-4 w-4" />
<span>Profile</span>
</CommandItem>
<CommandItem>
<CommandItem
onSelect={() => {
router.push('/settings?tab=billing')
setIsOpen(false)
}}
>
<CreditCard className="mr-2 h-4 w-4" />
<span>Billing</span>
</CommandItem>
<CommandItem
onSelect={() => {
router.push('/settings')
router.push('/settings?tab=workspace')
setIsOpen(false)
}}
>
Expand Down
Loading

0 comments on commit 6107e7d

Please sign in to comment.