Skip to content

Commit bd3ba2a

Browse files
authored
Move createFolder action to effect rpc (#893)
* move createFolder action to effect rpc * formatting * provide org id in rpc auth * formatting
1 parent 74113a4 commit bd3ba2a

File tree

10 files changed

+163
-174
lines changed

10 files changed

+163
-174
lines changed

apps/web/actions/caps/get-shared-spaces.ts

Lines changed: 0 additions & 65 deletions
This file was deleted.

apps/web/actions/folders/createFolder.ts

Lines changed: 0 additions & 62 deletions
This file was deleted.

apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@ import {
77
DialogTitle,
88
Input,
99
} from "@cap/ui";
10+
import type { Folder } from "@cap/web-domain";
1011
import { faFolderPlus } from "@fortawesome/free-solid-svg-icons";
1112
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
1213
import clsx from "clsx";
14+
import { Option } from "effect";
15+
import { useRouter } from "next/navigation";
1316
import React, { useEffect, useRef, useState } from "react";
1417
import { toast } from "sonner";
15-
import { createFolder } from "@/actions/folders/createFolder";
18+
import { useEffectMutation } from "@/lib/EffectRuntime";
19+
import { withRpc } from "@/lib/Rpcs";
1620
import { BlueFolder, NormalFolder, RedFolder, YellowFolder } from "./Folders";
1721

1822
interface Props {
@@ -54,31 +58,33 @@ export const NewFolderDialog: React.FC<Props> = ({
5458
>(null);
5559
const [folderName, setFolderName] = useState<string>("");
5660
const folderRefs = useRef<Record<string, any>>({});
57-
const [loading, setLoading] = useState<boolean>(false);
61+
const router = useRouter();
5862

5963
useEffect(() => {
6064
if (!open) setSelectedColor(null);
6165
}, [open]);
6266

63-
const createFolderHandler = async () => {
64-
if (!selectedColor) return;
65-
try {
66-
setLoading(true);
67-
await createFolder({
68-
name: folderName,
69-
color: selectedColor,
70-
spaceId,
71-
});
67+
const createFolder = useEffectMutation({
68+
mutationFn: (data: { name: string; color: Folder.FolderColor }) =>
69+
withRpc((r) =>
70+
r.FolderCreate({
71+
name: data.name,
72+
color: data.color,
73+
spaceId: Option.fromNullable(spaceId),
74+
parentId: Option.none(),
75+
}),
76+
),
77+
onSuccess: () => {
7278
setFolderName("");
7379
setSelectedColor(null);
7480
onOpenChange(false);
81+
router.refresh();
7582
toast.success("Folder created successfully");
76-
} catch (error: any) {
83+
},
84+
onError: () => {
7785
toast.error("Failed to create folder");
78-
} finally {
79-
setLoading(false);
80-
}
81-
};
86+
},
87+
});
8288

8389
return (
8490
<Dialog open={open} onOpenChange={onOpenChange}>
@@ -141,13 +147,20 @@ export const NewFolderDialog: React.FC<Props> = ({
141147
Cancel
142148
</Button>
143149
<Button
144-
onClick={createFolderHandler}
150+
onClick={() => {
151+
if (selectedColor === null) return;
152+
createFolder.mutate({ name: folderName, color: selectedColor });
153+
}}
145154
size="sm"
146-
spinner={loading}
155+
spinner={createFolder.isPending}
147156
variant="dark"
148-
disabled={!selectedColor || !folderName.trim().length || loading}
157+
disabled={
158+
!selectedColor ||
159+
!folderName.trim().length ||
160+
createFolder.isPending
161+
}
149162
>
150-
{loading ? "Creating..." : "Create"}
163+
{createFolder.isPending ? "Creating..." : "Create"}
151164
</Button>
152165
</DialogFooter>
153166
</DialogContent>

apps/web/app/(org)/dashboard/folder/[id]/components/SubfolderDialog.tsx

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,16 @@ import {
99
DialogTitle,
1010
Input,
1111
} from "@cap/ui";
12+
import type { Folder } from "@cap/web-domain";
1213
import { faFolderPlus } from "@fortawesome/free-solid-svg-icons";
1314
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
1415
import clsx from "clsx";
16+
import { Option } from "effect";
17+
import { useRouter } from "next/navigation";
1518
import React, { useEffect, useRef, useState } from "react";
1619
import { toast } from "sonner";
17-
import { createFolder } from "@/actions/folders/createFolder";
20+
import { useEffectMutation } from "@/lib/EffectRuntime";
21+
import { withRpc } from "@/lib/Rpcs";
1822
import { useDashboardContext } from "../../../Contexts";
1923
import {
2024
BlueFolder,
@@ -26,7 +30,7 @@ import {
2630
interface Props {
2731
open: boolean;
2832
onOpenChange: (open: boolean) => void;
29-
parentFolderId: string;
33+
parentFolderId: Folder.FolderId;
3034
}
3135

3236
const FolderOptions = [
@@ -62,8 +66,8 @@ export const SubfolderDialog: React.FC<Props> = ({
6266
>(null);
6367
const [folderName, setFolderName] = useState<string>("");
6468
const folderRefs = useRef<Record<string, any>>({});
65-
const [loading, setLoading] = useState<boolean>(false);
6669
const { activeSpace } = useDashboardContext();
70+
const router = useRouter();
6771

6872
useEffect(() => {
6973
if (!open) {
@@ -72,26 +76,27 @@ export const SubfolderDialog: React.FC<Props> = ({
7276
}
7377
}, [open]);
7478

75-
const createSubfolderHandler = async () => {
76-
if (!selectedColor) return;
77-
try {
78-
setLoading(true);
79-
await createFolder({
80-
name: folderName,
81-
color: selectedColor,
82-
parentId: parentFolderId,
83-
spaceId: activeSpace?.id,
84-
});
79+
const createSubfolder = useEffectMutation({
80+
mutationFn: (data: { name: string; color: Folder.FolderColor }) =>
81+
withRpc((r) =>
82+
r.FolderCreate({
83+
name: data.name,
84+
color: data.color,
85+
spaceId: Option.fromNullable(activeSpace?.id),
86+
parentId: Option.some(parentFolderId),
87+
}),
88+
),
89+
onSuccess: () => {
8590
setFolderName("");
8691
setSelectedColor(null);
8792
onOpenChange(false);
93+
router.refresh();
8894
toast.success("Subfolder created successfully");
89-
} catch (error: any) {
95+
},
96+
onError: () => {
9097
toast.error("Failed to create subfolder");
91-
} finally {
92-
setLoading(false);
93-
}
94-
};
98+
},
99+
});
95100

96101
return (
97102
<Dialog open={open} onOpenChange={onOpenChange}>
@@ -154,11 +159,21 @@ export const SubfolderDialog: React.FC<Props> = ({
154159
Cancel
155160
</Button>
156161
<Button
157-
onClick={createSubfolderHandler}
162+
onClick={() => {
163+
if (selectedColor === null) return;
164+
createSubfolder.mutate({
165+
name: folderName,
166+
color: selectedColor,
167+
});
168+
}}
158169
size="sm"
159-
spinner={loading}
170+
spinner={createSubfolder.isPending}
160171
variant="dark"
161-
disabled={!selectedColor || !folderName.trim().length || loading}
172+
disabled={
173+
!selectedColor ||
174+
!folderName.trim().length ||
175+
createSubfolder.isPending
176+
}
162177
>
163178
Create
164179
</Button>

packages/web-backend/src/Auth.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@ export const HttpAuthMiddlewareLive = Layer.effect(
4545
),
4646
);
4747

48-
return { id: user.id, email: user.email };
48+
return {
49+
id: user.id,
50+
email: user.email,
51+
activeOrgId: user.activeOrganizationId,
52+
};
4953
}).pipe(
5054
Effect.provideService(Database, database),
5155
Effect.catchTags({
@@ -71,6 +75,7 @@ export const provideOptionalAuth = <A, E, R>(
7175
CurrentUser.context({
7276
id: user.id,
7377
email: user.email,
78+
activeOrgId: user.activeOrganizationId,
7479
}),
7580
),
7681
Option.match({

packages/web-backend/src/Folders/FoldersRpcs.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ export const FolderRpcsLive = Folder.FolderRpcs.toLayer(
1717
() => new InternalError({ type: "database" }),
1818
),
1919
),
20+
FolderCreate: (data) =>
21+
folders
22+
.create(data)
23+
.pipe(
24+
Effect.catchTag(
25+
"DatabaseError",
26+
() => new InternalError({ type: "database" }),
27+
),
28+
),
2029
};
2130
}),
2231
);

0 commit comments

Comments
 (0)