diff --git a/apps/dashboard/src/actions/change-team-action.ts b/apps/dashboard/src/actions/change-team-action.ts
index 783421b22..26920a3ec 100644
--- a/apps/dashboard/src/actions/change-team-action.ts
+++ b/apps/dashboard/src/actions/change-team-action.ts
@@ -7,11 +7,14 @@ import { redirect } from "next/navigation";
import { action } from "./safe-action";
import { changeTeamSchema } from "./schema";
-export const changeTeamAction = action(changeTeamSchema, async ({ teamId }) => {
- const supabase = createClient();
- const user = await updateUser(supabase, { team_id: teamId });
+export const changeTeamAction = action(
+ changeTeamSchema,
+ async ({ teamId, redirectTo }) => {
+ const supabase = createClient();
+ const user = await updateUser(supabase, { team_id: teamId });
- revalidateTag(`user_${user.data.id}`);
+ revalidateTag(`user_${user.data.id}`);
- redirect("/");
-});
+ redirect(redirectTo);
+ }
+);
diff --git a/apps/dashboard/src/actions/create-team-action.ts b/apps/dashboard/src/actions/create-team-action.ts
index bfe403f29..df54c2720 100644
--- a/apps/dashboard/src/actions/create-team-action.ts
+++ b/apps/dashboard/src/actions/create-team-action.ts
@@ -3,16 +3,24 @@
import { createTeam, updateUser } from "@midday/supabase/mutations";
import { createClient } from "@midday/supabase/server";
import { revalidateTag } from "next/cache";
+import { redirect } from "next/navigation";
import { action } from "./safe-action";
import { createTeamSchema } from "./schema";
-export const createTeamAction = action(createTeamSchema, async ({ name }) => {
- const supabase = createClient();
- const { team_id } = await createTeam(supabase, { name });
- const user = await updateUser(supabase, { team_id });
+export const createTeamAction = action(
+ createTeamSchema,
+ async ({ name, redirectTo }) => {
+ const supabase = createClient();
+ const { team_id } = await createTeam(supabase, { name });
+ const user = await updateUser(supabase, { team_id });
- revalidateTag(`user_${user.data.id}`);
- revalidateTag(`teams_${user.data.id}`);
+ revalidateTag(`user_${user.data.id}`);
+ revalidateTag(`teams_${user.data.id}`);
- return team_id;
-});
+ if (redirectTo) {
+ redirect(redirectTo);
+ }
+
+ return team_id;
+ }
+);
diff --git a/apps/dashboard/src/actions/invite-team-members-action.ts b/apps/dashboard/src/actions/invite-team-members-action.ts
index b334362c1..9a8f6f488 100644
--- a/apps/dashboard/src/actions/invite-team-members-action.ts
+++ b/apps/dashboard/src/actions/invite-team-members-action.ts
@@ -6,9 +6,9 @@ import { getI18n } from "@midday/email/locales";
import { getUser } from "@midday/supabase/cached-queries";
import { createClient } from "@midday/supabase/server";
import { renderAsync } from "@react-email/components";
-import { revalidatePath } from "next/cache";
import { revalidateTag } from "next/cache";
import { headers } from "next/headers";
+import { redirect } from "next/navigation";
import { Resend } from "resend";
import { action } from "./safe-action";
import { inviteTeamMembersSchema } from "./schema";
@@ -17,7 +17,7 @@ const resend = new Resend(env.RESEND_API_KEY);
export const inviteTeamMembersAction = action(
inviteTeamMembersSchema,
- async ({ invites }) => {
+ async ({ invites, redirectTo }) => {
const supabase = createClient();
const user = await getUser();
@@ -69,5 +69,9 @@ export const inviteTeamMembersAction = action(
const htmlEmails = await Promise.all(emails);
await resend.batch.send(htmlEmails);
+
+ if (redirectTo) {
+ redirect(redirectTo);
+ }
}
);
diff --git a/apps/dashboard/src/actions/leave-team-action.ts b/apps/dashboard/src/actions/leave-team-action.ts
index 4a01f74b5..c6c4bc766 100644
--- a/apps/dashboard/src/actions/leave-team-action.ts
+++ b/apps/dashboard/src/actions/leave-team-action.ts
@@ -4,12 +4,13 @@ import { getTeamMembers, getUser } from "@midday/supabase/cached-queries";
import { leaveTeam } from "@midday/supabase/mutations";
import { createClient } from "@midday/supabase/server";
import { revalidateTag } from "next/cache";
+import { redirect } from "next/navigation";
import { action } from "./safe-action";
import { leaveTeamSchema } from "./schema";
export const leaveTeamAction = action(
leaveTeamSchema,
- async ({ teamId, role }) => {
+ async ({ teamId, role, redirectTo }) => {
const supabase = createClient();
const user = await getUser();
const { data: teamMembersData } = await getTeamMembers();
@@ -31,6 +32,10 @@ export const leaveTeamAction = action(
revalidateTag(`user_${user.data.id}`);
revalidateTag(`teams_${user.data.id}`);
+ if (redirectTo) {
+ redirect(redirectTo);
+ }
+
return data;
}
);
diff --git a/apps/dashboard/src/actions/schema.ts b/apps/dashboard/src/actions/schema.ts
index 94bbf8389..1f34dd7b0 100644
--- a/apps/dashboard/src/actions/schema.ts
+++ b/apps/dashboard/src/actions/schema.ts
@@ -141,10 +141,12 @@ export const updaterMenuSchema = z.array(
export const changeTeamSchema = z.object({
teamId: z.string(),
+ redirectTo: z.string(),
});
export const createTeamSchema = z.object({
name: z.string(),
+ redirectTo: z.string().optional(),
});
export const changeUserRoleSchema = z.object({
@@ -160,6 +162,7 @@ export const deleteTeamMemberSchema = z.object({
export const leaveTeamSchema = z.object({
teamId: z.string(),
+ redirectTo: z.string().optional(),
role: z.enum(["owner", "member"]),
});
@@ -174,6 +177,7 @@ export const inviteTeamMembersSchema = z.object({
role: z.enum(["owner", "member"]),
})
),
+ redirectTo: z.string().optional(),
});
export type InviteTeamMembersFormValues = z.infer<
diff --git a/apps/dashboard/src/app/[locale]/@dashboard/(root)/layout.tsx b/apps/dashboard/src/app/[locale]/@dashboard/(root)/layout.tsx
index bf892cb0e..940d088e1 100644
--- a/apps/dashboard/src/app/[locale]/@dashboard/(root)/layout.tsx
+++ b/apps/dashboard/src/app/[locale]/@dashboard/(root)/layout.tsx
@@ -4,10 +4,21 @@ import { ConnectBankModal } from "@/components/modals/connect-bank-modal";
import { SelectAccountModal } from "@/components/modals/select-account-modal";
import { Sidebar } from "@/components/sidebar";
import { getCountryCode } from "@midday/location";
+import { getUser } from "@midday/supabase/cached-queries";
+import { redirect } from "next/navigation";
-export default function Layout({ children }: { children: React.ReactNode }) {
+export default async function Layout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ const user = await getUser();
const countryCode = getCountryCode();
+ if (!user?.data?.team) {
+ redirect("/teams");
+ }
+
return (
diff --git a/apps/dashboard/src/app/[locale]/@teams/create/page.tsx b/apps/dashboard/src/app/[locale]/@dashboard/teams/create/page.tsx
similarity index 72%
rename from apps/dashboard/src/app/[locale]/@teams/create/page.tsx
rename to apps/dashboard/src/app/[locale]/@dashboard/teams/create/page.tsx
index b8330d0d9..cd2586cda 100644
--- a/apps/dashboard/src/app/[locale]/@teams/create/page.tsx
+++ b/apps/dashboard/src/app/[locale]/@dashboard/teams/create/page.tsx
@@ -1,7 +1,5 @@
-import { getTeams } from "@midday/supabase/cached-queries";
-import { Button } from "@midday/ui/button";
+import { CreateTeamForm } from "@/components/create-team-form";
import { Icons } from "@midday/ui/icons";
-import { Input } from "@midday/ui/input";
import Link from "next/link";
export default async function CreateTeam() {
@@ -30,14 +28,7 @@ export default async function CreateTeam() {
-
-
-
+
diff --git a/apps/dashboard/src/app/[locale]/@teams/invite/[code]/page.tsx b/apps/dashboard/src/app/[locale]/@dashboard/teams/invite/[code]/page.tsx
similarity index 100%
rename from apps/dashboard/src/app/[locale]/@teams/invite/[code]/page.tsx
rename to apps/dashboard/src/app/[locale]/@dashboard/teams/invite/[code]/page.tsx
diff --git a/apps/dashboard/src/app/[locale]/@teams/invite/page.tsx b/apps/dashboard/src/app/[locale]/@dashboard/teams/invite/page.tsx
similarity index 50%
rename from apps/dashboard/src/app/[locale]/@teams/invite/page.tsx
rename to apps/dashboard/src/app/[locale]/@dashboard/teams/invite/page.tsx
index f72bf76cd..18e4479bd 100644
--- a/apps/dashboard/src/app/[locale]/@teams/invite/page.tsx
+++ b/apps/dashboard/src/app/[locale]/@dashboard/teams/invite/page.tsx
@@ -1,7 +1,5 @@
-import { getTeams } from "@midday/supabase/cached-queries";
-import { Button } from "@midday/ui/button";
+import { InviteForm } from "@/components/invite-form";
import { Icons } from "@midday/ui/icons";
-import { Input } from "@midday/ui/input";
import Link from "next/link";
export default async function InviteMembers() {
@@ -25,26 +23,7 @@ export default async function InviteMembers() {
Invite new members by email address
-
-
-
-
-
-
-
-
-
-
+
diff --git a/apps/dashboard/src/app/[locale]/@teams/(select)/page.tsx b/apps/dashboard/src/app/[locale]/@dashboard/teams/page.tsx
similarity index 92%
rename from apps/dashboard/src/app/[locale]/@teams/(select)/page.tsx
rename to apps/dashboard/src/app/[locale]/@dashboard/teams/page.tsx
index b949d6a85..aa9ff0f22 100644
--- a/apps/dashboard/src/app/[locale]/@teams/(select)/page.tsx
+++ b/apps/dashboard/src/app/[locale]/@dashboard/teams/page.tsx
@@ -8,7 +8,7 @@ export default async function Teams() {
const { data } = await getTeams();
if (!data.length > 0) {
- redirect("/create");
+ redirect("/teams/create");
}
return (
@@ -32,7 +32,7 @@ export default async function Teams() {
-
+
Create team
diff --git a/apps/dashboard/src/app/[locale]/layout.tsx b/apps/dashboard/src/app/[locale]/layout.tsx
index c42f27b67..8ecdd67c6 100644
--- a/apps/dashboard/src/app/[locale]/layout.tsx
+++ b/apps/dashboard/src/app/[locale]/layout.tsx
@@ -1,24 +1,20 @@
-import { getUser } from "@midday/supabase/cached-queries";
+import { createClient } from "@midday/supabase/server";
import { Providers } from "./providers";
export default async function Layout({
dashboard,
login,
- teams,
params: { locale },
}: {
dashboard: React.ReactNode;
login: React.ReactNode;
- teams: React.ReactNode;
params: { locale: string };
}) {
- let content = login;
+ const supabase = createClient();
- const user = await getUser();
+ const {
+ data: { session },
+ } = await supabase.auth.getSession();
- if (user?.data) {
- content = user?.data.team ? dashboard : teams;
- }
-
- return {content};
+ return {session ? dashboard : login};
}
diff --git a/apps/dashboard/src/components/create-team-form.tsx b/apps/dashboard/src/components/create-team-form.tsx
new file mode 100644
index 000000000..06c6defe0
--- /dev/null
+++ b/apps/dashboard/src/components/create-team-form.tsx
@@ -0,0 +1,42 @@
+"use client";
+
+import { createTeamAction } from "@/actions/create-team-action";
+import { Button } from "@midday/ui/button";
+import { Input } from "@midday/ui/input";
+import { Loader2 } from "lucide-react";
+import { useAction } from "next-safe-action/hook";
+import { useRouter } from "next/navigation";
+import { useState } from "react";
+
+export function CreateTeamForm() {
+ const [name, setName] = useState("");
+ const router = useRouter();
+ const createTeam = useAction(createTeamAction);
+
+ return (
+ <>
+ setName(evt.target.value)}
+ />
+
+
+ >
+ );
+}
diff --git a/apps/dashboard/src/components/delete-team.tsx b/apps/dashboard/src/components/delete-team.tsx
index 97752e1f1..076ae4ba8 100644
--- a/apps/dashboard/src/components/delete-team.tsx
+++ b/apps/dashboard/src/components/delete-team.tsx
@@ -27,7 +27,7 @@ import { useRouter } from "next/navigation";
export function DeleteTeam({ teamId }) {
const router = useRouter();
const deleteTeam = useAction(deleteTeamAction, {
- onSuccess: () => router.push("/"),
+ onSuccess: () => router.push("/teams"),
});
return (
diff --git a/apps/dashboard/src/components/invite-form.tsx b/apps/dashboard/src/components/invite-form.tsx
new file mode 100644
index 000000000..1fe809a2c
--- /dev/null
+++ b/apps/dashboard/src/components/invite-form.tsx
@@ -0,0 +1,161 @@
+"use client";
+
+import { inviteTeamMembersAction } from "@/actions/invite-team-members-action";
+import {
+ InviteTeamMembersFormValues,
+ inviteTeamMembersSchema,
+} from "@/actions/schema";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { Button } from "@midday/ui/button";
+import { Form, FormControl, FormField, FormItem } from "@midday/ui/form";
+import { Icons } from "@midday/ui/icons";
+import { Input } from "@midday/ui/input";
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@midday/ui/select";
+import { useToast } from "@midday/ui/use-toast";
+import { Loader2 } from "lucide-react";
+import { useAction } from "next-safe-action/hook";
+import Link from "next/link";
+import { useRouter } from "next/navigation";
+import { useFieldArray, useForm } from "react-hook-form";
+
+export function InviteForm() {
+ const router = useRouter();
+ const { toast } = useToast();
+
+ const inviteMembers = useAction(inviteTeamMembersAction, {
+ onError: () => {
+ toast({
+ duration: 3500,
+ variant: "error",
+ title: "Something went wrong pleaase try again.",
+ });
+ },
+ });
+
+ const form = useForm({
+ resolver: zodResolver(inviteTeamMembersSchema),
+ defaultValues: {
+ invites: [
+ {
+ email: "",
+ role: "member",
+ },
+ ],
+ },
+ });
+
+ const onSubmit = form.handleSubmit((data) => {
+ inviteMembers.execute({
+ // Remove invites without email (last appended invite validation)
+ invites: data.invites.filter((invite) => invite.email !== undefined),
+ redirectTo: "/",
+ });
+ });
+
+ const { fields, append } = useFieldArray({
+ name: "invites",
+ control: form.control,
+ });
+
+ return (
+
+
+ );
+}
diff --git a/apps/dashboard/src/components/modals/create-team-modal.tsx b/apps/dashboard/src/components/modals/create-team-modal.tsx
index d1ef26835..c941d79e2 100644
--- a/apps/dashboard/src/components/modals/create-team-modal.tsx
+++ b/apps/dashboard/src/components/modals/create-team-modal.tsx
@@ -12,9 +12,11 @@ import { Loader2 } from "lucide-react";
import { useAction } from "next-safe-action/hook";
import { useState } from "react";
-export function CreateTeamModal() {
+export function CreateTeamModal({ onOpenChange }) {
const [name, setName] = useState("");
- const createTeam = useAction(createTeamAction);
+ const createTeam = useAction(createTeamAction, {
+ onSuccess: () => onOpenChange(false),
+ });
return (
diff --git a/apps/dashboard/src/components/tables/select-team/table.tsx b/apps/dashboard/src/components/tables/select-team/table.tsx
index 3d9e0f813..752f2f41a 100644
--- a/apps/dashboard/src/components/tables/select-team/table.tsx
+++ b/apps/dashboard/src/components/tables/select-team/table.tsx
@@ -34,7 +34,12 @@ export function SelectTeamTable({ data }) {
diff --git a/apps/dashboard/src/components/tables/teams/table.tsx b/apps/dashboard/src/components/tables/teams/table.tsx
index 757e83210..a9c48c2c1 100644
--- a/apps/dashboard/src/components/tables/teams/table.tsx
+++ b/apps/dashboard/src/components/tables/teams/table.tsx
@@ -42,7 +42,6 @@ import {
import { MoreHorizontal } from "lucide-react";
import { Loader2 } from "lucide-react";
import { useAction } from "next-safe-action/hook";
-import { useRouter } from "next/navigation";
import * as React from "react";
export type Payment = {
@@ -84,18 +83,11 @@ export const columns: ColumnDef
[] = [
{
id: "actions",
cell: ({ row }) => {
- const router = useRouter();
const { toast } = useToast();
- const manageTeam = useAction(changeTeamAction, {
- onSuccess: () => router.push("/settings"),
- });
-
- const viewTeam = useAction(changeTeamAction, {
- onSuccess: () => router.push("/"),
- });
+ const manageTeam = useAction(changeTeamAction);
+ const viewTeam = useAction(changeTeamAction);
const leaveTeam = useAction(leaveTeamAction, {
- onSuccess: () => router.push("/teams"),
onError: () => {
toast({
duration: 3500,
@@ -136,7 +128,12 @@ export const columns: ColumnDef[] = [
@@ -144,7 +141,10 @@ export const columns: ColumnDef
[] = [
-
+
{teams.map(({ team }) => {
return (
changeTeam.execute({ teamId: team.id })}
+ onClick={() =>
+ changeTeam.execute({ teamId: team.id, redirectTo: "/" })
+ }
>