Initial Planlink implementation
This commit is contained in:
+1569
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,238 @@
|
||||
const API_URL = import.meta.env.VITE_API_URL || "http://localhost:4000/api";
|
||||
|
||||
async function request(path, options = {}) {
|
||||
const res = await fetch(`${API_URL}${path}`, {
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(options.headers || {})
|
||||
},
|
||||
...options
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
if (!res.ok) {
|
||||
const message = data?.message || "request failed";
|
||||
throw new Error(message);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
export function login(payload) {
|
||||
return request("/auth/login", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function register(payload) {
|
||||
return request("/auth/register", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function logout() {
|
||||
return request("/auth/logout", { method: "POST" });
|
||||
}
|
||||
|
||||
export function me() {
|
||||
return request("/me");
|
||||
}
|
||||
|
||||
export function listProjects() {
|
||||
return request("/projects");
|
||||
}
|
||||
|
||||
export function createProject(payload) {
|
||||
return request("/projects", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function listProjectMembers(projectId) {
|
||||
return request(`/projects/${projectId}/members`);
|
||||
}
|
||||
|
||||
export function inviteProjectMember(projectId, payload) {
|
||||
return request(`/projects/${projectId}/invites`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function listProjectInvites(projectId) {
|
||||
return request(`/projects/${projectId}/invites`);
|
||||
}
|
||||
|
||||
export function acceptProjectInvite(projectId, token) {
|
||||
return request(`/projects/${projectId}/invites/accept`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ token })
|
||||
});
|
||||
}
|
||||
|
||||
export function revokeProjectInvite(projectId, inviteId) {
|
||||
return request(`/projects/${projectId}/invites/revoke`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ inviteId })
|
||||
});
|
||||
}
|
||||
|
||||
export function listBoards(projectId) {
|
||||
return request(`/boards?projectId=${projectId}`);
|
||||
}
|
||||
|
||||
export function createBoard(payload) {
|
||||
return request("/boards", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function listBoardActivity(boardId) {
|
||||
return request(`/boards/${boardId}/activity`);
|
||||
}
|
||||
|
||||
export function listLists(boardId) {
|
||||
return request(`/lists?boardId=${boardId}`);
|
||||
}
|
||||
|
||||
export function createList(payload) {
|
||||
return request("/lists", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function updateList(id, payload) {
|
||||
return request(`/lists/${id}`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function listCards(listId) {
|
||||
return request(`/cards?listId=${listId}`);
|
||||
}
|
||||
|
||||
export function createCard(payload) {
|
||||
return request("/cards", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function updateCard(id, payload) {
|
||||
return request(`/cards/${id}`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function listComments(cardId) {
|
||||
return request(`/cards/${cardId}/comments`);
|
||||
}
|
||||
|
||||
export function addComment(cardId, body) {
|
||||
return request(`/cards/${cardId}/comments`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ body })
|
||||
});
|
||||
}
|
||||
|
||||
export function listAttachments(cardId) {
|
||||
return request(`/cards/${cardId}/attachments`);
|
||||
}
|
||||
|
||||
export function addAttachment(cardId, payload) {
|
||||
return request(`/cards/${cardId}/attachments`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function listCardMembers(cardId) {
|
||||
return request(`/cards/${cardId}/members`);
|
||||
}
|
||||
|
||||
export function addCardMember(cardId, userId) {
|
||||
return request(`/cards/${cardId}/members`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ userId })
|
||||
});
|
||||
}
|
||||
|
||||
export function listCardLabels(cardId) {
|
||||
return request(`/cards/${cardId}/labels`);
|
||||
}
|
||||
|
||||
export function addCardLabel(cardId, labelId) {
|
||||
return request(`/cards/${cardId}/labels`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ labelId })
|
||||
});
|
||||
}
|
||||
|
||||
export function listLabels(projectId) {
|
||||
return request(`/labels?projectId=${projectId}`);
|
||||
}
|
||||
|
||||
export function createLabel(payload) {
|
||||
return request("/labels", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function createPlannerRequest(payload) {
|
||||
return request("/planner/requests", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function runPlannerRequest(id) {
|
||||
return request(`/planner/requests/${id}/run`, { method: "POST" });
|
||||
}
|
||||
|
||||
export function streamPlannerRequest(id, onDelta, onDone, onError) {
|
||||
const url = `${API_URL}/planner/requests/${id}/stream`;
|
||||
const source = new EventSource(url, { withCredentials: true });
|
||||
|
||||
source.addEventListener("delta", (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
onDelta?.(data.delta || "");
|
||||
});
|
||||
|
||||
source.addEventListener("done", () => {
|
||||
onDone?.();
|
||||
source.close();
|
||||
});
|
||||
|
||||
source.addEventListener("error", () => {
|
||||
onError?.();
|
||||
source.close();
|
||||
});
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
export function saveManualPlanner(payload) {
|
||||
return request("/planner/manual", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function listPlanners() {
|
||||
return request("/planner");
|
||||
}
|
||||
|
||||
export function updatePlanner(id, outputText) {
|
||||
return request(`/planner/requests/${id}`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify({ outputText })
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import React from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import App from "./App.jsx";
|
||||
import "./styles.css";
|
||||
|
||||
createRoot(document.getElementById("root")).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
@@ -0,0 +1,12 @@
|
||||
import { io } from "socket.io-client";
|
||||
|
||||
const SOCKET_URL = import.meta.env.VITE_SOCKET_URL || "http://localhost:4000";
|
||||
|
||||
let socket;
|
||||
|
||||
export function getSocket() {
|
||||
if (!socket) {
|
||||
socket = io(SOCKET_URL, { withCredentials: true });
|
||||
}
|
||||
return socket;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
color-scheme: light dark;
|
||||
}
|
||||
Reference in New Issue
Block a user