@osdk/create-app 0.17.0-beta.0 → 0.17.0-beta.2
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/CHANGELOG.md +13 -0
- package/build/browser/index.js +8 -2
- package/build/browser/index.js.map +1 -1
- package/build/cjs/index.cjs +8 -2
- package/build/cjs/index.cjs.map +1 -1
- package/build/esm/index.js +8 -2
- package/build/esm/index.js.map +1 -1
- package/build/esm/templates.d.ts.map +1 -1
- package/package.json +2 -2
- package/templates/template-tutorial-todo-aip-app/.eslintrc.cjs +18 -0
- package/templates/template-tutorial-todo-aip-app/README.md.hbs +34 -0
- package/templates/template-tutorial-todo-aip-app/index.html +15 -0
- package/templates/template-tutorial-todo-aip-app/package.json.hbs +31 -0
- package/templates/template-tutorial-todo-aip-app/public/aip-icon.svg +3 -0
- package/templates/template-tutorial-todo-aip-app/public/todo-aip-app.svg +19 -0
- package/templates/template-tutorial-todo-aip-app/src/AuthCallback.tsx +24 -0
- package/templates/template-tutorial-todo-aip-app/src/AuthenticatedRoute.tsx +33 -0
- package/templates/template-tutorial-todo-aip-app/src/CreateProjectButton.module.css +3 -0
- package/templates/template-tutorial-todo-aip-app/src/CreateProjectButton.tsx +36 -0
- package/templates/template-tutorial-todo-aip-app/src/CreateProjectDialog.module.css +15 -0
- package/templates/template-tutorial-todo-aip-app/src/CreateProjectDialog.tsx +74 -0
- package/templates/template-tutorial-todo-aip-app/src/CreateTaskButton.module.css +3 -0
- package/templates/template-tutorial-todo-aip-app/src/CreateTaskButton.tsx +38 -0
- package/templates/template-tutorial-todo-aip-app/src/CreateTaskDialog.module.css +66 -0
- package/templates/template-tutorial-todo-aip-app/src/CreateTaskDialog.tsx +140 -0
- package/templates/template-tutorial-todo-aip-app/src/DeleteProjectButton.module.css +3 -0
- package/templates/template-tutorial-todo-aip-app/src/DeleteProjectButton.tsx +37 -0
- package/templates/template-tutorial-todo-aip-app/src/DeleteProjectDialog.module.css +3 -0
- package/templates/template-tutorial-todo-aip-app/src/DeleteProjectDialog.tsx +57 -0
- package/templates/template-tutorial-todo-aip-app/src/Dialog.module.css +11 -0
- package/templates/template-tutorial-todo-aip-app/src/Dialog.tsx +19 -0
- package/templates/template-tutorial-todo-aip-app/src/Home.module.css +80 -0
- package/templates/template-tutorial-todo-aip-app/src/Home.tsx +135 -0
- package/templates/template-tutorial-todo-aip-app/src/Layout.module.css +16 -0
- package/templates/template-tutorial-todo-aip-app/src/Layout.tsx +23 -0
- package/templates/template-tutorial-todo-aip-app/src/Login.module.css +5 -0
- package/templates/template-tutorial-todo-aip-app/src/Login.tsx +44 -0
- package/templates/template-tutorial-todo-aip-app/src/ProjectSelect.tsx +40 -0
- package/templates/template-tutorial-todo-aip-app/src/TaskList.module.css +7 -0
- package/templates/template-tutorial-todo-aip-app/src/TaskList.tsx +44 -0
- package/templates/template-tutorial-todo-aip-app/src/TaskListItem.module.css +35 -0
- package/templates/template-tutorial-todo-aip-app/src/TaskListItem.tsx +58 -0
- package/templates/template-tutorial-todo-aip-app/src/client.ts.hbs +31 -0
- package/templates/template-tutorial-todo-aip-app/src/index.css +75 -0
- package/templates/template-tutorial-todo-aip-app/src/main.tsx +36 -0
- package/templates/template-tutorial-todo-aip-app/src/mocks.ts +187 -0
- package/templates/template-tutorial-todo-aip-app/src/useProjectTasks.ts +75 -0
- package/templates/template-tutorial-todo-aip-app/src/useProjects.ts +58 -0
- package/templates/template-tutorial-todo-aip-app/src/vite-env.d.ts +1 -0
- package/templates/template-tutorial-todo-aip-app/tsconfig.json +25 -0
- package/templates/template-tutorial-todo-aip-app/tsconfig.node.json +10 -0
- package/templates/template-tutorial-todo-aip-app/vite.config.ts.hbs +19 -0
- package/templates/template-tutorial-todo-app/README.md.hbs +1 -1
- package/templates/template-tutorial-todo-app/src/CreateProjectDialog.tsx +3 -3
- package/templates/template-tutorial-todo-app/src/CreateTaskDialog.tsx +3 -3
- package/templates/template-tutorial-todo-app/src/DeleteProjectDialog.tsx +3 -3
- package/templates/template-tutorial-todo-app/src/Dialog.module.css +5 -0
- package/templates/template-tutorial-todo-app/src/Dialog.tsx +1 -1
- package/templates/template-tutorial-todo-app/src/Home.module.css +1 -2
- package/templates/template-tutorial-todo-app/src/Home.tsx +4 -3
- package/templates/template-tutorial-todo-app/src/TaskListItem.module.css +10 -0
- package/templates/template-tutorial-todo-app/src/TaskListItem.tsx +11 -8
- package/templates/template-tutorial-todo-app/src/index.css +0 -5
- package/templates/template-tutorial-todo-app/src/mocks.ts +3 -3
- package/templates/template-tutorial-todo-app/src/useProjectTasks.ts +5 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { MockProject } from "./mocks";
|
|
2
|
+
import css from "./TaskList.module.css";
|
|
3
|
+
import TaskListItem from "./TaskListItem";
|
|
4
|
+
import { useProjectTasks } from "./useProjectTasks";
|
|
5
|
+
|
|
6
|
+
interface TaskListProps {
|
|
7
|
+
project: MockProject;
|
|
8
|
+
onTaskDeleted: (taskId: string | undefined) => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function TaskList({ project, onTaskDeleted }: TaskListProps) {
|
|
12
|
+
const {
|
|
13
|
+
tasks,
|
|
14
|
+
isLoading: isLoadingTasks,
|
|
15
|
+
isError: isErrorTasks,
|
|
16
|
+
deleteTask,
|
|
17
|
+
} = useProjectTasks(project);
|
|
18
|
+
|
|
19
|
+
if (isErrorTasks) {
|
|
20
|
+
return <div className={css.taskList}>Error loading tasks!</div>;
|
|
21
|
+
} else if (isLoadingTasks) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const data = tasks ?? [];
|
|
26
|
+
if (data.length === 0) {
|
|
27
|
+
return <div className={css.taskList}>No tasks found</div>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<ul className={css.taskList}>
|
|
32
|
+
{data.map((task) => (
|
|
33
|
+
<TaskListItem
|
|
34
|
+
key={task.id}
|
|
35
|
+
task={task}
|
|
36
|
+
deleteTask={deleteTask}
|
|
37
|
+
onTaskDeleted={onTaskDeleted}
|
|
38
|
+
/>
|
|
39
|
+
))}
|
|
40
|
+
</ul>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export default TaskList;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
.li {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: row;
|
|
4
|
+
align-items: flex-start;
|
|
5
|
+
padding: 5px;
|
|
6
|
+
border: 1px solid #ccc;
|
|
7
|
+
gap: 5px;
|
|
8
|
+
border-radius: 5px;
|
|
9
|
+
background-color: #f9f9f9;
|
|
10
|
+
margin: 10px 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.checked {
|
|
14
|
+
text-decoration: line-through;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.delete {
|
|
18
|
+
border: 1px solid #ccc;
|
|
19
|
+
padding: 2px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.task {
|
|
23
|
+
display: flex;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
width: 100%;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.textArea {
|
|
29
|
+
border: none;
|
|
30
|
+
background-color: #f9f9f9;
|
|
31
|
+
color: gray;
|
|
32
|
+
resize: none;
|
|
33
|
+
overflow: hidden;
|
|
34
|
+
pointer-events: none;
|
|
35
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import type { MockTask } from "./mocks";
|
|
3
|
+
import css from "./TaskListItem.module.css";
|
|
4
|
+
|
|
5
|
+
interface TaskListItemProps {
|
|
6
|
+
task: MockTask;
|
|
7
|
+
deleteTask: (task: MockTask) => Promise<void>;
|
|
8
|
+
onTaskDeleted: (taskId: string | undefined) => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function TaskListItem({ task, deleteTask, onTaskDeleted }: TaskListItemProps) {
|
|
12
|
+
const [isDeleting, setIsDeleting] = useState(false);
|
|
13
|
+
const textAreaRef = useRef<HTMLTextAreaElement>(null);
|
|
14
|
+
|
|
15
|
+
const handleClick = useCallback(async () => {
|
|
16
|
+
setIsDeleting(true);
|
|
17
|
+
try {
|
|
18
|
+
await deleteTask(task);
|
|
19
|
+
} finally {
|
|
20
|
+
onTaskDeleted(task.id);
|
|
21
|
+
setIsDeleting(false);
|
|
22
|
+
}
|
|
23
|
+
}, [deleteTask, task, onTaskDeleted]);
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (textAreaRef.current) {
|
|
27
|
+
const textArea = textAreaRef.current;
|
|
28
|
+
textArea.style.height = "auto";
|
|
29
|
+
textArea.style.height = `${textArea.scrollHeight}px`;
|
|
30
|
+
}
|
|
31
|
+
}, [task.description]);
|
|
32
|
+
const cleanDescription = task.description?.trim();
|
|
33
|
+
return (
|
|
34
|
+
<li className={css.li}>
|
|
35
|
+
<input
|
|
36
|
+
type="checkbox"
|
|
37
|
+
onChange={handleClick}
|
|
38
|
+
checked={isDeleting}
|
|
39
|
+
className={css.delete}
|
|
40
|
+
title="Delete task"
|
|
41
|
+
/>
|
|
42
|
+
<div className={`${css.task} ${isDeleting ? css.checked : ""}`}>
|
|
43
|
+
<span>{task.title}</span>
|
|
44
|
+
{cleanDescription != null && (
|
|
45
|
+
<textarea
|
|
46
|
+
ref={textAreaRef}
|
|
47
|
+
readOnly
|
|
48
|
+
value={task.description}
|
|
49
|
+
className={css.textArea}
|
|
50
|
+
rows={1}
|
|
51
|
+
/>
|
|
52
|
+
)}
|
|
53
|
+
</div>
|
|
54
|
+
</li>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export default TaskListItem;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { FoundryClient, PublicClientAuth } from "{{osdkPackage}}";
|
|
2
|
+
|
|
3
|
+
const url = import.meta.env.VITE_FOUNDRY_API_URL;
|
|
4
|
+
const clientId = import.meta.env.VITE_FOUNDRY_CLIENT_ID;
|
|
5
|
+
const redirectUrl = import.meta.env.VITE_FOUNDRY_REDIRECT_URL;
|
|
6
|
+
checkEnv(url, "VITE_FOUNDRY_API_URL");
|
|
7
|
+
checkEnv(clientId, "VITE_FOUNDRY_CLIENT_ID");
|
|
8
|
+
checkEnv(redirectUrl, "VITE_FOUNDRY_REDIRECT_URL");
|
|
9
|
+
|
|
10
|
+
function checkEnv(
|
|
11
|
+
value: string | undefined,
|
|
12
|
+
name: string,
|
|
13
|
+
): asserts value is string {
|
|
14
|
+
if (value == null) {
|
|
15
|
+
throw new Error(`Missing environment variable: ${name}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Initialize the client to interact with the Ontology SDK
|
|
21
|
+
*/
|
|
22
|
+
const client = new FoundryClient({
|
|
23
|
+
url,
|
|
24
|
+
auth: new PublicClientAuth({
|
|
25
|
+
clientId,
|
|
26
|
+
url,
|
|
27
|
+
redirectUrl,
|
|
28
|
+
}),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export default client;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
|
3
|
+
line-height: 1.5;
|
|
4
|
+
font-weight: 400;
|
|
5
|
+
|
|
6
|
+
font-synthesis: none;
|
|
7
|
+
text-rendering: optimizeLegibility;
|
|
8
|
+
-webkit-font-smoothing: antialiased;
|
|
9
|
+
-moz-osx-font-smoothing: grayscale;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
#root-container {
|
|
13
|
+
display: flex;
|
|
14
|
+
flex: 1;
|
|
15
|
+
align-items: center;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
#root {
|
|
19
|
+
max-width: 1280px;
|
|
20
|
+
margin: 2rem auto;
|
|
21
|
+
padding: 2rem;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
a {
|
|
25
|
+
font-weight: 500;
|
|
26
|
+
color: #646cff;
|
|
27
|
+
text-decoration: inherit;
|
|
28
|
+
}
|
|
29
|
+
a:hover {
|
|
30
|
+
color: #535bf2;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
body {
|
|
34
|
+
margin: 0;
|
|
35
|
+
display: flex;
|
|
36
|
+
flex-direction: column;
|
|
37
|
+
min-width: 320px;
|
|
38
|
+
min-height: 100vh;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
h1 {
|
|
42
|
+
font-size: 3.2em;
|
|
43
|
+
line-height: 1.1;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
button {
|
|
47
|
+
border-radius: 8px;
|
|
48
|
+
border: 1px solid transparent;
|
|
49
|
+
padding: 0.6em 1.2em;
|
|
50
|
+
font-size: 1em;
|
|
51
|
+
font-weight: 500;
|
|
52
|
+
font-family: inherit;
|
|
53
|
+
cursor: pointer;
|
|
54
|
+
transition: border-color 0.25s;
|
|
55
|
+
}
|
|
56
|
+
button:hover {
|
|
57
|
+
border-color: #646cff;
|
|
58
|
+
}
|
|
59
|
+
button:focus,
|
|
60
|
+
button:focus-visible {
|
|
61
|
+
outline: 4px auto -webkit-focus-ring-color;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@media (prefers-color-scheme: light) {
|
|
65
|
+
:root {
|
|
66
|
+
color: #213547;
|
|
67
|
+
background-color: #ffffff;
|
|
68
|
+
}
|
|
69
|
+
a:hover {
|
|
70
|
+
color: #747bff;
|
|
71
|
+
}
|
|
72
|
+
button {
|
|
73
|
+
background-color: #f9f9f9;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import ReactDOM from "react-dom/client";
|
|
2
|
+
import { createBrowserRouter, RouterProvider } from "react-router-dom";
|
|
3
|
+
import AuthCallback from "./AuthCallback";
|
|
4
|
+
import AuthenticatedRoute from "./AuthenticatedRoute";
|
|
5
|
+
import Home from "./Home";
|
|
6
|
+
import Login from "./Login";
|
|
7
|
+
import "./index.css";
|
|
8
|
+
|
|
9
|
+
const router = createBrowserRouter(
|
|
10
|
+
[
|
|
11
|
+
{
|
|
12
|
+
path: "/",
|
|
13
|
+
element: <AuthenticatedRoute />,
|
|
14
|
+
children: [
|
|
15
|
+
{
|
|
16
|
+
path: "/",
|
|
17
|
+
element: <Home />,
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
path: "/login",
|
|
23
|
+
element: <Login />,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
// This is the route defined in your application's redirect URL
|
|
27
|
+
path: "/auth/callback",
|
|
28
|
+
element: <AuthCallback />,
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
{ basename: import.meta.env.BASE_URL },
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
35
|
+
<RouterProvider router={router} />,
|
|
36
|
+
);
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
export interface MockProject {
|
|
2
|
+
$apiName: string;
|
|
3
|
+
$primaryKey: string;
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
description: string;
|
|
7
|
+
tasks: MockTask[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface MockTask {
|
|
11
|
+
$apiName: string;
|
|
12
|
+
$primaryKey: string;
|
|
13
|
+
id: string;
|
|
14
|
+
title: string;
|
|
15
|
+
description: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const projects: MockProject[] = [
|
|
19
|
+
{
|
|
20
|
+
$apiName: "MockProject",
|
|
21
|
+
$primaryKey: "1",
|
|
22
|
+
id: "1",
|
|
23
|
+
name: "Mock project",
|
|
24
|
+
description: "This is a mock description",
|
|
25
|
+
tasks: [
|
|
26
|
+
{
|
|
27
|
+
$apiName: "MockTask",
|
|
28
|
+
$primaryKey: "1",
|
|
29
|
+
id: "1",
|
|
30
|
+
title: "Try to",
|
|
31
|
+
description: "task description 1",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
$apiName: "MockTask",
|
|
35
|
+
$primaryKey: "2",
|
|
36
|
+
id: "2",
|
|
37
|
+
title: "Implement this",
|
|
38
|
+
description: "task description 2",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
$apiName: "MockTask",
|
|
42
|
+
$primaryKey: "3",
|
|
43
|
+
id: "3",
|
|
44
|
+
title: "With the Ontology SDK!",
|
|
45
|
+
description: "task description 3",
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
$apiName: "MockProject",
|
|
51
|
+
$primaryKey: "2",
|
|
52
|
+
id: "2",
|
|
53
|
+
name: "Yet another mock project",
|
|
54
|
+
description: "This is another mock description",
|
|
55
|
+
tasks: [
|
|
56
|
+
{
|
|
57
|
+
$apiName: "MockTask",
|
|
58
|
+
$primaryKey: "4",
|
|
59
|
+
id: "4",
|
|
60
|
+
title: "More tasks here",
|
|
61
|
+
description: "More task description",
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
async function delay(): Promise<void> {
|
|
68
|
+
return new Promise((resolve) =>
|
|
69
|
+
setTimeout(() => resolve(), 500 + Math.random() * 1000)
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Good enough random id for mocks
|
|
74
|
+
function randomId(): string {
|
|
75
|
+
return `${Math.floor(Math.random() * 2 ** 31)}`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function getProjects(): Promise<MockProject[]> {
|
|
79
|
+
await delay();
|
|
80
|
+
const result = [...projects];
|
|
81
|
+
result.sort((p1, p2) => p1.name.localeCompare(p2.name));
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function createProject({
|
|
86
|
+
name,
|
|
87
|
+
description = "",
|
|
88
|
+
}: {
|
|
89
|
+
name: string;
|
|
90
|
+
description?: string;
|
|
91
|
+
}): Promise<MockProject["$primaryKey"]> {
|
|
92
|
+
await delay();
|
|
93
|
+
const id = randomId();
|
|
94
|
+
projects.push({
|
|
95
|
+
$apiName: "MockProject",
|
|
96
|
+
$primaryKey: id,
|
|
97
|
+
id,
|
|
98
|
+
name,
|
|
99
|
+
description,
|
|
100
|
+
tasks: [],
|
|
101
|
+
});
|
|
102
|
+
return id;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function getRecommendedProjectDescription(
|
|
106
|
+
project: MockProject,
|
|
107
|
+
): Promise<string> {
|
|
108
|
+
await delay();
|
|
109
|
+
if (project.tasks.length === 0) {
|
|
110
|
+
throw new Error("Project description recommendation requires tasks");
|
|
111
|
+
}
|
|
112
|
+
return `AIP Logic mock description for project`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function updateProjectDescription(
|
|
116
|
+
project: MockProject,
|
|
117
|
+
description: string,
|
|
118
|
+
): Promise<void> {
|
|
119
|
+
await delay();
|
|
120
|
+
project.description = description;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function deleteProject(id: string): Promise<void> {
|
|
124
|
+
await delay();
|
|
125
|
+
const idx = projects.findIndex((p) => p.id === id);
|
|
126
|
+
if (idx !== -1) {
|
|
127
|
+
projects.splice(idx, 1);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function createTask({
|
|
132
|
+
title,
|
|
133
|
+
description = "",
|
|
134
|
+
projectId,
|
|
135
|
+
}: {
|
|
136
|
+
title: string;
|
|
137
|
+
description: string;
|
|
138
|
+
projectId: string;
|
|
139
|
+
}): Promise<MockTask["$primaryKey"]> {
|
|
140
|
+
await delay();
|
|
141
|
+
const project = projects.find((p) => p.id === projectId);
|
|
142
|
+
if (project == null) {
|
|
143
|
+
throw new Error(`Project ${projectId} not found!`);
|
|
144
|
+
}
|
|
145
|
+
const id = randomId();
|
|
146
|
+
project.tasks.unshift({
|
|
147
|
+
$apiName: "MockTask",
|
|
148
|
+
$primaryKey: id,
|
|
149
|
+
id,
|
|
150
|
+
title,
|
|
151
|
+
description,
|
|
152
|
+
});
|
|
153
|
+
return id;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function getRecommendedTaskDescription(
|
|
157
|
+
taskName: string,
|
|
158
|
+
): Promise<string> {
|
|
159
|
+
await delay();
|
|
160
|
+
if (taskName.length === 0) {
|
|
161
|
+
throw new Error("Task name must not be empty");
|
|
162
|
+
}
|
|
163
|
+
return `Mock AIP description for task`;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function deleteTask(id: string): Promise<void> {
|
|
167
|
+
await delay();
|
|
168
|
+
for (const project of projects) {
|
|
169
|
+
const idx = project.tasks.findIndex((t) => t.id === id);
|
|
170
|
+
if (idx !== -1) {
|
|
171
|
+
project.tasks.splice(idx, 1);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const Mocks = {
|
|
177
|
+
getProjects,
|
|
178
|
+
createProject,
|
|
179
|
+
getRecommendedProjectDescription,
|
|
180
|
+
deleteProject,
|
|
181
|
+
createTask,
|
|
182
|
+
deleteTask,
|
|
183
|
+
getRecommendedTaskDescription,
|
|
184
|
+
updateProjectDescription,
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
export default Mocks;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import useSWR from "swr";
|
|
3
|
+
import Mocks, { MockProject, MockTask } from "./mocks";
|
|
4
|
+
|
|
5
|
+
export function useProjectTasks(project: MockProject | undefined) {
|
|
6
|
+
const { data, isLoading, isValidating, error, mutate } = useSWR<MockTask[]>(
|
|
7
|
+
project != null ? `projects/${project.id}/tasks` : null,
|
|
8
|
+
// Try to implement this with the Ontology SDK!
|
|
9
|
+
async () => {
|
|
10
|
+
if (project == null) {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
return project.tasks;
|
|
14
|
+
},
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
const createTask: (
|
|
18
|
+
title: string,
|
|
19
|
+
description: string,
|
|
20
|
+
) => Promise<MockTask["$primaryKey"] | undefined> = useCallback(
|
|
21
|
+
async (title: string, description: string) => {
|
|
22
|
+
if (project == null) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
// Try to implement this with the Ontology SDK!
|
|
26
|
+
const id = await Mocks.createTask({
|
|
27
|
+
title,
|
|
28
|
+
description,
|
|
29
|
+
projectId: project.$primaryKey,
|
|
30
|
+
});
|
|
31
|
+
await mutate();
|
|
32
|
+
return id;
|
|
33
|
+
},
|
|
34
|
+
[project, mutate],
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const deleteTask: (task: MockTask) => Promise<void> = useCallback(
|
|
38
|
+
async (task) => {
|
|
39
|
+
if (project == null) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
await sleep(1000);
|
|
43
|
+
// Try to implement this with the Ontology SDK!
|
|
44
|
+
await Mocks.deleteTask(task.$primaryKey);
|
|
45
|
+
await mutate();
|
|
46
|
+
},
|
|
47
|
+
[project, mutate],
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const getRecommendedTaskDescription: (taskName: string) => Promise<string> =
|
|
51
|
+
useCallback(
|
|
52
|
+
async (taskName: string) => {
|
|
53
|
+
// Try to implement this with the Ontology SDK!
|
|
54
|
+
const recommendedTaskDescription = await Mocks
|
|
55
|
+
.getRecommendedTaskDescription(taskName);
|
|
56
|
+
await mutate();
|
|
57
|
+
return recommendedTaskDescription;
|
|
58
|
+
},
|
|
59
|
+
[mutate],
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
tasks: data,
|
|
64
|
+
isLoading,
|
|
65
|
+
isValidating,
|
|
66
|
+
isError: error,
|
|
67
|
+
createTask,
|
|
68
|
+
deleteTask,
|
|
69
|
+
getRecommendedTaskDescription,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function sleep(ms: number) {
|
|
74
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
75
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import useSWR from "swr";
|
|
3
|
+
import type { MockProject } from "./mocks";
|
|
4
|
+
import Mocks from "./mocks";
|
|
5
|
+
|
|
6
|
+
function useProjects() {
|
|
7
|
+
const { data, isLoading, isValidating, error, mutate } = useSWR<
|
|
8
|
+
MockProject[]
|
|
9
|
+
>("projects", async () => {
|
|
10
|
+
// Try to implement this with the Ontology SDK!
|
|
11
|
+
return Mocks.getProjects();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const createProject: (
|
|
15
|
+
name: string,
|
|
16
|
+
) => Promise<MockProject["$primaryKey"]> = useCallback(
|
|
17
|
+
async (name: string) => {
|
|
18
|
+
// Try to implement this with the Ontology SDK!
|
|
19
|
+
const id = await Mocks.createProject({ name });
|
|
20
|
+
await mutate();
|
|
21
|
+
return id;
|
|
22
|
+
},
|
|
23
|
+
[mutate],
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const updateProjectDescription: (
|
|
27
|
+
project: MockProject,
|
|
28
|
+
) => Promise<void> = useCallback(
|
|
29
|
+
async (project) => {
|
|
30
|
+
// Try to implement this with the Ontology SDK!
|
|
31
|
+
const description = await Mocks.getRecommendedProjectDescription(project);
|
|
32
|
+
await Mocks.updateProjectDescription(project, description);
|
|
33
|
+
await mutate();
|
|
34
|
+
},
|
|
35
|
+
[mutate],
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const deleteProject: (project: MockProject) => Promise<void> = useCallback(
|
|
39
|
+
async (project) => {
|
|
40
|
+
// Try to implement this with the Ontology SDK!
|
|
41
|
+
await Mocks.deleteProject(project.$primaryKey);
|
|
42
|
+
await mutate();
|
|
43
|
+
},
|
|
44
|
+
[mutate],
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
projects: data,
|
|
49
|
+
isLoading,
|
|
50
|
+
isValidating,
|
|
51
|
+
isError: error,
|
|
52
|
+
createProject,
|
|
53
|
+
deleteProject,
|
|
54
|
+
updateProjectDescription,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export default useProjects;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
|
|
9
|
+
/* Bundler mode */
|
|
10
|
+
"moduleResolution": "bundler",
|
|
11
|
+
"allowImportingTsExtensions": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"noEmit": true,
|
|
15
|
+
"jsx": "react-jsx",
|
|
16
|
+
|
|
17
|
+
/* Linting */
|
|
18
|
+
"strict": true,
|
|
19
|
+
"noUnusedLocals": true,
|
|
20
|
+
"noUnusedParameters": true,
|
|
21
|
+
"noFallthroughCasesInSwitch": true
|
|
22
|
+
},
|
|
23
|
+
"include": ["src"],
|
|
24
|
+
"references": [{ "path": "./tsconfig.node.json" }]
|
|
25
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import react from "@vitejs/plugin-react";
|
|
2
|
+
import { defineConfig } from "vite";
|
|
3
|
+
|
|
4
|
+
// https://vitejs.dev/config/
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
plugins: [react()],
|
|
7
|
+
server: {
|
|
8
|
+
port: 8080,
|
|
9
|
+
{{#if corsProxy}}
|
|
10
|
+
proxy: {
|
|
11
|
+
"^(/multipass/api|/api)": {
|
|
12
|
+
target: "{{foundryUrl}}",
|
|
13
|
+
changeOrigin: true,
|
|
14
|
+
secure: true,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
{{/if}}
|
|
18
|
+
},
|
|
19
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# {{project}}
|
|
2
2
|
|
|
3
|
-
This project was generated with `@osdk/create-app` and is intended to be used alongside the Developer Console tutorial for creating a
|
|
3
|
+
This project was generated with `@osdk/create-app` and is intended to be used alongside the Developer Console tutorial for creating a To Do App against a reference Ontology.
|
|
4
4
|
|
|
5
5
|
## Developing
|
|
6
6
|
|
|
@@ -39,12 +39,12 @@ function CreateProjectDialog({
|
|
|
39
39
|
<Dialog
|
|
40
40
|
isOpen={isOpen}
|
|
41
41
|
buttons={[
|
|
42
|
-
<button disabled={isCreating} onClick={handleSubmit} key="create">
|
|
43
|
-
Create project
|
|
44
|
-
</button>,
|
|
45
42
|
<button disabled={isCreating} onClick={onClose} key="cancel">
|
|
46
43
|
Cancel
|
|
47
44
|
</button>,
|
|
45
|
+
<button disabled={isCreating} onClick={handleSubmit} key="create">
|
|
46
|
+
Create project
|
|
47
|
+
</button>,
|
|
48
48
|
]}
|
|
49
49
|
>
|
|
50
50
|
<label>
|