Skip to content

Commit 4b816c1

Browse files
before merge
1 parent b69c949 commit 4b816c1

File tree

11 files changed

+538
-73
lines changed

11 files changed

+538
-73
lines changed

Codespace_Service/src/controllers/codespaceController.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,4 +184,21 @@ export class CodespaceController {
184184
next(error);
185185
}
186186
}
187+
188+
static async createSession(req, res, next) {
189+
try {
190+
const { codespaceId, branchName } = req.body;
191+
const codespace = await CodespaceService.createBranchWithSession(
192+
codespaceId,
193+
branchName
194+
);
195+
196+
res.status(201).json({
197+
codespace,
198+
message: "Session created successfully",
199+
});
200+
} catch (error) {
201+
next(error);
202+
}
203+
}
187204
}

Codespace_Service/src/routes/codespaceRoutes.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ router.get("/", CodespaceController.getCodespaces);
1616

1717
router.get("/:id", validateCodespaceId, CodespaceController.getCodespaceById);
1818

19-
router.post("/:id/share",CodespaceController.shareCodespace);
19+
router.post("/:id/share", CodespaceController.shareCodespace);
20+
21+
router.post("/session", CodespaceController.createSession);
2022

2123
router.post("/", validateCodespaceData, CodespaceController.createCodespace);
2224

@@ -26,7 +28,14 @@ router.put(
2628
validateCodespaceData,
2729
CodespaceController.updateCodespace
2830
);
29-
router.post("/:id/sharebyemail", validateCodespaceId, CodespaceController.shareCodespaceByEmail);
31+
router.post(
32+
"/:id/sharebyemail",
33+
validateCodespaceId,
34+
CodespaceController.shareCodespaceByEmail
35+
);
3036
router.delete("/:id", validateCodespaceId, CodespaceController.deleteCodespace);
31-
router.put("/accept-invitation/:invitationId", CodespaceController.acceptInvitation);
37+
router.put(
38+
"/accept-invitation/:invitationId",
39+
CodespaceController.acceptInvitation
40+
);
3241
export default router;

Codespace_Service/src/services/codespaceService.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,48 @@ export class CodespaceService {
127127
return true;
128128
}
129129

