-
-
-
-
-
-
}
- />
-
}
- />
-
}
- />
-
}
- />
-
}
- />
-
- }
- />
-
-
-
+
+
-
-
Loading subscription details...}>
-
-
-
-
-
+
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
);
diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/org/[organizationId]/(specific-organization-pages)/@sidebar/default.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/org/[organizationId]/(specific-organization-pages)/@sidebar/default.tsx
index 136465de..246cea91 100644
--- a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/org/[organizationId]/(specific-organization-pages)/@sidebar/default.tsx
+++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/org/[organizationId]/(specific-organization-pages)/@sidebar/default.tsx
@@ -1,16 +1,13 @@
-import { ProFeatureGateDialog } from '@/components/ProFeatureGateDialog';
import { SidebarLink } from '@/components/SidebarLink';
-import { SubscriptionCardSmall } from '@/components/SubscriptionCardSmall';
-import { T } from '@/components/ui/Typography';
-import { fetchSlimOrganizations, getOrganizationSlugByOrganizationId } from '@/data/user/organizations';
import { cn } from '@/utils/cn';
import { notFound } from 'next/navigation';
import { Suspense } from 'react';
import { DesktopSidebarFallback } from '@/components/SidebarComponents/SidebarFallback';
import { SwitcherAndToggle } from '@/components/SidebarComponents/SidebarLogo';
+import { fetchSlimOrganizations, getOrganizationSlugByOrganizationId } from '@/data/user/organizations';
import { organizationParamSchema } from '@/utils/zod-schemas/params';
-import { DollarSign, FileBox, Home, Layers, Settings, UserRound } from 'lucide-react';
+import { Activity, FileText, GitCompare, Home, Layers, MessageCircle, Settings, Shield } from 'lucide-react';
async function OrganizationSidebarInternal({
organizationId,
@@ -24,58 +21,56 @@ async function OrganizationSidebarInternal({
return (
-
-
-
-
-
-
-
}
- />
-
}
- />
-
}
- />
-
}
- />
-
}
- />
-
- }
- />
-
-
- {/*
*/}
-
-
-
-
Loading subscription details...}>
-
-
+
+
+
+
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
);
diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/org/[organizationId]/(specific-organization-pages)/page.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/org/[organizationId]/(specific-organization-pages)/page.tsx
index 1b6800a9..334fe73d 100644
--- a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/org/[organizationId]/(specific-organization-pages)/page.tsx
+++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/org/[organizationId]/(specific-organization-pages)/page.tsx
@@ -51,7 +51,7 @@ async function Dashboard({ params, searchParams }: DashboardProps) {
Export PDF
-
+
Create Project
diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/org/[organizationId]/(specific-organization-pages)/projects/OrganizationProjectsTable.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/org/[organizationId]/(specific-organization-pages)/projects/OrganizationProjectsTable.tsx
index f1c58186..f81c30b3 100644
--- a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/org/[organizationId]/(specific-organization-pages)/projects/OrganizationProjectsTable.tsx
+++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/org/[organizationId]/(specific-organization-pages)/projects/OrganizationProjectsTable.tsx
@@ -1,8 +1,5 @@
"use client";
-import { ProjectStatus } from '@/components/Projects/ProjectsCardList';
import { T } from '@/components/ui/Typography';
-import { ProjectBadge } from '@/components/ui/badge-project';
-import { Button } from '@/components/ui/button';
import { Card } from '@/components/ui/card';
import {
Table,
@@ -12,6 +9,7 @@ import {
TableHeader,
TableRow,
} from '@/components/ui/table';
+import { getRepoDetails } from '@/data/user/repos';
import type { Tables } from '@/lib/database.types';
import {
flexRender,
@@ -23,77 +21,76 @@ import {
type ColumnDef,
type SortingState,
} from '@tanstack/react-table';
-import { format } from 'date-fns';
-import { ChevronsUpDown } from 'lucide-react';
+import { GitBranch } from 'lucide-react';
+import moment from 'moment';
import Link from 'next/link';
-import { useState } from 'react';
+import { useEffect, useState } from 'react';
+
+type ProjectWithRepo = Tables<'projects'> & { repoName: string | null };
type Props = {
projects: Tables<'projects'>[];
};
export function OrganizationProjectsTable({ projects }: Props) {
+ const [projectsWithRepos, setProjectsWithRepos] = useState([]);
+
+ useEffect(() => {
+ const fetchRepoDetails = async () => {
+ const updatedProjects = await Promise.all(
+ projects.map(async (project) => {
+ const repoDetails = await getRepoDetails(project.repo_id);
+ return { ...project, repoName: repoDetails?.name ?? null };
+ })
+ );
+ setProjectsWithRepos(updatedProjects);
+ };
+
+ fetchRepoDetails();
+ }, [projects]);
- const columns: ColumnDef>[] = [
+ const columns: ColumnDef[] = [
{
accessorKey: 'name',
- cell: ({ row }) => {
- return
+ header: 'Name',
+ cell: ({ row }) => (
+
{row.getValue('name')}
- },
- header: ({ column }) => (
- {
- column.toggleSorting(column.getIsSorted() === 'asc');
- }}
- >
-
- Project Name
-
-
-
),
},
{
- accessorKey: 'project_status',
- header: 'Status',
+ accessorKey: 'latest_action_on',
+ header: 'Latest Action On',
cell: ({ row }) => {
- const status = row.getValue('project_status') as ProjectStatus
- return (
-
- {ProjectStatus[status]}
-
- )
+ const date = moment(row.getValue('latest_action_on') as string);
+ return date.format('MMM DD YYYY, HH:mm [hrs]');
},
},
{
- accessorKey: 'created_at',
- cell: info => format(new Date(String(info.getValue())), 'dd MMMM yyyy')
- ,
- header: ({ column }) => (
- {
- column.toggleSorting(column.getIsSorted() === 'asc');
- }}
- >
-
- Created On
-
-
-
+ accessorKey: 'by',
+ header: 'By',
+ cell: ({ row }) => row.getValue('by') || 'unknown',
+ },
+ {
+ accessorKey: 'terraform_working_dir',
+ header: 'Configuration Source / Working Directory',
+ cell: ({ row }) => (
+
+
+
+ {row.original.repoName ? `${row.original.repoName.toLowerCase().replace(/\s+/g, '-').replace(/[A-Z]/g, (letter) => letter.toLowerCase())}` : ''}
+ {row.original.repoName && ((row.getValue('terraform_working_dir') as string) || '') ? '/' : ''}
+ {((row.getValue('terraform_working_dir') as string) || '').replace(/\s+/g, '-').replace(/[A-Z]/g, (letter) => letter.toLowerCase())}
+
+
),
- enableSorting: true,
},
];
const [sorting, setSorting] = useState([]);
const table = useReactTable({
- data: projects,
+ data: projectsWithRepos,
columns,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
@@ -106,26 +103,30 @@ export function OrganizationProjectsTable({ projects }: Props) {
});
if (projects.length === 0) {
-
- return
-
- No projects found
-
-
-
+ return (
+
+
+ No projects found
+
+
+ );
}
return (
-
-
+
{table.getHeaderGroups().map(headerGroup => (
{headerGroup.headers.map(header => (
-
- {flexRender(header.column.columnDef.header, header.getContext())}
+
+ {header.isPlaceholder
+ ? null
+ : flexRender(
+ header.column.columnDef.header,
+ header.getContext()
+ )}
))}
@@ -133,9 +134,9 @@ export function OrganizationProjectsTable({ projects }: Props) {
{table.getRowModel().rows.map(row => (
-
+
{row.getVisibleCells().map(cell => (
-
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
))}
@@ -143,7 +144,7 @@ export function OrganizationProjectsTable({ projects }: Props) {
))}
-
-
+
+
);
-}
+}
\ No newline at end of file
diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/org/[organizationId]/(specific-organization-pages)/create-project/page.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/org/[organizationId]/(specific-organization-pages)/projects/create/page.tsx
similarity index 100%
rename from src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/org/[organizationId]/(specific-organization-pages)/create-project/page.tsx
rename to src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/org/[organizationId]/(specific-organization-pages)/projects/create/page.tsx
diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/org/[organizationId]/(specific-organization-pages)/projects/page.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/org/[organizationId]/(specific-organization-pages)/projects/page.tsx
index 43e1d557..891ad52c 100644
--- a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/org/[organizationId]/(specific-organization-pages)/projects/page.tsx
+++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/org/[organizationId]/(specific-organization-pages)/projects/page.tsx
@@ -1,14 +1,16 @@
-import { CreateProjectDialog } from "@/components/CreateProjectDialog";
import { PageHeading } from "@/components/PageHeading";
import { Pagination } from "@/components/Pagination";
import { Search } from "@/components/Search";
import { T } from "@/components/ui/Typography";
+import { Button } from "@/components/ui/button";
import { getProjects, getProjectsTotalCount } from "@/data/user/projects";
import {
organizationParamSchema,
projectsfilterSchema
} from "@/utils/zod-schemas/params";
+import { Plus } from "lucide-react";
import type { Metadata } from "next";
+import Link from "next/link";
import { Suspense } from "react";
import type { DashboardProps } from "../page";
import { OrganizationProjectsTable } from "./OrganizationProjectsTable";
@@ -44,7 +46,7 @@ export default async function Page({
const filters = projectsfilterSchema.parse(searchParams);
return (
-
+
-
+
+
+
+ Create Project
+
+
{
-
+
-
- }
- />
- {project.team_id && (
- }
- />
- )}
- }
- />
- }
- />
- }
- />
- }
- />
- }
- />
-
-
+
+
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+
);
}
@@ -82,4 +80,4 @@ export async function ProjectSidebar({ params }: { params: unknown }) {
);
-}
+}
\ No newline at end of file
diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/layout.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/layout.tsx
index 835fda44..314b6179 100644
--- a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/layout.tsx
+++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/layout.tsx
@@ -36,6 +36,7 @@ export default async function ProjectLayout({
const project = await getSlimProjectBySlug(projectSlug);
return (
+
diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/page.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/page.tsx
index 5286ba5f..1ea43755 100644
--- a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/page.tsx
+++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/page.tsx
@@ -1,12 +1,9 @@
-import { ChatContainer } from "@/components/chat-container";
-import { T } from "@/components/ui/Typography";
-import { getSlimProjectBySlug } from "@/data/user/projects";
+import { Badge } from "@/components/ui/badge";
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+import { getProjectById, getSlimProjectBySlug } from "@/data/user/projects";
import { projectSlugParamSchema } from "@/utils/zod-schemas/params";
-import { nanoid } from "ai";
+import { CalendarIcon, GitBranchIcon, LayersIcon } from "lucide-react";
import type { Metadata } from "next";
-import { Suspense } from "react";
-import { CommentInput } from "./CommentInput";
-import { ProjectComments } from "./ProjectComments";
type ProjectPageProps = {
params: {
@@ -20,7 +17,6 @@ export async function generateMetadata({
const { projectSlug } = projectSlugParamSchema.parse(params);
const project = await getSlimProjectBySlug(projectSlug);
-
return {
title: `Project | ${project.name}`,
description: `View and manage your project ${project.name}`,
@@ -29,32 +25,51 @@ export async function generateMetadata({
export default async function ProjectPage({ params }: { params: unknown }) {
const { projectSlug } = projectSlugParamSchema.parse(params);
- const project = await getSlimProjectBySlug(projectSlug);
+ const slimProject = await getSlimProjectBySlug(projectSlug);
+ const project = await getProjectById(slimProject.id);
- const newChatId = nanoid();
return (
-
-
-
-
+
+
+
+ {project.name}
+ {project.project_status}
-
-
-
-
Comments
-
-
-
-
-
-
+
+
+
+
+
+ Created: {new Date(project.created_at).toLocaleDateString()}
+
+
+
+ Organization: {project.organization_id}
+
+
+
+ Repository: {project.repo_id}
-
-
+
+
+
+
+
+ Project Details
+
+
+
+
Terraform Working Directory: {project.terraform_working_dir}
+
Managing State: {project.is_managing_state ? 'Yes' : 'No'}
+
In Main Branch: {project.is_in_main_branch ? 'Yes' : 'No'}
+
Generated: {project.is_generated ? 'Yes' : 'No'}
+
Latest Action: {project.latest_action_on ? new Date(project.latest_action_on).toLocaleString() : 'No actions'}
+
+
+
+
);
-}
+}
\ No newline at end of file
diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/app_admin/(admin-pages)/users/GetLoginLinkDialog.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/app_admin/(admin-pages)/users/GetLoginLinkDialog.tsx
index 899d9ce2..2b373f68 100644
--- a/src/app/(dynamic-pages)/(authenticated-pages)/app_admin/(admin-pages)/users/GetLoginLinkDialog.tsx
+++ b/src/app/(dynamic-pages)/(authenticated-pages)/app_admin/(admin-pages)/users/GetLoginLinkDialog.tsx
@@ -1,3 +1,4 @@
+
'use client';
import { Button } from '@/components/ui/button';
import {
diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/onboarding/OnboardingFlow.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/onboarding/OnboardingFlow.tsx
index 705e368f..369c6baf 100644
--- a/src/app/(dynamic-pages)/(authenticated-pages)/onboarding/OnboardingFlow.tsx
+++ b/src/app/(dynamic-pages)/(authenticated-pages)/onboarding/OnboardingFlow.tsx
@@ -32,6 +32,7 @@ export function UserOnboardingFlow({
const [currentStep, setCurrentStep] = useState
(
getInitialFlowState(flowStates, onboardingStatus)
);
+
const { replace } = useRouter();
const nextStep = useCallback(() => {
@@ -53,6 +54,8 @@ export function UserOnboardingFlow({
exit: { opacity: 0, y: -50 },
};
+ console.log(currentStep)
+
return (
- createOrganization(organizationTitle, organizationSlug, { isOnboardingFlow: true }),
- onSuccess: () => {
+ mutationFn: async ({ organizationTitle, organizationSlug }: CreateOrganizationSchema) => {
+ console.log('creating organization mutation');
+ return createOrganization(organizationTitle, organizationSlug, { isOnboardingFlow: true })
+ }
+ ,
+ onSuccess: (data) => {
+ console.log('success', data);
toast({ title: "Organization created!", description: "Your new organization is ready." });
onSuccess();
},
@@ -36,6 +40,7 @@ export function OrganizationCreation({ onSuccess }: OrganizationCreationProps) {
});
const onSubmit = (data: CreateOrganizationSchema) => {
+ console.log('submitting');
createOrgMutation.mutate(data);
};
diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/onboarding/page.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/onboarding/page.tsx
index e89e21e5..45077bbb 100644
--- a/src/app/(dynamic-pages)/(authenticated-pages)/onboarding/page.tsx
+++ b/src/app/(dynamic-pages)/(authenticated-pages)/onboarding/page.tsx
@@ -47,7 +47,10 @@ async function OnboardingFlowWrapper({ userId, userEmail }: { userId: string; us
serverGetLoggedInUser(),
]);
const { userProfile } = onboardingConditions;
+ console.log(userProfile);
const onboardingStatus = authUserMetadataSchema.parse(user.user_metadata);
+ console.log(onboardingStatus);
+ console.log(userEmail);
return (
- await createProjectAction({ organizationId, name, slug }),
- {
- loadingMessage: "Creating project...",
- successMessage: "Project created!",
- errorMessage: "Failed to create project",
- onSuccess: (response) => {
- setOpen(false);
- if (response.status === "success" && response.data) {
- router.push(`/project/${response.data.slug}`);
- }
- },
- },
- );
-
- const onSubmit: SubmitHandler<{ name: string; slug: string }> = (data) => {
- createProjectMutation.mutate({
- organizationId,
- name: data.name,
- slug: data.slug,
- });
- };
-
- return (
- <>
-
-
-
-
- Create Project
-
-
-
-
-
-
-
-
- Create Project
-
- Create a new project and get started.
-
-
-
-
-
-
- >
- );
-}
diff --git a/src/components/CreateProjectForm.tsx b/src/components/CreateProjectForm.tsx
index ef2c4422..02ad532a 100644
--- a/src/components/CreateProjectForm.tsx
+++ b/src/components/CreateProjectForm.tsx
@@ -3,166 +3,269 @@
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
+import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
+import { createProjectAction } from "@/data/user/projects";
+import { useSAToastMutation } from "@/hooks/useSAToastMutation";
+import { generateSlug } from "@/lib/utils";
+import { zodResolver } from "@hookform/resolvers/zod";
import { motion } from "framer-motion";
-import { Check, Github, MonitorOff, Play, RotateCw } from "lucide-react";
+import { Check, Github } from "lucide-react";
+import { useRouter } from "next/navigation";
import { useState } from 'react';
+import { Controller, useForm } from "react-hook-form";
+import { z } from "zod";
+import { InputTags } from "./InputTags";
import { T } from "./ui/Typography";
const MotionCard = motion(Card);
+const createProjectFormSchema = z.object({
+ name: z.string().min(1, "Project name is required"),
+ repository: z.number().int().positive("Repository ID must be a positive integer"),
+ terraformDir: z.string().min(1, "Terraform working directory is required"),
+ managedState: z.boolean(),
+ labels: z.array(z.string()),
+});
+
+type CreateProjectFormData = z.infer;
+
+const repositories = [
+ { id: 12, name: 'Repository 1' },
+ { id: 123, name: 'Repository 2' },
+ { id: 41, name: 'Repository 3' },
+ { id: 24, name: 'Repository 4' },
+ { id: 124, name: 'Repository 5' },
+];
+
export default function CreateProjectForm({ organizationId }: { organizationId: string }) {
- const [selectedRepo, setSelectedRepo] = useState('');
- const [autoQueueRun, setAutoQueueRun] = useState('apply-before-merge');
+ const [selectedRepo, setSelectedRepo] = useState(repositories[0].id);
+ const router = useRouter();
+
+ const { control, handleSubmit, setValue, watch } = useForm({
+ resolver: zodResolver(createProjectFormSchema),
+ defaultValues: {
+ name: "",
+ repository: repositories[0].id,
+ terraformDir: "",
+ managedState: true,
+ labels: [],
+ },
+ });
+
+ const createProjectMutation = useSAToastMutation(
+ async (data: CreateProjectFormData) => {
+ const slug = generateSlug(data.name);
+ return await createProjectAction({
+ organizationId,
+ name: data.name,
+ slug,
+ repoId: data.repository,
+ terraformWorkingDir: data.terraformDir,
+ isManagingState: data.managedState,
+ labels: data.labels,
+ });
+ },
+ {
+ loadingMessage: "Creating project...",
+ successMessage: "Project created!",
+ errorMessage: "Failed to create project",
+ onSuccess: (response) => {
+ if (response.status === "success" && response.data) {
+ router.push(`/project/${response.data.slug}`);
+ }
+ },
+ },
+ );
+
+ const onSubmit = (data: CreateProjectFormData) => {
+ createProjectMutation.mutate(data);
+ };
+
- const repositories = [
- { id: 'repo1', name: 'Repository 1' },
- { id: 'repo2', name: 'Repository 2' },
- { id: 'repo3', name: 'Repository 3' },
- { id: 'repo4', name: 'Repository 4' },
- { id: 'repo5', name: 'Repository 5' },
- ];
return (
-
-
- Create new Project
- Create a new project within your organization.
-
-
- Cancel
- Create Project
-
-
-
-
-
-
- Project Details
- Provide the name of your project
-
-
-
-
-
-
-
-
-
-
- Select a repository
- Choose the repository for your project
-
-
-
- Connected to GitHub
-
-
-
-
-
-
- {repositories.map((repo, index) => (
- setSelectedRepo(repo.id)}
- initial={{ opacity: 0, y: 20 }}
- animate={{ opacity: 1, y: 0 }}
- transition={{ delay: index * 0.1 }}
- >
-
-
- {repo.name}
-
-
- ))}
-
-
-
-
-
-
-
-
-
-
Terraform Configuration
-
Specify the working directory for Terraform
+