diff --git a/apps/webapp/app/components/navigation/SideMenu.tsx b/apps/webapp/app/components/navigation/SideMenu.tsx index 1f660acdf..c32089a3a 100644 --- a/apps/webapp/app/components/navigation/SideMenu.tsx +++ b/apps/webapp/app/components/navigation/SideMenu.tsx @@ -357,23 +357,31 @@ function ProjectSelector({
- {organization.projects.map((p) => { - const isSelected = p.id === project.id; - return ( - - {p.name} - -
- } - isSelected={isSelected} - icon="folder" - /> - ); - })} + {organization.projects.length > 0 ? ( + organization.projects.map((p) => { + const isSelected = p.id === project.id; + return ( + + {p.name} + + + } + isSelected={isSelected} + icon="folder" + /> + ); + }) + ) : ( + + )}
))} diff --git a/apps/webapp/app/presenters/NewOrganizationPresenter.server.ts b/apps/webapp/app/presenters/NewOrganizationPresenter.server.ts index 4d3fe0aae..c81fda8a0 100644 --- a/apps/webapp/app/presenters/NewOrganizationPresenter.server.ts +++ b/apps/webapp/app/presenters/NewOrganizationPresenter.server.ts @@ -10,11 +10,16 @@ export class NewOrganizationPresenter { public async call({ userId }: { userId: User["id"] }) { const organizations = await this.#prismaClient.organization.findMany({ + select: { + projects: { + where: { deletedAt: null }, + }, + }, where: { members: { some: { userId } } }, }); return { - hasOrganizations: organizations.length > 0, + hasOrganizations: organizations.filter((o) => o.projects.length > 0).length > 0, }; } } diff --git a/apps/webapp/app/presenters/SelectBestProjectPresenter.server.ts b/apps/webapp/app/presenters/SelectBestProjectPresenter.server.ts index 55adbd6cc..e87239cc5 100644 --- a/apps/webapp/app/presenters/SelectBestProjectPresenter.server.ts +++ b/apps/webapp/app/presenters/SelectBestProjectPresenter.server.ts @@ -14,7 +14,7 @@ export class SelectBestProjectPresenter { const projectId = await getCurrentProjectId(request); if (projectId) { const project = await this.#prismaClient.project.findUnique({ - where: { id: projectId, organization: { members: { some: { userId } } } }, + where: { id: projectId, deletedAt: null, organization: { members: { some: { userId } } } }, include: { organization: true }, }); if (project) { @@ -28,6 +28,7 @@ export class SelectBestProjectPresenter { organization: true, }, where: { + deletedAt: null, organization: { members: { some: { userId } }, }, diff --git a/apps/webapp/app/routes/_app.orgs.new/route.tsx b/apps/webapp/app/routes/_app.orgs.new/route.tsx index 61b23c6da..c30e0bd4a 100644 --- a/apps/webapp/app/routes/_app.orgs.new/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.new/route.tsx @@ -3,7 +3,7 @@ import { parse } from "@conform-to/zod"; import { RadioGroup } from "@radix-ui/react-radio-group"; import type { ActionFunction, LoaderFunctionArgs } from "@remix-run/node"; import { json, redirect } from "@remix-run/node"; -import { Form, useActionData } from "@remix-run/react"; +import { Form, useActionData, useNavigation } from "@remix-run/react"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import { z } from "zod"; import { MainCenteredContainer } from "~/components/layout/AppLayout"; @@ -23,7 +23,7 @@ import { createOrganization } from "~/models/organization.server"; import { NewOrganizationPresenter } from "~/presenters/NewOrganizationPresenter.server"; import { commitCurrentProjectSession, setCurrentProjectId } from "~/services/currentProject.server"; import { requireUserId } from "~/services/session.server"; -import { plansPath, projectPath, rootPath, selectPlanPath } from "~/utils/pathBuilder"; +import { projectPath, rootPath, selectPlanPath } from "~/utils/pathBuilder"; const schema = z.object({ orgName: z.string().min(3).max(50), @@ -86,6 +86,7 @@ export default function NewOrganizationPage() { const { hasOrganizations } = useTypedLoaderData(); const lastSubmission = useActionData(); const { isManagedCloud } = useFeatures(); + const navigation = useNavigation(); const [form, { orgName, projectName }] = useForm({ id: "create-organization", @@ -95,8 +96,11 @@ export default function NewOrganizationPage() { return parse(formData, { schema }); }, shouldRevalidate: "onSubmit", + shouldValidate: "onSubmit", }); + const isLoading = navigation.state === "submitting" || navigation.state === "loading"; + return ( @@ -161,7 +165,12 @@ export default function NewOrganizationPage() { + } diff --git a/apps/webapp/app/services/deleteOrganization.server.ts b/apps/webapp/app/services/deleteOrganization.server.ts index 4887d5d18..c93ac8b0d 100644 --- a/apps/webapp/app/services/deleteOrganization.server.ts +++ b/apps/webapp/app/services/deleteOrganization.server.ts @@ -1,15 +1,9 @@ +import { DateFormatter } from "@internationalized/date"; import { PrismaClient } from "@trigger.dev/database"; import { prisma } from "~/db.server"; -import { DisableJobService } from "./jobs/disableJob.server"; -import { AuthenticatedEnvironment } from "./apiAuth.server"; -import { DeleteJobService } from "./jobs/deleteJob.server"; -import { DeleteEndpointService } from "./endpoints/deleteEndpointService"; -import { logger } from "./logger.server"; -import { DisableScheduleSourceService } from "./schedules/disableScheduleSource.server"; import { featuresForRequest } from "~/features.server"; -import { DeleteProjectService } from "./deleteProject.server"; import { BillingService } from "./billing.server"; -import { DateFormatter } from "@internationalized/date"; +import { DeleteProjectService } from "./deleteProject.server"; export class DeleteOrganizationService { #prismaClient: PrismaClient; diff --git a/apps/webapp/app/services/deleteProject.server.ts b/apps/webapp/app/services/deleteProject.server.ts index 8b030c3fa..fa9f1b792 100644 --- a/apps/webapp/app/services/deleteProject.server.ts +++ b/apps/webapp/app/services/deleteProject.server.ts @@ -1,13 +1,12 @@ import { PrismaClient } from "@trigger.dev/database"; import { prisma } from "~/db.server"; -import { DisableJobService } from "./jobs/disableJob.server"; -import { AuthenticatedEnvironment } from "./apiAuth.server"; -import { DeleteJobService } from "./jobs/deleteJob.server"; import { DeleteEndpointService } from "./endpoints/deleteEndpointService"; import { logger } from "./logger.server"; import { DisableScheduleSourceService } from "./schedules/disableScheduleSource.server"; -type Options = { projectId: string; userId: string } | { projectSlug: string; userId: string }; +type Options = ({ projectId: string } | { projectSlug: string }) & { + userId: string; +}; export class DeleteProjectService { #prismaClient: PrismaClient; @@ -52,7 +51,7 @@ export class DeleteProjectService { } if (project.deletedAt) { - throw new Error("Project already deleted"); + return; } //disable and delete all jobs