add: databases WIP
This commit is contained in:
parent
dd98781cc5
commit
c05af7e7aa
|
@ -0,0 +1,103 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Progress } from '@/components/ui/progress';
|
||||||
|
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
||||||
|
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet';
|
||||||
|
import { useWorkspace } from '@/hooks/useWorkspace';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import { Plus } from 'lucide-react';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
interface DatabaseNewSteps {
|
||||||
|
display: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const steps: DatabaseNewSteps[] = [
|
||||||
|
{
|
||||||
|
display: 'Database Type',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
display: 'Configuration',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
display: 'Review & Deploy',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function DatabaseNewForm() {
|
||||||
|
const [open, setOpen] = useState<boolean>(false);
|
||||||
|
const [currentSteps, setCurrentSteps] = useState<number>(1);
|
||||||
|
|
||||||
|
const { id } = useWorkspace();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Sheet open={open} onOpenChange={() => setOpen((open) => !open)}>
|
||||||
|
<SheetTrigger asChild>
|
||||||
|
<Button className="flex gap-1 justify-center items-center bg-[#3A7BFE]">
|
||||||
|
<Plus />
|
||||||
|
New Database
|
||||||
|
</Button>
|
||||||
|
</SheetTrigger>
|
||||||
|
<SheetContent className="min-w-[500px]">
|
||||||
|
<SheetHeader>
|
||||||
|
<SheetTitle className="text-2xl">New Database</SheetTitle>
|
||||||
|
<SheetDescription>Deploy a new database</SheetDescription>
|
||||||
|
</SheetHeader>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<div className="flex flex-col gap-2 my-6">
|
||||||
|
<div className="flex flex-row justify-between">
|
||||||
|
<p>
|
||||||
|
Step: <span className="text-muted-foreground">{steps[currentSteps - 1].display}</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{currentSteps} / {steps.length}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Progress className="bg-gray-300 h-3 " color="red" value={currentSteps * (100 / steps.length)} max={100} indicatorColor="bg-[#3A7BFE]" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{currentSteps == 1 && (
|
||||||
|
<div>
|
||||||
|
{/* <RadioGroup defaultValue={'vitess'} className="flex flex-col gap-2 mb-4" onValueChange={}>
|
||||||
|
<div className={cn('flex flex-row items-center gap-4 border-[1px] rounded-lg py-2 px-4', provider.value == newApplication.serviceProvider ? 'border-[#3A7BFE]' : 'border-gray-300')}>
|
||||||
|
<RadioGroupItem value={'vitess'} id={'vitess'} />
|
||||||
|
<label htmlFor={'vitess'} className="flex flex-row items-center gap-4">
|
||||||
|
<img src={provider.image} alt={'Vitess'} className="w-8 h-8 rounded-full" />
|
||||||
|
Vitess
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</RadioGroup> */}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="flex flex-row justify-between my-6">
|
||||||
|
<Button
|
||||||
|
variant={'outline'}
|
||||||
|
className={cn(currentSteps == 1 && 'opacity-0 pointer-events-none select-none')}
|
||||||
|
onClick={() => {
|
||||||
|
setCurrentSteps((prev) => (prev - 1 < 1 ? 1 : prev - 1));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Back
|
||||||
|
</Button>
|
||||||
|
{currentSteps == steps.length ? (
|
||||||
|
<Button className="bg-[#3A7BFE]" onClick={() => {}}>
|
||||||
|
Deploy
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
className="bg-[#3A7BFE]"
|
||||||
|
onClick={() => {
|
||||||
|
setCurrentSteps((prev) => (prev + 1 > steps.length ? steps.length : prev + 1));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Next
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</SheetContent>
|
||||||
|
</Sheet>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import { Button } from '@/components/ui/button';
|
import { Database } from 'lucide-react';
|
||||||
import { Database, Plus } from 'lucide-react';
|
import DatabaseNewForm from './database-new-form';
|
||||||
|
|
||||||
export default async function Databases() {
|
export default async function Databases() {
|
||||||
return (
|
return (
|
||||||
|
@ -11,10 +11,11 @@ export default async function Databases() {
|
||||||
<h3 className="font-semibold">No database</h3>
|
<h3 className="font-semibold">No database</h3>
|
||||||
<p className="text-gray-700">Get started by creating a new database.</p>
|
<p className="text-gray-700">Get started by creating a new database.</p>
|
||||||
</div>
|
</div>
|
||||||
<Button className="flex gap-1 justify-center items-center bg-[#3A7BFE]">
|
{/* <Button className="flex gap-1 justify-center items-center bg-[#3A7BFE]">
|
||||||
<Plus />
|
<Plus />
|
||||||
New Database
|
New Database
|
||||||
</Button>
|
</Button> */}
|
||||||
|
<DatabaseNewForm />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,8 +1,17 @@
|
||||||
|
import { WorkspaceProvider } from '@/contexts/workspace';
|
||||||
import Navigation from './Navigation';
|
import Navigation from './Navigation';
|
||||||
|
import prisma from '@/lib/prisma';
|
||||||
|
|
||||||
|
export default async function WorkspaceLayout({ children, params }: Readonly<{ children: React.ReactNode; params: { workspace: string } }>) {
|
||||||
|
const workspace = await prisma.workspace.findUnique({
|
||||||
|
where: {
|
||||||
|
slug: params.workspace,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!workspace) return <div>Workspace not found</div>;
|
||||||
|
|
||||||
export default async function WorkspaceLayout({ children }: Readonly<{ children: React.ReactNode }>) {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<WorkspaceProvider workspace={workspace}>
|
||||||
<div className="bg-white flex justify-center py-2 pt-4 border-b-2 border-b-gray-200">
|
<div className="bg-white flex justify-center py-2 pt-4 border-b-2 border-b-gray-200">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="border-t-[1px] border-t-gray-200">
|
<div className="border-t-[1px] border-t-gray-200">
|
||||||
|
@ -13,6 +22,6 @@ export default async function WorkspaceLayout({ children }: Readonly<{ children:
|
||||||
<div className="bg-[#ECEFF1] overflow-y-auto flex-grow">
|
<div className="bg-[#ECEFF1] overflow-y-auto flex-grow">
|
||||||
<div className="container">{children}</div>
|
<div className="container">{children}</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</WorkspaceProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { Workspace } from '@prisma/client';
|
||||||
|
import { createContext } from 'react';
|
||||||
|
|
||||||
|
export const Work = createContext<Workspace>();
|
||||||
|
|
||||||
|
export function WorkspaceProvider({ children, workspace }: { children: React.ReactNode; workspace: Workspace }) {
|
||||||
|
return <Work.Provider value={workspace}>{children}</Work.Provider>;
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { Work } from '@/contexts/workspace';
|
||||||
|
import { Workspace } from '@prisma/client';
|
||||||
|
import { useContext } from 'react';
|
||||||
|
|
||||||
|
export const useWorkspace = (): Workspace => useContext(Work);
|
|
@ -0,0 +1,73 @@
|
||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
import { Database, DatabaseProvider } from "@prisma/client";
|
||||||
|
|
||||||
|
type DatabaseType = DatabaseProvider;
|
||||||
|
|
||||||
|
export class CreateDatabase {
|
||||||
|
|
||||||
|
private workspaceId: string;
|
||||||
|
private id: string;
|
||||||
|
public name: string;
|
||||||
|
public type: DatabaseType;
|
||||||
|
|
||||||
|
public username: string;
|
||||||
|
private password: string;
|
||||||
|
private host: string | undefined;
|
||||||
|
private port: number | undefined;
|
||||||
|
|
||||||
|
constructor(workspaceId: string, name: string, type: DatabaseType) {
|
||||||
|
this.id = "new-database-uuid"; // TODO: Generate UUID
|
||||||
|
this.username = "root"; // TODO: Generate random username
|
||||||
|
this.password = "toor"; // TODO: Generate random password
|
||||||
|
|
||||||
|
this.workspaceId = workspaceId;
|
||||||
|
this.name = name;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deploying the database to the cluster
|
||||||
|
public async deploy(): Promise<Database> {
|
||||||
|
throw new Error("Not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Saving the newly created database to the database
|
||||||
|
protected async save(): Promise<Database> {
|
||||||
|
// Checking if the connection details are provided
|
||||||
|
if(!this.host || !this.port) throw new MissingConnectionDetails();
|
||||||
|
|
||||||
|
const database = await prisma.database.create({
|
||||||
|
data: {
|
||||||
|
id: this.id,
|
||||||
|
provider: this.type,
|
||||||
|
name: this.name,
|
||||||
|
workspaceId: this.workspaceId,
|
||||||
|
username: this.username,
|
||||||
|
password: this.password,
|
||||||
|
|
||||||
|
host: this.host,
|
||||||
|
port: this.port
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return database;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class DatabaseError extends Error {
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SavingDatabaseError extends DatabaseError {
|
||||||
|
constructor() {
|
||||||
|
super("Error saving the database");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MissingConnectionDetails extends DatabaseError {
|
||||||
|
constructor() {
|
||||||
|
super("Missing connection details");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Database } from "@prisma/client";
|
||||||
|
import { CreateDatabase, SavingDatabaseError } from "./database";
|
||||||
|
|
||||||
|
export class CreateVitessDatabase extends CreateDatabase {
|
||||||
|
constructor(workspaceId: string, name: string) {
|
||||||
|
super(workspaceId, name, "VITESS");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deploy(): Promise<Database> {
|
||||||
|
try {
|
||||||
|
// Saving the database to the database
|
||||||
|
const database = await this.save();
|
||||||
|
return database;
|
||||||
|
} catch (error) {
|
||||||
|
throw new SavingDatabaseError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -73,6 +73,29 @@ model Workspace {
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
Application Application[]
|
Application Application[]
|
||||||
Deployment Deployment[]
|
Deployment Deployment[]
|
||||||
|
Database Database[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model Database {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
name String
|
||||||
|
provider DatabaseProvider
|
||||||
|
|
||||||
|
host String
|
||||||
|
port Int
|
||||||
|
username String
|
||||||
|
password String
|
||||||
|
|
||||||
|
workspaceId String
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
Workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DatabaseProvider {
|
||||||
|
VITESS
|
||||||
|
REDIS
|
||||||
}
|
}
|
||||||
|
|
||||||
model Application {
|
model Application {
|
||||||
|
|
Loading…
Reference in New Issue