130+
static async createBranchWithSession(codespaceId, branchName, userId) {
131+
// await this.checkUserPermission(codespaceId, userId, ["admin", "owner", "Developer"]);
132+
133+
try {
134+
const { data, error } = await supabase.rpc("create_branch_with_session", {
135+
p_workspace_id: codespaceId,
136+
p_branch_name: branchName,
137+
});
138+
139+
if (error) {
140+
throw new Error(`Failed to create branch: ${error.message}`);
141+
}
142+
143+
return data;
144+
} catch (err) {
145+
console.error("Error in createBranchWithSession:", err);
146+
147+
if (err.message.includes("already exists")) {
148+
const duplicateError = new Error(
149+
`Branch "${branchName}" already exists in this workspace`
150+
);
151+
duplicateError.statusCode = 409;
152+
duplicateError.code = "BRANCH_ALREADY_EXISTS";
153+
throw duplicateError;
154+
}
155+
156+
if (err.message.includes("Workspace not found")) {
157+
const notFoundError = new Error("Workspace not found");
158+
notFoundError.statusCode = 404;
159+
notFoundError.code = "WORKSPACE_NOT_FOUND";
160+
throw notFoundError;
161+
}
162+
163+
const serviceError = new Error(
164+
`Failed to create branch with session: ${err.message}`
165+
);
166+
serviceError.statusCode = err.statusCode || 500;
167+
serviceError.code = err.code || "BRANCH_CREATION_FAILED";
168+
throw serviceError;
169+
}
170+
}
171+
130172
static async checkUserPermission(codespaceId, userId, allowedRoles = []) {
131173
const { data, error } = await supabase
132174
.from("workspace_members")

Frontend/src/App/CodeEditor/GitPanel/BranchSelector.tsx

Lines changed: 74 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,97 @@
11
import { useState } from "react";
22
import { useTheme } from "../../../Contexts/ThemeProvider";
3-
// import type { Branch } from "./GitTypes";
4-
import { ChevronDown, GitBranch } from "lucide-react";
3+
import { ChevronDown, GitBranch, Plus } from "lucide-react";
54
import { useEditorCollaboration } from "../../../Contexts/EditorContext";
6-
7-
// interface BranchSelectorProps {
8-
// branches: Branch[];
9-
// onBranchSelect: (branchName: string) => void;
10-
// }
5+
import CreateBranch from "./CreateBranch";
116

127
const BranchSelector = () => {
138
const { theme } = useTheme();
149
const [isOpen, setIsOpen] = useState(false);
15-
const { codespace, activeSessionIndex, setActiveSessionIndex } =
16-
useEditorCollaboration();
10+
const [showCreateBranch, setShowCreateBranch] = useState(false);
11+
const {
12+
codespace,
13+
activeSessionIndex,
14+
setActiveSessionIndex,
15+
createBranchWithSession,
16+
} = useEditorCollaboration();
1717

18-
// Get the current active branch
19-
// const activeBranch = branches.find((b) => b.isActive)?.name || "main";
2018
const branchSessions = codespace?.sessions || [];
21-
2219
const activeBranch = branchSessions[activeSessionIndex].name || "main";
2320

2421
const toggleDropdown = () => setIsOpen(!isOpen);
2522

26-
// const handleBranchSelect = (branchName: string) => {
27-
// onBranchSelect(branchName);
28-
// setIsOpen(false);
29-
// };
23+
const handleCreateBranch = async (branchName: string) => {
24+
setShowCreateBranch(false);
25+
createBranchWithSession(branchName);
26+
};
3027

31-
return (
32-
<div className="relative">
33-
<button
34-
className={`flex items-center justify-between w-full px-3 py-2 text-sm font-medium ${theme.surface} ${theme.border} border ${theme.text} ${theme.hover} rounded-md`}
35-
onClick={toggleDropdown}
36-
aria-haspopup="true"
37-
aria-expanded={isOpen}
38-
>
39-
<span className="flex items-center">
40-
<GitBranch className="w-4 h-4 mr-2" />
41-
{activeBranch}
42-
</span>
43-
<ChevronDown className="w-4 h-4" />
44-
</button>
28+
if (showCreateBranch) {
29+
return (
30+
<CreateBranch
31+
onCreateBranch={handleCreateBranch}
32+
onCancel={() => setShowCreateBranch(false)}
33+
/>
34+
);
35+
}
4536

46-
{isOpen && (
47-
<div
48-
className={`absolute z-10 w-full mt-1 overflow-auto rounded-md shadow-lg ${theme.surface} ${theme.border} border max-h-60`}
37+
return (
38+
<div className="space-y-2">
39+
<div className="relative">
40+
<button
41+
className={`flex items-center justify-between w-full px-3 py-2 text-sm font-medium ${theme.surface} ${theme.border} border ${theme.text} ${theme.hover} rounded-md`}
42+
onClick={toggleDropdown}
43+
aria-haspopup="true"
44+
aria-expanded={isOpen}
4945
>
50-
<ul className="py-1 text-sm" role="menu" aria-orientation="vertical">
51-
{branchSessions.map((branch, index) => (
52-
<li key={branch.branchId}>
46+
<span className="flex items-center">
47+
<GitBranch className="w-4 h-4 mr-2" />
48+
{activeBranch}
49+
</span>
50+
<ChevronDown className="w-4 h-4" />
51+
</button>
52+
53+
{isOpen && (
54+
<div
55+
className={`absolute z-10 w-full mt-1 overflow-auto rounded-md shadow-lg ${theme.surface} ${theme.border} border max-h-60`}
56+
>
57+
<ul
58+
className="py-1 text-sm"
59+
role="menu"
60+
aria-orientation="vertical"
61+
>
62+
{branchSessions.map((branch, index) => {
63+
return (
64+
<li key={branch.branchId}>
65+
<button
66+
className={`block w-full text-left px-4 py-2 ${
67+
theme.text
68+
} ${index === activeSessionIndex ? theme.active : ""} ${
69+
theme.hover
70+
}`}
71+
role="menuitem"
72+
onClick={() => setActiveSessionIndex(index)}
73+
>
74+
{branch.name}
75+
</button>
76+
</li>
77+
);
78+
})}
79+
<li className={`border-t ${theme.border}`}>
5380
<button
54-
className={`block w-full text-left px-4 py-2 ${theme.text} ${
55-
index === activeSessionIndex ? theme.active : ""
56-
} ${theme.hover}`}
57-
role="menuitem"
58-
// onClick={() => handleBranchSelect(branch.name)}
59-
onClick={() => setActiveSessionIndex(index)}
81+
className={`flex items-center w-full text-left px-4 py-2 ${theme.text} ${theme.hover}`}
82+
onClick={() => {
83+
setShowCreateBranch(true);
84+
setIsOpen(false);
85+
}}
6086
>
61-
{branch.name}
87+
<Plus className="w-4 h-4 mr-2" />
88+
Create new branch
6289
</button>
6390
</li>
64-
))}
65-
</ul>
66-
</div>
67-
)}
91+
</ul>
92+
</div>
93+
)}
94+
</div>
6895
</div>
6996
);
7097
};
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { useState } from "react";
2+
import { useTheme } from "../../../Contexts/ThemeProvider";
3+
import { GitBranch, Plus, X } from "lucide-react";
4+
5+
interface CreateBranchProps {
6+
onCreateBranch: (branchName: string) => void;
7+
onCancel?: () => void;
8+
}
9+
10+
const CreateBranch = ({ onCreateBranch, onCancel }: CreateBranchProps) => {
11+
const { theme } = useTheme();
12+
const [branchName, setBranchName] = useState("");
13+
const [isCreating, setIsCreating] = useState(false);
14+
15+
const handleSubmit = async (e: React.FormEvent) => {
16+
e.preventDefault();
17+
if (!branchName.trim()) return;
18+
19+
setIsCreating(true);
20+
try {
21+
await onCreateBranch(branchName.trim());
22+
setBranchName("");
23+
onCancel?.();
24+
} catch (error) {
25+
console.error("Failed to create branch:", error);
26+
} finally {
27+
setIsCreating(false);
28+
}
29+
};
30+
31+
const handleCancel = () => {
32+
setBranchName("");
33+
onCancel?.();
34+
};
35+
36+
return (
37+
<div className={`p-4 ${theme.surface} ${theme.border} border rounded-md`}>
38+
<div className="flex items-center mb-3">
39+
<GitBranch className="w-4 h-4 mr-2" />
40+
<h3 className={`text-sm font-medium ${theme.text}`}>
41+
Create New Branch
42+
</h3>
43+
</div>
44+
45+
<form onSubmit={handleSubmit} className="space-y-3">
46+
<div>
47+
<input
48+
type="text"
49+
value={branchName}
50+
onChange={(e) => setBranchName(e.target.value)}
51+
placeholder="Enter branch name..."
52+
className={`w-full px-3 py-2 text-sm ${theme.surface} ${theme.border} border ${theme.text} rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500`}
53+
disabled={isCreating}
54+
autoFocus
55+
/>
56+
</div>
57+
58+
<div className="flex justify-end space-x-2">
59+
<button
60+
type="button"
61+
onClick={handleCancel}
62+
className={`flex items-center px-3 py-2 text-sm font-medium ${theme.text} ${theme.hover} rounded-md`}
63+
disabled={isCreating}
64+
>
65+
<X className="w-4 h-4 mr-1" />
66+
Cancel
67+
</button>
68+
69+
<button
70+
type="submit"
71+
disabled={!branchName.trim() || isCreating}
72+
className={`flex items-center px-3 py-2 text-sm font-medium bg-blue-600 text-white rounded-md hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed`}
73+
>
74+
<Plus className="w-4 h-4 mr-1" />
75+
{isCreating ? "Creating..." : "Create"}
76+
</button>
77+
</div>
78+
</form>
79+
</div>
80+
);
81+
};
82+
83+
export default CreateBranch;

Frontend/src/App/CodeEditor/PanelSystem.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,14 +261,14 @@ export const PanelSystem: React.FC<PanelSystemProps> = ({
261261

262262
<div
263263
ref={leftResizeRef}
264-
className={`w-[2px] ${theme.active} hover:bg-blue-500 cursor-ew-resize transition-colors flex-shrink-0`}
264+
className={`w-[4px] ${theme.active} hover:bg-blue-500 cursor-ew-resize transition-colors flex-shrink-0`}
265265
></div>
266266

267267
<div className="flex-1 flex flex-col min-w-0">{children}</div>
268268

269269
<div
270270
ref={rightResizeRef}
271-
className={`w-[2px] ${theme.active} hover:bg-blue-500 cursor-ew-resize transition-colors flex-shrink-0`}
271+
className={`w-[4px] ${theme.active} hover:bg-blue-500 cursor-ew-resize transition-colors flex-shrink-0`}
272272
></div>
273273

274274
<div

0 commit comments

Comments
 (0)