@autoship/react 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AutoshipButton.d.ts +7 -0
- package/dist/AutoshipButton.d.ts.map +1 -0
- package/dist/AutoshipButton.js +67 -0
- package/dist/AutoshipProvider.d.ts +15 -0
- package/dist/AutoshipProvider.d.ts.map +1 -0
- package/dist/AutoshipProvider.js +15 -0
- package/dist/QuestionDialog.d.ts +9 -0
- package/dist/QuestionDialog.d.ts.map +1 -0
- package/dist/QuestionDialog.js +85 -0
- package/dist/SnapdevButton.d.ts +7 -0
- package/dist/SnapdevButton.d.ts.map +1 -0
- package/dist/SnapdevButton.js +67 -0
- package/dist/SnapdevProvider.d.ts +15 -0
- package/dist/SnapdevProvider.d.ts.map +1 -0
- package/dist/SnapdevProvider.js +15 -0
- package/dist/TaskDetailDialog.d.ts +9 -0
- package/dist/TaskDetailDialog.d.ts.map +1 -0
- package/dist/TaskDetailDialog.js +172 -0
- package/dist/TaskDialog.d.ts +7 -0
- package/dist/TaskDialog.d.ts.map +1 -0
- package/dist/TaskDialog.js +85 -0
- package/dist/TaskList.d.ts +23 -0
- package/dist/TaskList.d.ts.map +1 -0
- package/dist/TaskList.js +126 -0
- package/dist/cli/autoship.d.ts +3 -0
- package/dist/cli/autoship.d.ts.map +1 -0
- package/dist/cli/autoship.js +37 -0
- package/dist/cli/init.d.ts +2 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +281 -0
- package/dist/cli/setup.d.ts +3 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js +284 -0
- package/dist/cli/snapdev.d.ts +3 -0
- package/dist/cli/snapdev.d.ts.map +1 -0
- package/dist/cli/snapdev.js +42 -0
- package/dist/hooks/useAutoship.d.ts +18 -0
- package/dist/hooks/useAutoship.d.ts.map +1 -0
- package/dist/hooks/useAutoship.js +23 -0
- package/dist/hooks/useSnapdev.d.ts +18 -0
- package/dist/hooks/useSnapdev.d.ts.map +1 -0
- package/dist/hooks/useSnapdev.js +23 -0
- package/dist/hooks/useTasks.d.ts +8 -0
- package/dist/hooks/useTasks.d.ts.map +1 -0
- package/dist/hooks/useTasks.js +57 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/migrations/20250118000000_initial_schema.sql +143 -0
- package/package.json +46 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface AutoshipButtonProps {
|
|
3
|
+
position?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
|
|
4
|
+
showTaskList?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function AutoshipButton({ position, showTaskList, }: AutoshipButtonProps): React.ReactElement;
|
|
7
|
+
//# sourceMappingURL=AutoshipButton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AutoshipButton.d.ts","sourceRoot":"","sources":["../src/AutoshipButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAIxC,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,cAAc,GAAG,aAAa,GAAG,WAAW,GAAG,UAAU,CAAC;IACrE,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,wBAAgB,cAAc,CAAC,EAC7B,QAAyB,EACzB,YAAmB,GACpB,EAAE,mBAAmB,GAAG,KAAK,CAAC,YAAY,CAkJ1C"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { TaskDialog } from "./TaskDialog";
|
|
4
|
+
import { TaskList } from "./TaskList";
|
|
5
|
+
export function AutoshipButton({ position = "bottom-right", showTaskList = true, }) {
|
|
6
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
7
|
+
const [view, setView] = useState("menu");
|
|
8
|
+
const positionStyles = {
|
|
9
|
+
"bottom-right": { bottom: 20, right: 20 },
|
|
10
|
+
"bottom-left": { bottom: 20, left: 20 },
|
|
11
|
+
"top-right": { top: 20, right: 20 },
|
|
12
|
+
"top-left": { top: 20, left: 20 },
|
|
13
|
+
};
|
|
14
|
+
const handleClose = () => {
|
|
15
|
+
setIsOpen(false);
|
|
16
|
+
setView("menu");
|
|
17
|
+
};
|
|
18
|
+
return (_jsxs(_Fragment, { children: [_jsx("button", { onClick: () => setIsOpen(true), style: {
|
|
19
|
+
position: "fixed",
|
|
20
|
+
...positionStyles[position],
|
|
21
|
+
width: 56,
|
|
22
|
+
height: 56,
|
|
23
|
+
borderRadius: "50%",
|
|
24
|
+
backgroundColor: "#10b981",
|
|
25
|
+
color: "white",
|
|
26
|
+
border: "none",
|
|
27
|
+
cursor: "pointer",
|
|
28
|
+
boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
|
|
29
|
+
display: "flex",
|
|
30
|
+
alignItems: "center",
|
|
31
|
+
justifyContent: "center",
|
|
32
|
+
fontSize: 24,
|
|
33
|
+
zIndex: 9999,
|
|
34
|
+
}, "aria-label": "Open Autoship", children: _jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "currentColor", xmlns: "http://www.w3.org/2000/svg", children: [_jsx("path", { d: "M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", fill: "none" }), _jsxs("g", { children: [_jsx("path", { d: "M18 6l-1.5 1.5L18 9l1.5-1.5L18 6z", fill: "currentColor" }), _jsx("path", { d: "M20.5 3.5l-1 1L20.5 5.5l1-1L20.5 3.5z", fill: "currentColor" }), _jsx("path", { d: "M16.5 2.5l-0.5 0.5L16.5 3.5l0.5-0.5L16.5 2.5z", fill: "currentColor" })] })] }) }), isOpen && (_jsx("div", { style: {
|
|
35
|
+
position: "fixed",
|
|
36
|
+
inset: 0,
|
|
37
|
+
backgroundColor: "rgba(0,0,0,0.5)",
|
|
38
|
+
display: "flex",
|
|
39
|
+
alignItems: "center",
|
|
40
|
+
justifyContent: "center",
|
|
41
|
+
zIndex: 10000,
|
|
42
|
+
}, onClick: handleClose, children: _jsxs("div", { style: {
|
|
43
|
+
backgroundColor: "white",
|
|
44
|
+
borderRadius: 12,
|
|
45
|
+
padding: 24,
|
|
46
|
+
minWidth: 400,
|
|
47
|
+
maxWidth: "90vw",
|
|
48
|
+
maxHeight: "80vh",
|
|
49
|
+
overflow: "auto",
|
|
50
|
+
}, onClick: (e) => e.stopPropagation(), children: [view === "menu" && (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: [_jsx("h2", { style: { margin: 0, marginBottom: 8 }, children: "Autoship" }), _jsx("button", { onClick: () => setView("new"), style: {
|
|
51
|
+
padding: "12px 16px",
|
|
52
|
+
backgroundColor: "#10b981",
|
|
53
|
+
color: "white",
|
|
54
|
+
border: "none",
|
|
55
|
+
borderRadius: 8,
|
|
56
|
+
cursor: "pointer",
|
|
57
|
+
fontSize: 16,
|
|
58
|
+
}, children: "Submit New Request" }), showTaskList && (_jsx("button", { onClick: () => setView("list"), style: {
|
|
59
|
+
padding: "12px 16px",
|
|
60
|
+
backgroundColor: "#f3f4f6",
|
|
61
|
+
color: "#374151",
|
|
62
|
+
border: "1px solid #e5e7eb",
|
|
63
|
+
borderRadius: 8,
|
|
64
|
+
cursor: "pointer",
|
|
65
|
+
fontSize: 16,
|
|
66
|
+
}, children: "View My Requests" }))] })), view === "new" && (_jsx(TaskDialog, { onClose: handleClose, onBack: () => setView("menu") })), view === "list" && _jsx(TaskList, { onBack: () => setView("menu") })] }) }))] }));
|
|
67
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
import React from "react";
|
|
3
|
+
export interface AutoshipContextValue {
|
|
4
|
+
supabase: SupabaseClient;
|
|
5
|
+
userId?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function useAutoshipContext(): AutoshipContextValue;
|
|
8
|
+
export interface AutoshipProviderProps {
|
|
9
|
+
supabaseUrl: string;
|
|
10
|
+
supabaseAnonKey: string;
|
|
11
|
+
userId?: string;
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
}
|
|
14
|
+
export declare function AutoshipProvider({ supabaseUrl, supabaseAnonKey, userId, children, }: AutoshipProviderProps): React.ReactElement;
|
|
15
|
+
//# sourceMappingURL=AutoshipProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AutoshipProvider.d.ts","sourceRoot":"","sources":["../src/AutoshipProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAgB,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,KAA6C,MAAM,OAAO,CAAC;AAElE,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,cAAc,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID,wBAAgB,kBAAkB,IAAI,oBAAoB,CAMzD;AAED,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,WAAW,EACX,eAAe,EACf,MAAM,EACN,QAAQ,GACT,EAAE,qBAAqB,GAAG,KAAK,CAAC,YAAY,CAW5C"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createClient } from "@supabase/supabase-js";
|
|
3
|
+
import { createContext, useContext, useMemo } from "react";
|
|
4
|
+
const AutoshipContext = createContext(null);
|
|
5
|
+
export function useAutoshipContext() {
|
|
6
|
+
const ctx = useContext(AutoshipContext);
|
|
7
|
+
if (!ctx) {
|
|
8
|
+
throw new Error("useAutoshipContext must be used within AutoshipProvider");
|
|
9
|
+
}
|
|
10
|
+
return ctx;
|
|
11
|
+
}
|
|
12
|
+
export function AutoshipProvider({ supabaseUrl, supabaseAnonKey, userId, children, }) {
|
|
13
|
+
const supabase = useMemo(() => createClient(supabaseUrl, supabaseAnonKey), [supabaseUrl, supabaseAnonKey]);
|
|
14
|
+
return (_jsx(AutoshipContext.Provider, { value: { supabase, userId }, children: children }));
|
|
15
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Task } from "./TaskList";
|
|
3
|
+
export interface QuestionDialogProps {
|
|
4
|
+
task: Task;
|
|
5
|
+
onBack: () => void;
|
|
6
|
+
onAnswered: () => void;
|
|
7
|
+
}
|
|
8
|
+
export declare function QuestionDialog({ task, onBack, onAnswered, }: QuestionDialogProps): React.ReactElement;
|
|
9
|
+
//# sourceMappingURL=QuestionDialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QuestionDialog.d.ts","sourceRoot":"","sources":["../src/QuestionDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,MAAM,EACN,UAAU,GACX,EAAE,mBAAmB,GAAG,KAAK,CAAC,YAAY,CA4I1C"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { useAutoshipContext } from "./AutoshipProvider";
|
|
4
|
+
export function QuestionDialog({ task, onBack, onAnswered, }) {
|
|
5
|
+
const { supabase } = useAutoshipContext();
|
|
6
|
+
const [answers, setAnswers] = useState({});
|
|
7
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
8
|
+
const questions = task.task_questions || [];
|
|
9
|
+
const unansweredQuestions = questions.filter((q) => !q.answer);
|
|
10
|
+
const handleSubmit = async () => {
|
|
11
|
+
if (Object.keys(answers).length === 0)
|
|
12
|
+
return;
|
|
13
|
+
setIsSubmitting(true);
|
|
14
|
+
try {
|
|
15
|
+
// Update each question in task_questions table
|
|
16
|
+
for (const [questionId, answer] of Object.entries(answers)) {
|
|
17
|
+
const { error } = await supabase
|
|
18
|
+
.from("task_questions")
|
|
19
|
+
.update({
|
|
20
|
+
answer: answer,
|
|
21
|
+
answered_at: new Date().toISOString(),
|
|
22
|
+
})
|
|
23
|
+
.eq("id", questionId);
|
|
24
|
+
if (error)
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
// Check if all questions are now answered
|
|
28
|
+
const remainingUnanswered = unansweredQuestions.filter((q) => !answers[q.id]);
|
|
29
|
+
const allAnswered = remainingUnanswered.length === 0;
|
|
30
|
+
// Update task status if all questions answered
|
|
31
|
+
if (allAnswered && (task.status === "needs_info" || task.status === "blocked")) {
|
|
32
|
+
const { error: statusError } = await supabase
|
|
33
|
+
.from("agent_tasks")
|
|
34
|
+
.update({ status: "pending" })
|
|
35
|
+
.eq("id", task.id);
|
|
36
|
+
if (statusError)
|
|
37
|
+
throw statusError;
|
|
38
|
+
}
|
|
39
|
+
onAnswered();
|
|
40
|
+
onBack();
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
console.error("Failed to submit answers:", err);
|
|
44
|
+
alert("Failed to submit. Please try again.");
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
setIsSubmitting(false);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
return (_jsxs("div", { children: [_jsxs("div", { style: { display: "flex", alignItems: "center", marginBottom: 16 }, children: [_jsx("button", { type: "button", onClick: onBack, style: {
|
|
51
|
+
marginRight: 12,
|
|
52
|
+
background: "none",
|
|
53
|
+
border: "none",
|
|
54
|
+
fontSize: 20,
|
|
55
|
+
cursor: "pointer",
|
|
56
|
+
padding: 4,
|
|
57
|
+
}, children: "\u2190" }), _jsxs("h2", { style: { margin: 0, fontSize: 18 }, children: ["Questions: ", task.title] })] }), _jsx("p", { style: { color: "#666", marginBottom: 16, fontSize: 14 }, children: "The AI needs some clarification before proceeding. Please answer the questions below." }), unansweredQuestions.length === 0 ? (_jsx("p", { style: { color: "#10b981" }, children: "All questions have been answered!" })) : (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [unansweredQuestions.map((q, index) => (_jsxs("div", { children: [_jsxs("label", { style: {
|
|
58
|
+
display: "block",
|
|
59
|
+
marginBottom: 6,
|
|
60
|
+
fontWeight: 500,
|
|
61
|
+
fontSize: 14,
|
|
62
|
+
}, children: [index + 1, ". ", q.question] }), _jsx("textarea", { value: answers[q.id] || "", onChange: (e) => setAnswers({ ...answers, [q.id]: e.target.value }), placeholder: "Your answer...", rows: 3, style: {
|
|
63
|
+
width: "100%",
|
|
64
|
+
padding: 10,
|
|
65
|
+
borderRadius: 6,
|
|
66
|
+
border: "1px solid #ddd",
|
|
67
|
+
fontSize: 14,
|
|
68
|
+
resize: "vertical",
|
|
69
|
+
boxSizing: "border-box",
|
|
70
|
+
fontFamily: "inherit",
|
|
71
|
+
} })] }, q.id))), _jsx("button", { onClick: handleSubmit, disabled: isSubmitting || Object.keys(answers).length === 0, style: {
|
|
72
|
+
padding: 12,
|
|
73
|
+
backgroundColor: isSubmitting || Object.keys(answers).length === 0
|
|
74
|
+
? "#a7f3d0"
|
|
75
|
+
: "#10b981",
|
|
76
|
+
color: "white",
|
|
77
|
+
border: "none",
|
|
78
|
+
borderRadius: 6,
|
|
79
|
+
cursor: isSubmitting || Object.keys(answers).length === 0
|
|
80
|
+
? "not-allowed"
|
|
81
|
+
: "pointer",
|
|
82
|
+
fontSize: 16,
|
|
83
|
+
fontWeight: 500,
|
|
84
|
+
}, children: isSubmitting ? "Submitting..." : "Submit Answers" })] }))] }));
|
|
85
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface SnapdevButtonProps {
|
|
3
|
+
position?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
|
|
4
|
+
showTaskList?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function SnapdevButton({ position, showTaskList, }: SnapdevButtonProps): React.ReactElement;
|
|
7
|
+
//# sourceMappingURL=SnapdevButton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SnapdevButton.d.ts","sourceRoot":"","sources":["../src/SnapdevButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAIxC,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,cAAc,GAAG,aAAa,GAAG,WAAW,GAAG,UAAU,CAAC;IACrE,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,wBAAgB,aAAa,CAAC,EAC5B,QAAyB,EACzB,YAAmB,GACpB,EAAE,kBAAkB,GAAG,KAAK,CAAC,YAAY,CAkJzC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { TaskDialog } from "./TaskDialog";
|
|
4
|
+
import { TaskList } from "./TaskList";
|
|
5
|
+
export function SnapdevButton({ position = "bottom-right", showTaskList = true, }) {
|
|
6
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
7
|
+
const [view, setView] = useState("menu");
|
|
8
|
+
const positionStyles = {
|
|
9
|
+
"bottom-right": { bottom: 20, right: 20 },
|
|
10
|
+
"bottom-left": { bottom: 20, left: 20 },
|
|
11
|
+
"top-right": { top: 20, right: 20 },
|
|
12
|
+
"top-left": { top: 20, left: 20 },
|
|
13
|
+
};
|
|
14
|
+
const handleClose = () => {
|
|
15
|
+
setIsOpen(false);
|
|
16
|
+
setView("menu");
|
|
17
|
+
};
|
|
18
|
+
return (_jsxs(_Fragment, { children: [_jsx("button", { onClick: () => setIsOpen(true), style: {
|
|
19
|
+
position: "fixed",
|
|
20
|
+
...positionStyles[position],
|
|
21
|
+
width: 56,
|
|
22
|
+
height: 56,
|
|
23
|
+
borderRadius: "50%",
|
|
24
|
+
backgroundColor: "#10b981",
|
|
25
|
+
color: "white",
|
|
26
|
+
border: "none",
|
|
27
|
+
cursor: "pointer",
|
|
28
|
+
boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
|
|
29
|
+
display: "flex",
|
|
30
|
+
alignItems: "center",
|
|
31
|
+
justifyContent: "center",
|
|
32
|
+
fontSize: 24,
|
|
33
|
+
zIndex: 9999,
|
|
34
|
+
}, "aria-label": "Open Snapdev", children: _jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "currentColor", xmlns: "http://www.w3.org/2000/svg", children: [_jsx("path", { d: "M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", fill: "none" }), _jsxs("g", { children: [_jsx("path", { d: "M18 6l-1.5 1.5L18 9l1.5-1.5L18 6z", fill: "currentColor" }), _jsx("path", { d: "M20.5 3.5l-1 1L20.5 5.5l1-1L20.5 3.5z", fill: "currentColor" }), _jsx("path", { d: "M16.5 2.5l-0.5 0.5L16.5 3.5l0.5-0.5L16.5 2.5z", fill: "currentColor" })] })] }) }), isOpen && (_jsx("div", { style: {
|
|
35
|
+
position: "fixed",
|
|
36
|
+
inset: 0,
|
|
37
|
+
backgroundColor: "rgba(0,0,0,0.5)",
|
|
38
|
+
display: "flex",
|
|
39
|
+
alignItems: "center",
|
|
40
|
+
justifyContent: "center",
|
|
41
|
+
zIndex: 10000,
|
|
42
|
+
}, onClick: handleClose, children: _jsxs("div", { style: {
|
|
43
|
+
backgroundColor: "white",
|
|
44
|
+
borderRadius: 12,
|
|
45
|
+
padding: 24,
|
|
46
|
+
minWidth: 400,
|
|
47
|
+
maxWidth: "90vw",
|
|
48
|
+
maxHeight: "80vh",
|
|
49
|
+
overflow: "auto",
|
|
50
|
+
}, onClick: (e) => e.stopPropagation(), children: [view === "menu" && (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: [_jsx("h2", { style: { margin: 0, marginBottom: 8 }, children: "Snapdev" }), _jsx("button", { onClick: () => setView("new"), style: {
|
|
51
|
+
padding: "12px 16px",
|
|
52
|
+
backgroundColor: "#10b981",
|
|
53
|
+
color: "white",
|
|
54
|
+
border: "none",
|
|
55
|
+
borderRadius: 8,
|
|
56
|
+
cursor: "pointer",
|
|
57
|
+
fontSize: 16,
|
|
58
|
+
}, children: "Submit New Request" }), showTaskList && (_jsx("button", { onClick: () => setView("list"), style: {
|
|
59
|
+
padding: "12px 16px",
|
|
60
|
+
backgroundColor: "#f3f4f6",
|
|
61
|
+
color: "#374151",
|
|
62
|
+
border: "1px solid #e5e7eb",
|
|
63
|
+
borderRadius: 8,
|
|
64
|
+
cursor: "pointer",
|
|
65
|
+
fontSize: 16,
|
|
66
|
+
}, children: "View My Requests" }))] })), view === "new" && (_jsx(TaskDialog, { onClose: handleClose, onBack: () => setView("menu") })), view === "list" && _jsx(TaskList, { onBack: () => setView("menu") })] }) }))] }));
|
|
67
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
import React from "react";
|
|
3
|
+
export interface SnapdevContextValue {
|
|
4
|
+
supabase: SupabaseClient;
|
|
5
|
+
userId?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function useSnapdevContext(): SnapdevContextValue;
|
|
8
|
+
export interface SnapdevProviderProps {
|
|
9
|
+
supabaseUrl: string;
|
|
10
|
+
supabaseAnonKey: string;
|
|
11
|
+
userId?: string;
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
}
|
|
14
|
+
export declare function SnapdevProvider({ supabaseUrl, supabaseAnonKey, userId, children, }: SnapdevProviderProps): React.ReactElement;
|
|
15
|
+
//# sourceMappingURL=SnapdevProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SnapdevProvider.d.ts","sourceRoot":"","sources":["../src/SnapdevProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAgB,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,KAA6C,MAAM,OAAO,CAAC;AAElE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,cAAc,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID,wBAAgB,iBAAiB,IAAI,mBAAmB,CAMvD;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,wBAAgB,eAAe,CAAC,EAC9B,WAAW,EACX,eAAe,EACf,MAAM,EACN,QAAQ,GACT,EAAE,oBAAoB,GAAG,KAAK,CAAC,YAAY,CAW3C"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createClient } from "@supabase/supabase-js";
|
|
3
|
+
import { createContext, useContext, useMemo } from "react";
|
|
4
|
+
const SnapdevContext = createContext(null);
|
|
5
|
+
export function useSnapdevContext() {
|
|
6
|
+
const ctx = useContext(SnapdevContext);
|
|
7
|
+
if (!ctx) {
|
|
8
|
+
throw new Error("useSnapdevContext must be used within SnapdevProvider");
|
|
9
|
+
}
|
|
10
|
+
return ctx;
|
|
11
|
+
}
|
|
12
|
+
export function SnapdevProvider({ supabaseUrl, supabaseAnonKey, userId, children, }) {
|
|
13
|
+
const supabase = useMemo(() => createClient(supabaseUrl, supabaseAnonKey), [supabaseUrl, supabaseAnonKey]);
|
|
14
|
+
return (_jsx(SnapdevContext.Provider, { value: { supabase, userId }, children: children }));
|
|
15
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Task } from "./TaskList";
|
|
3
|
+
export interface TaskDetailDialogProps {
|
|
4
|
+
task: Task;
|
|
5
|
+
onBack: () => void;
|
|
6
|
+
onUpdated: () => void;
|
|
7
|
+
}
|
|
8
|
+
export declare function TaskDetailDialog({ task, onBack, onUpdated, }: TaskDetailDialogProps): React.ReactElement;
|
|
9
|
+
//# sourceMappingURL=TaskDetailDialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskDetailDialog.d.ts","sourceRoot":"","sources":["../src/TaskDetailDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,MAAM,EACN,SAAS,GACV,EAAE,qBAAqB,GAAG,KAAK,CAAC,YAAY,CAoU5C"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { useAutoshipContext } from "./AutoshipProvider";
|
|
4
|
+
export function TaskDetailDialog({ task, onBack, onUpdated, }) {
|
|
5
|
+
const { supabase } = useAutoshipContext();
|
|
6
|
+
const [answers, setAnswers] = useState({});
|
|
7
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
8
|
+
const questions = task.task_questions || [];
|
|
9
|
+
const unansweredQuestions = questions.filter((q) => !q.answer);
|
|
10
|
+
const answeredQuestions = questions.filter((q) => q.answer);
|
|
11
|
+
const statusColors = {
|
|
12
|
+
pending: "#f59e0b",
|
|
13
|
+
in_progress: "#3b82f6",
|
|
14
|
+
complete: "#10b981",
|
|
15
|
+
failed: "#ef4444",
|
|
16
|
+
blocked: "#f59e0b",
|
|
17
|
+
needs_info: "#8b5cf6",
|
|
18
|
+
};
|
|
19
|
+
const statusLabels = {
|
|
20
|
+
pending: "Pending",
|
|
21
|
+
in_progress: "In Progress",
|
|
22
|
+
complete: "Complete",
|
|
23
|
+
failed: "Failed",
|
|
24
|
+
blocked: "Blocked",
|
|
25
|
+
needs_info: "Needs Info",
|
|
26
|
+
};
|
|
27
|
+
const handleSubmitAnswers = async () => {
|
|
28
|
+
if (Object.keys(answers).length === 0)
|
|
29
|
+
return;
|
|
30
|
+
setIsSubmitting(true);
|
|
31
|
+
try {
|
|
32
|
+
// Update each question in task_questions table
|
|
33
|
+
for (const [questionId, answer] of Object.entries(answers)) {
|
|
34
|
+
const { error } = await supabase
|
|
35
|
+
.from("task_questions")
|
|
36
|
+
.update({
|
|
37
|
+
answer: answer,
|
|
38
|
+
answered_at: new Date().toISOString(),
|
|
39
|
+
})
|
|
40
|
+
.eq("id", questionId);
|
|
41
|
+
if (error)
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
// Check if all questions are now answered
|
|
45
|
+
const remainingUnanswered = unansweredQuestions.filter((q) => !answers[q.id]);
|
|
46
|
+
const allAnswered = remainingUnanswered.length === 0;
|
|
47
|
+
// Update task status if all questions answered
|
|
48
|
+
if (allAnswered && (task.status === "needs_info" || task.status === "blocked")) {
|
|
49
|
+
const { error: statusError } = await supabase
|
|
50
|
+
.from("agent_tasks")
|
|
51
|
+
.update({ status: "pending" })
|
|
52
|
+
.eq("id", task.id);
|
|
53
|
+
if (statusError)
|
|
54
|
+
throw statusError;
|
|
55
|
+
}
|
|
56
|
+
onUpdated();
|
|
57
|
+
onBack();
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
console.error("Failed to submit answers:", err);
|
|
61
|
+
alert("Failed to submit. Please try again.");
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
setIsSubmitting(false);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const formatDate = (dateStr) => {
|
|
68
|
+
return new Date(dateStr).toLocaleString();
|
|
69
|
+
};
|
|
70
|
+
return (_jsxs("div", { children: [_jsxs("div", { style: { display: "flex", alignItems: "center", marginBottom: 16 }, children: [_jsx("button", { type: "button", onClick: onBack, style: {
|
|
71
|
+
marginRight: 12,
|
|
72
|
+
background: "none",
|
|
73
|
+
border: "none",
|
|
74
|
+
fontSize: 20,
|
|
75
|
+
cursor: "pointer",
|
|
76
|
+
padding: 4,
|
|
77
|
+
}, children: "\u2190" }), _jsx("h2", { style: { margin: 0, fontSize: 18, flex: 1 }, children: "Task Details" }), _jsx("span", { style: {
|
|
78
|
+
backgroundColor: statusColors[task.status] || "#9ca3af",
|
|
79
|
+
color: "white",
|
|
80
|
+
padding: "4px 10px",
|
|
81
|
+
borderRadius: 12,
|
|
82
|
+
fontSize: 12,
|
|
83
|
+
}, children: statusLabels[task.status] || task.status })] }), _jsxs("div", { style: {
|
|
84
|
+
padding: 12,
|
|
85
|
+
backgroundColor: "#f9fafb",
|
|
86
|
+
borderRadius: 8,
|
|
87
|
+
marginBottom: 16,
|
|
88
|
+
}, children: [_jsx("h3", { style: { margin: "0 0 8px 0", fontSize: 16 }, children: task.title }), _jsx("p", { style: {
|
|
89
|
+
margin: 0,
|
|
90
|
+
fontSize: 14,
|
|
91
|
+
color: "#4b5563",
|
|
92
|
+
lineHeight: 1.5,
|
|
93
|
+
whiteSpace: "pre-wrap",
|
|
94
|
+
}, children: task.description }), _jsxs("p", { style: {
|
|
95
|
+
margin: "12px 0 0 0",
|
|
96
|
+
fontSize: 12,
|
|
97
|
+
color: "#9ca3af",
|
|
98
|
+
}, children: ["Created: ", formatDate(task.created_at)] }), task.pr_url && (_jsx("a", { href: task.pr_url, target: "_blank", rel: "noopener noreferrer", style: {
|
|
99
|
+
display: "inline-block",
|
|
100
|
+
marginTop: 8,
|
|
101
|
+
fontSize: 13,
|
|
102
|
+
color: "#10b981",
|
|
103
|
+
}, children: "View Pull Request" }))] }), questions.length > 0 && (_jsxs("div", { children: [_jsx("h3", { style: { margin: "0 0 12px 0", fontSize: 15, color: "#374151" }, children: "Agent Feedback & Questions" }), answeredQuestions.length > 0 && (_jsxs("div", { style: { marginBottom: 16 }, children: [_jsx("p", { style: {
|
|
104
|
+
margin: "0 0 8px 0",
|
|
105
|
+
fontSize: 12,
|
|
106
|
+
color: "#6b7280",
|
|
107
|
+
textTransform: "uppercase",
|
|
108
|
+
fontWeight: 600,
|
|
109
|
+
}, children: "Previous Q&A" }), _jsx("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: answeredQuestions.map((q) => (_jsxs("div", { style: {
|
|
110
|
+
padding: 12,
|
|
111
|
+
backgroundColor: "#f0fdf4",
|
|
112
|
+
borderRadius: 8,
|
|
113
|
+
borderLeft: "3px solid #10b981",
|
|
114
|
+
}, children: [_jsxs("p", { style: {
|
|
115
|
+
margin: 0,
|
|
116
|
+
fontSize: 14,
|
|
117
|
+
fontWeight: 500,
|
|
118
|
+
color: "#166534",
|
|
119
|
+
}, children: ["Q: ", q.question] }), _jsxs("p", { style: {
|
|
120
|
+
margin: "8px 0 0 0",
|
|
121
|
+
fontSize: 14,
|
|
122
|
+
color: "#15803d",
|
|
123
|
+
}, children: ["A: ", q.answer] }), q.answered_at && (_jsxs("p", { style: {
|
|
124
|
+
margin: "6px 0 0 0",
|
|
125
|
+
fontSize: 11,
|
|
126
|
+
color: "#6b7280",
|
|
127
|
+
}, children: ["Answered: ", formatDate(q.answered_at)] }))] }, q.id))) })] })), unansweredQuestions.length > 0 && (_jsxs("div", { children: [_jsx("p", { style: {
|
|
128
|
+
margin: "0 0 8px 0",
|
|
129
|
+
fontSize: 12,
|
|
130
|
+
color: "#059669",
|
|
131
|
+
textTransform: "uppercase",
|
|
132
|
+
fontWeight: 600,
|
|
133
|
+
}, children: "Questions Awaiting Your Response" }), _jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: [unansweredQuestions.map((q, index) => (_jsxs("div", { style: {
|
|
134
|
+
padding: 12,
|
|
135
|
+
backgroundColor: "#ecfdf5",
|
|
136
|
+
borderRadius: 8,
|
|
137
|
+
borderLeft: "3px solid #10b981",
|
|
138
|
+
}, children: [_jsxs("label", { style: {
|
|
139
|
+
display: "block",
|
|
140
|
+
marginBottom: 8,
|
|
141
|
+
fontWeight: 500,
|
|
142
|
+
fontSize: 14,
|
|
143
|
+
color: "#047857",
|
|
144
|
+
}, children: [index + 1, ". ", q.question] }), _jsx("textarea", { value: answers[q.id] || "", onChange: (e) => setAnswers({ ...answers, [q.id]: e.target.value }), placeholder: "Type your answer here...", rows: 3, style: {
|
|
145
|
+
width: "100%",
|
|
146
|
+
padding: 10,
|
|
147
|
+
borderRadius: 6,
|
|
148
|
+
border: "1px solid #a7f3d0",
|
|
149
|
+
fontSize: 14,
|
|
150
|
+
resize: "vertical",
|
|
151
|
+
boxSizing: "border-box",
|
|
152
|
+
fontFamily: "inherit",
|
|
153
|
+
} })] }, q.id))), _jsx("button", { onClick: handleSubmitAnswers, disabled: isSubmitting || Object.keys(answers).length === 0, style: {
|
|
154
|
+
padding: 12,
|
|
155
|
+
backgroundColor: isSubmitting || Object.keys(answers).length === 0
|
|
156
|
+
? "#a7f3d0"
|
|
157
|
+
: "#10b981",
|
|
158
|
+
color: "white",
|
|
159
|
+
border: "none",
|
|
160
|
+
borderRadius: 6,
|
|
161
|
+
cursor: isSubmitting || Object.keys(answers).length === 0
|
|
162
|
+
? "not-allowed"
|
|
163
|
+
: "pointer",
|
|
164
|
+
fontSize: 16,
|
|
165
|
+
fontWeight: 500,
|
|
166
|
+
}, children: isSubmitting ? "Submitting..." : "Submit Answers" })] })] }))] })), questions.length === 0 && (_jsx("div", { style: {
|
|
167
|
+
padding: 16,
|
|
168
|
+
backgroundColor: "#f9fafb",
|
|
169
|
+
borderRadius: 8,
|
|
170
|
+
textAlign: "center",
|
|
171
|
+
}, children: _jsx("p", { style: { margin: 0, color: "#6b7280", fontSize: 14 }, children: "No questions or feedback from the agent yet." }) }))] }));
|
|
172
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskDialog.d.ts","sourceRoot":"","sources":["../src/TaskDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAGxC,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,MAAM,GACP,EAAE,eAAe,GAAG,KAAK,CAAC,YAAY,CAiJtC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { useAutoshipContext } from "./AutoshipProvider";
|
|
4
|
+
export function TaskDialog({ onClose, onBack, }) {
|
|
5
|
+
const { supabase, userId } = useAutoshipContext();
|
|
6
|
+
const [title, setTitle] = useState("");
|
|
7
|
+
const [description, setDescription] = useState("");
|
|
8
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
9
|
+
const [submitted, setSubmitted] = useState(false);
|
|
10
|
+
const handleSubmit = async (e) => {
|
|
11
|
+
e.preventDefault();
|
|
12
|
+
if (!title.trim() || !description.trim())
|
|
13
|
+
return;
|
|
14
|
+
setIsSubmitting(true);
|
|
15
|
+
try {
|
|
16
|
+
const id = `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
17
|
+
const { error } = await supabase.from("agent_tasks").insert({
|
|
18
|
+
id,
|
|
19
|
+
title: title.trim(),
|
|
20
|
+
description: description.trim(),
|
|
21
|
+
priority: 0,
|
|
22
|
+
status: "pending",
|
|
23
|
+
submitted_by: userId || null,
|
|
24
|
+
});
|
|
25
|
+
if (error)
|
|
26
|
+
throw error;
|
|
27
|
+
setSubmitted(true);
|
|
28
|
+
setTimeout(() => onClose(), 2000);
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
console.error("Failed to submit task:", err);
|
|
32
|
+
alert("Failed to submit. Please try again.");
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
setIsSubmitting(false);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
if (submitted) {
|
|
39
|
+
return (_jsxs("div", { style: { textAlign: "center", padding: 20 }, children: [_jsx("div", { style: { fontSize: 48, marginBottom: 16 }, children: "\u2713" }), _jsx("h3", { style: { margin: 0, marginBottom: 8 }, children: "Request Submitted!" }), _jsx("p", { style: { margin: 0, color: "#666" }, children: "We'll get to work on this soon." })] }));
|
|
40
|
+
}
|
|
41
|
+
return (_jsxs("form", { onSubmit: handleSubmit, children: [_jsxs("div", { style: { display: "flex", alignItems: "center", marginBottom: 16 }, children: [_jsx("button", { type: "button", onClick: onBack, style: {
|
|
42
|
+
marginRight: 12,
|
|
43
|
+
background: "none",
|
|
44
|
+
border: "none",
|
|
45
|
+
fontSize: 20,
|
|
46
|
+
cursor: "pointer",
|
|
47
|
+
padding: 4,
|
|
48
|
+
}, children: "\u2190" }), _jsx("h2", { style: { margin: 0 }, children: "New Request" })] }), _jsxs("div", { style: { marginBottom: 16 }, children: [_jsx("label", { style: {
|
|
49
|
+
display: "block",
|
|
50
|
+
marginBottom: 4,
|
|
51
|
+
fontWeight: 500,
|
|
52
|
+
fontSize: 14,
|
|
53
|
+
}, children: "Title" }), _jsx("input", { type: "text", value: title, onChange: (e) => setTitle(e.target.value), placeholder: "e.g., Add dark mode", style: {
|
|
54
|
+
width: "100%",
|
|
55
|
+
padding: 10,
|
|
56
|
+
borderRadius: 6,
|
|
57
|
+
border: "1px solid #ddd",
|
|
58
|
+
fontSize: 16,
|
|
59
|
+
boxSizing: "border-box",
|
|
60
|
+
}, required: true })] }), _jsxs("div", { style: { marginBottom: 16 }, children: [_jsx("label", { style: {
|
|
61
|
+
display: "block",
|
|
62
|
+
marginBottom: 4,
|
|
63
|
+
fontWeight: 500,
|
|
64
|
+
fontSize: 14,
|
|
65
|
+
}, children: "Description" }), _jsx("textarea", { value: description, onChange: (e) => setDescription(e.target.value), placeholder: "Describe what you'd like in detail...", rows: 5, style: {
|
|
66
|
+
width: "100%",
|
|
67
|
+
padding: 10,
|
|
68
|
+
borderRadius: 6,
|
|
69
|
+
border: "1px solid #ddd",
|
|
70
|
+
fontSize: 16,
|
|
71
|
+
resize: "vertical",
|
|
72
|
+
boxSizing: "border-box",
|
|
73
|
+
fontFamily: "inherit",
|
|
74
|
+
}, required: true })] }), _jsx("button", { type: "submit", disabled: isSubmitting, style: {
|
|
75
|
+
width: "100%",
|
|
76
|
+
padding: 12,
|
|
77
|
+
backgroundColor: isSubmitting ? "#a7f3d0" : "#10b981",
|
|
78
|
+
color: "white",
|
|
79
|
+
border: "none",
|
|
80
|
+
borderRadius: 6,
|
|
81
|
+
cursor: isSubmitting ? "not-allowed" : "pointer",
|
|
82
|
+
fontSize: 16,
|
|
83
|
+
fontWeight: 500,
|
|
84
|
+
}, children: isSubmitting ? "Submitting..." : "Submit Request" })] }));
|
|
85
|
+
}
|