Initial Planlink implementation
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
<!doctype html>
|
||||
<html lang="es" class="h-full">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Planlink</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
</head>
|
||||
<body class="h-full bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-slate-100">
|
||||
<div id="root" class="h-full"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
Generated
+2614
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "planlink-client",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"socket.io-client": "^4.7.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"postcss": "^8.4.41",
|
||||
"tailwindcss": "^3.4.10",
|
||||
"vite": "^5.4.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {}
|
||||
}
|
||||
};
|
||||
+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;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
export default {
|
||||
darkMode: "class",
|
||||
content: ["./index.html", "./src/**/*.{js,jsx}"],
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
display: ["Space Grotesk", "ui-sans-serif", "system-ui"]
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: []
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
port: 5173
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user