Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(platform): Added screen for CREATE NEW PROJECT #540

Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/platform/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@radix-ui/react-menubar": "^1.0.4",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-scroll-area": "^1.0.5",
"@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
Expand Down
256 changes: 198 additions & 58 deletions apps/platform/src/app/(main)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
'use client'
import { useEffect, useState } from 'react'
import { toast } from 'sonner'
import { useRouter } from 'next/navigation'
import type {
Project,
CreateProjectRequest,
CreateProjectResponse,
GetAllProjectsRequest,
GetAllProjectsResponse,
Workspace
} from '@keyshade/schema'
import { ProjectController } from '@keyshade/api-client'
import { AddSVG } from '@public/svg/shared'
import ProjectCard from '@/components/dashboard/projectCard'
import {
Expand All @@ -17,90 +24,133 @@ import {
import { Button } from '@/components/ui/button'
import { Label } from '@/components/ui/label'
import { Input } from '@/components/ui/input'
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue
} from '@/components/ui/select'
import { Switch } from '@/components/ui/switch'
import type { NewProject, ProjectWithoutKeys, Workspace } from '@/types'
// import type { NewProject, ProjectWithoutKeys } from '@/types'
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTrigger
} from '@/components/ui/dialog'
import { Projects } from '@/lib/api-functions/projects'
// import { Projects } from '@/lib/api-functions/projects'
// import { ProjectWithoutKeys } from '@/types'

export default function Index(): JSX.Element {

type projectItem = GetAllProjectsResponse["items"][number]
const [isSheetOpen, setIsSheetOpen] = useState<boolean>(false)
const [projects, setProjects] = useState<ProjectWithoutKeys[] | []>([])
const [newProjectData, setNewProjectData] = useState<NewProject>({
const [isDialogOpen, setIsDialogOpen] = useState<boolean>(false)
const [projects, setProjects] = useState< projectItem[] | []>([])
const [newProjectData, setNewProjectData] = useState<CreateProjectRequest>({
name: '',
workspaceSlug: '',
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
name: '',
projectId: '',
description: ''
}
]
],
accessLevel: 'GLOBAL'
})

const router = useRouter()
// const router = useRouter()

const createNewProject = async () => {
const projectController = new ProjectController(
process.env.NEXT_PUBLIC_BACKEND_URL
)

const request: CreateProjectRequest = {
name: newProjectData.name,
workspaceSlug: currentWorkspace.slug,
description: newProjectData.description ?? undefined,
environments: newProjectData.environments,
accessLevel: newProjectData.accessLevel
}

const response = await projectController.createProject(request, {})
const data = response.data as CreateProjectResponse;

const currentWorkspace =
setIsDialogOpen(false);

}

const currentWorkspace: Workspace =
typeof localStorage !== 'undefined'
? (JSON.parse(
localStorage.getItem('currentWorkspace') ?? '{}'
) as Workspace)
: (JSON.parse(`{}`) as Workspace)

useEffect( () => {

console.log("Current workspace slug: ", currentWorkspace.slug);

useEffect(() => {
Projects.getProjectsbyWorkspaceID(currentWorkspace.id)
.then((data: ProjectWithoutKeys[] | [] | undefined) => {
const projectController = new ProjectController(
process.env.NEXT_PUBLIC_BACKEND_URL
)

projectController.getAllProjects({workspaceSlug: currentWorkspace.slug}, {})
.then((data: GetAllProjectsResponse) => {
if (data) {
poswalsameer marked this conversation as resolved.
Show resolved Hide resolved
setProjects(data)
//@ts-ignore
setProjects(data.data.items)
}
})
.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">
<div className="flex items-center justify-between">
<h1 className="text-[1.75rem] font-semibold ">My Projects</h1>

<Dialog>
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
<DialogTrigger>
<Button>
<Button onClick={() => setIsDialogOpen(true)}>
{' '}
<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>
<DialogContent className="h-[39.5rem] w-[28.625rem] rounded-[12px] border bg-[#1E1E1F] ">
<div className="flex h-[3.125rem] w-[25.625rem] flex-col items-start justify-center">
<DialogHeader className=" font-geist h-[1.875rem] w-[8.5rem] text-[1.125rem] font-semibold text-white ">
Create Projects
</DialogHeader>

<DialogDescription className=" font-inter h-[1.25rem] w-[25.625rem] text-[0.875rem] font-normal text-[#D4D4D4]">
Create your new project
</DialogDescription>
</div>
<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">
<div className="flex h-[29.125rem] w-[25.813rem] flex-col gap-[1rem] py-[1rem] ">
{/* NAME */}
<div className="flex h-[2.25rem] w-[25.813rem] items-center justify-center gap-[1rem]">
<Label
className="font-geist h-[1.25rem] w-[4.813rem] gap-[0.25rem] text-left text-[0.875rem] font-[500] "
htmlFor="name"
>
Name
</Label>
<Input
className="col-span-3"
className="col-span-3 h-[2.25rem] w-[20rem] "
id="name"
onChange={(e) => {
setNewProjectData((prev) => ({
Expand All @@ -111,12 +161,17 @@ export default function Index(): JSX.Element {
placeholder="Enter the name"
/>
</div>
<div className="flex flex-col items-start gap-4">
<Label className="text-right" htmlFor="name">

{/* DESCRIPTION */}
<div className="flex h-[5.625rem] w-[25.813rem] items-center justify-center gap-[1rem]">
<Label
className="font-geist h-[1.25rem] w-[4.813rem] gap-[0.25rem] text-left text-[0.875rem] font-[500] "
htmlFor="name"
>
Description
</Label>
<Input
className="col-span-3"
className="col-span-3 h-[5.625rem] w-[20rem] gap-[0.25rem]"
id="name"
onChange={(e) => {
setNewProjectData((prev) => ({
Expand All @@ -127,28 +182,113 @@ export default function Index(): JSX.Element {
placeholder="Enter the name"
/>
</div>
{/* {isNameEmpty ? (
<span className="ml-[3.5rem] mt-1 text-red-500">
Name cannot be empty
</span>
) : null} */}

{/* ENV. NAME */}
<div className="flex h-[2.25rem] w-[25.813rem] items-center justify-center gap-[1rem]">
<Label
className="font-geist h-[1.25rem] w-[4.813rem] gap-[0.25rem] text-left text-[0.875rem] font-[500] "
htmlFor="envName"
>
Env. Name
</Label>
<Input
className="col-span-3 h-[2.25rem] w-[20rem] "
id="envName"
onChange={(e) => {
setNewProjectData((prev) => ({
...prev,
envName: e.target.value
}))
}}
placeholder="Your project default environment name"
/>
</div>

{/* ENV. DESCRIPTION */}
<div className="flex h-[4.875rem] w-[25.813rem] items-center justify-center gap-[1rem]">
<Label
className="font-geist h-[1.25rem] w-[4.813rem] gap-[0.25rem] text-left text-[0.875rem] font-[500]"
htmlFor="envDescription"
>
Env. Description
</Label>
<Input
className="col-span-3 h-[4.875rem] w-[20rem] "
id="envDescription"
onChange={(e) => {
setNewProjectData((prev) => ({
...prev,
envDescription: e.target.value
}))
}}
placeholder="Detailed description about your environment"
/>
</div>

{/* ACCESS LEVEL */}
<div className="flex h-[2.25rem] w-[25.813rem] items-center justify-center gap-[1rem]">
<Label
className="font-geist h-[0.875rem] w-[5.5rem] gap-[0.25rem] text-left text-[0.875rem] font-[500] "
htmlFor="accessLevel"
>
Access Level
</Label>
<Select
defaultValue="GLOBAL"
onValueChange={(currValue) => {
setNewProjectData((prevData) => ({
...prevData,
accessLevel: currValue as
| 'GLOBAL'
| 'INTERNAL'
| 'PRIVATE'
}))
}}
>
<SelectTrigger className=" h-[2.25rem] w-[20rem] rounded-[0.375rem] border-[0.013rem] border-white/10 focus:border-[#3b82f6]">
<SelectValue />
</SelectTrigger>
<SelectContent className="border-[0.013rem] border-white/10 bg-neutral-800 text-white ">
<SelectGroup>
{['GLOBAL', 'INTERNAL', 'PRIVATE'].map(
(accessValue) => (
<SelectItem
className="group cursor-pointer rounded-sm"
key={accessValue.toUpperCase()}
value={accessValue.toUpperCase()}
>
{accessValue}
</SelectItem>
)
)}
</SelectGroup>
</SelectContent>
</Select>
</div>

<div className="flex h-[4.875rem] w-[25.813rem] items-center justify-center gap-[1rem]">
<div className="flex h-[2.875rem] w-[22.563rem] flex-col items-start justify-center">
<h1 className="font-geist h-[1.5rem] w-[18.688rem] text-[1rem] font-[500]">
Should the private key be saved or not?
</h1>
<h1 className="font-inter h-[1.25rem] w-[16.563rem] text-[0.8rem] font-normal text-[#A1A1AA] ">
Choose if you want to save your private key
</h1>
</div>

<div className="p-[0.125rem]">
<Switch className="h-[1.25rem] w-[2.25rem] " />
</div>
</div>
</div>
</div>
<div className="flex w-full justify-end">
<div className="flex h-[2.25rem] w-[25.625rem] justify-end">
<Button
onClick={() => {
Projects.createProject(newProjectData, currentWorkspace.id)
.then(() => {
toast.success('New project added successfully')
router.refresh()
})
.catch(() => {
toast.error('Failed to add new project')
})
}}
className="font-inter h-[2.25rem] w-[8rem] rounded-[0.375rem] text-[0.875rem] font-[500]"
onClick={createNewProject}
variant="secondary"
>
Add project
Create project
</Button>
</div>
</DialogContent>
Expand All @@ -157,7 +297,7 @@ export default function Index(): JSX.Element {

{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) => {
{projects.map((project: GetAllProjectsResponse['items'][number]) => {
return (
<ProjectCard
config={10}
Expand Down
1 change: 1 addition & 0 deletions apps/platform/src/app/auth/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export default function AuthPage(): React.JSX.Element {
setInInvalidEmail(false)

try {

const response = await fetch(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/api/auth/send-otp/${encodeURIComponent(userEmail)}`,
{
Expand Down
Loading