- {bar}
- {tab}
+
+
- >
+
+ {tab}
+
}>
-
- {children}
-
+ {bar}>}>
+
+ <>{children}>
+
+
}
diff --git a/src/app/(dashboard)/namespace/[namespaceId]/members/add/page.tsx b/src/app/(dashboard)/namespace/[namespaceId]/members/add/page.tsx
index fafad442..a5186c72 100644
--- a/src/app/(dashboard)/namespace/[namespaceId]/members/add/page.tsx
+++ b/src/app/(dashboard)/namespace/[namespaceId]/members/add/page.tsx
@@ -1,6 +1,6 @@
"use client"
-import {MemberAddPage} from "@edition/dashboard/member/MemberAddPage";
+import {MemberAddPage} from "@edition/member/pages/MemberAddPage";
export default function Page() {
return
diff --git a/src/app/(dashboard)/namespace/[namespaceId]/members/page.tsx b/src/app/(dashboard)/namespace/[namespaceId]/members/page.tsx
index 7e92b720..3e37f24d 100644
--- a/src/app/(dashboard)/namespace/[namespaceId]/members/page.tsx
+++ b/src/app/(dashboard)/namespace/[namespaceId]/members/page.tsx
@@ -1,6 +1,6 @@
"use client"
-import {MembersPage} from "@edition/dashboard/member/MembersPage";
+import {MembersPage} from "@edition/member/pages/MembersPage";
export default function Page() {
return
diff --git a/src/app/(dashboard)/namespace/[namespaceId]/page.tsx b/src/app/(dashboard)/namespace/[namespaceId]/page.tsx
index a9a68513..dbfc7343 100644
--- a/src/app/(dashboard)/namespace/[namespaceId]/page.tsx
+++ b/src/app/(dashboard)/namespace/[namespaceId]/page.tsx
@@ -1,6 +1,6 @@
"use client"
-import {NamespaceOverviewPage} from "@edition/dashboard/namespace/NamespaceOverviewPage";
+import {NamespaceOverviewPage} from "@edition/namespace/pages/NamespaceOverviewPage";
export default function Page() {
return
diff --git a/src/app/(dashboard)/namespace/[namespaceId]/projects/create/page.tsx b/src/app/(dashboard)/namespace/[namespaceId]/projects/create/page.tsx
index 8211b5a4..9b9fff72 100644
--- a/src/app/(dashboard)/namespace/[namespaceId]/projects/create/page.tsx
+++ b/src/app/(dashboard)/namespace/[namespaceId]/projects/create/page.tsx
@@ -1,4 +1,4 @@
-import {NamespaceProjectsCreatePage} from "@edition/dashboard/namespace/NamespaceProjectsCreatePage";
+import {NamespaceProjectsCreatePage} from "@edition/namespace/pages/NamespaceProjectsCreatePage";
export default function Page() {
return
diff --git a/src/app/(dashboard)/namespace/[namespaceId]/projects/page.tsx b/src/app/(dashboard)/namespace/[namespaceId]/projects/page.tsx
index 98e317e3..b6743871 100644
--- a/src/app/(dashboard)/namespace/[namespaceId]/projects/page.tsx
+++ b/src/app/(dashboard)/namespace/[namespaceId]/projects/page.tsx
@@ -1,4 +1,4 @@
-import {NamespaceProjectsPage} from "@edition/dashboard/namespace/NamespaceProjectsPage";
+import {NamespaceProjectsPage} from "@edition/namespace/pages/NamespaceProjectsPage";
export default function Page() {
return
diff --git a/src/app/(dashboard)/namespace/[namespaceId]/roles/[roleId]/settings/page.tsx b/src/app/(dashboard)/namespace/[namespaceId]/roles/[roleId]/settings/page.tsx
index 4eed9bc3..db745576 100644
--- a/src/app/(dashboard)/namespace/[namespaceId]/roles/[roleId]/settings/page.tsx
+++ b/src/app/(dashboard)/namespace/[namespaceId]/roles/[roleId]/settings/page.tsx
@@ -1,6 +1,6 @@
"use client"
-import {RoleSettingsPage} from "@edition/dashboard/role/RoleSettingsPage";
+import {RoleSettingsPage} from "@edition/role/pages/RoleSettingsPage";
export default function Page() {
return
diff --git a/src/app/(dashboard)/namespace/[namespaceId]/roles/create/page.tsx b/src/app/(dashboard)/namespace/[namespaceId]/roles/create/page.tsx
index a4998c3c..320e44f5 100644
--- a/src/app/(dashboard)/namespace/[namespaceId]/roles/create/page.tsx
+++ b/src/app/(dashboard)/namespace/[namespaceId]/roles/create/page.tsx
@@ -1,6 +1,6 @@
"use client"
-import {RoleCreatePage} from "@edition/dashboard/role/RoleCreatePage";
+import {RoleCreatePage} from "@edition/role/pages/RoleCreatePage";
export default function Page() {
return
diff --git a/src/app/(dashboard)/namespace/[namespaceId]/roles/page.tsx b/src/app/(dashboard)/namespace/[namespaceId]/roles/page.tsx
index 1268e43b..68dd8172 100644
--- a/src/app/(dashboard)/namespace/[namespaceId]/roles/page.tsx
+++ b/src/app/(dashboard)/namespace/[namespaceId]/roles/page.tsx
@@ -1,5 +1,13 @@
-import {RolesPage} from "@edition/dashboard/role/RolesPage";
+import {RolesPage} from "@edition/role/pages/RolesPage";
export default function Page() {
- return
+ return
+
+
}
\ No newline at end of file
diff --git a/src/app/(dashboard)/namespace/[namespaceId]/runtimes/[runtimeId]/settings/page.tsx b/src/app/(dashboard)/namespace/[namespaceId]/runtimes/[runtimeId]/settings/page.tsx
index 94cdc737..1b30dce7 100644
--- a/src/app/(dashboard)/namespace/[namespaceId]/runtimes/[runtimeId]/settings/page.tsx
+++ b/src/app/(dashboard)/namespace/[namespaceId]/runtimes/[runtimeId]/settings/page.tsx
@@ -1,4 +1,4 @@
-import {RuntimeSettingsPage} from "@edition/dashboard/runtime/RuntimeSettingsPage";
+import {RuntimeSettingsPage} from "@edition/runtime/pages/RuntimeSettingsPage";
export default function Page() {
return
diff --git a/src/app/(dashboard)/namespace/[namespaceId]/runtimes/create/page.tsx b/src/app/(dashboard)/namespace/[namespaceId]/runtimes/create/page.tsx
index 9063cb15..e149b9e1 100644
--- a/src/app/(dashboard)/namespace/[namespaceId]/runtimes/create/page.tsx
+++ b/src/app/(dashboard)/namespace/[namespaceId]/runtimes/create/page.tsx
@@ -1,4 +1,4 @@
-import {RuntimeCreatePage} from "@edition/dashboard/runtime/RuntimeCreatePage";
+import {RuntimeCreatePage} from "@edition/runtime/pages/RuntimeCreatePage";
export default function Page() {
return
diff --git a/src/app/(dashboard)/namespace/[namespaceId]/runtimes/page.tsx b/src/app/(dashboard)/namespace/[namespaceId]/runtimes/page.tsx
index f9add133..27fcc879 100644
--- a/src/app/(dashboard)/namespace/[namespaceId]/runtimes/page.tsx
+++ b/src/app/(dashboard)/namespace/[namespaceId]/runtimes/page.tsx
@@ -1,4 +1,4 @@
-import {RuntimesPage} from "@edition/dashboard/runtime/RuntimesPage";
+import {RuntimesPage} from "@edition/runtime/pages/RuntimesPage";
export default function Page() {
return
diff --git a/src/app/(dashboard)/namespace/[namespaceId]/settings/page.tsx b/src/app/(dashboard)/namespace/[namespaceId]/settings/page.tsx
index 201fd719..5421a642 100644
--- a/src/app/(dashboard)/namespace/[namespaceId]/settings/page.tsx
+++ b/src/app/(dashboard)/namespace/[namespaceId]/settings/page.tsx
@@ -1,6 +1,6 @@
"use client"
-import {OrganizationSettingsPage} from "@edition/dashboard/organization/OrganizationSettingsPage";
+import {OrganizationSettingsPage} from "@edition/organization/pages/OrganizationSettingsPage";
export default function Page() {
return
diff --git a/src/app/(dashboard)/organizations/create/page.tsx b/src/app/(dashboard)/organizations/create/page.tsx
index 70fbc55c..86e80fee 100644
--- a/src/app/(dashboard)/organizations/create/page.tsx
+++ b/src/app/(dashboard)/organizations/create/page.tsx
@@ -1,4 +1,4 @@
-import {OrganizationCreatePage} from "@edition/dashboard/organization/OrganizationCreatePage";
+import {OrganizationCreatePage} from "@edition/organization/pages/OrganizationCreatePage";
export default function Page() {
return
diff --git a/src/app/(dashboard)/organizations/page.tsx b/src/app/(dashboard)/organizations/page.tsx
index d0aed06b..e49f829e 100644
--- a/src/app/(dashboard)/organizations/page.tsx
+++ b/src/app/(dashboard)/organizations/page.tsx
@@ -1,4 +1,4 @@
-import {OrganizationsPage} from "@edition/dashboard/organization/OrganizationsPage";
+import {OrganizationsPage} from "@edition/organization/pages/OrganizationsPage";
export default function Page() {
return
diff --git a/src/app/(dashboard)/page.tsx b/src/app/(dashboard)/page.tsx
index 27103554..f3fe6f60 100644
--- a/src/app/(dashboard)/page.tsx
+++ b/src/app/(dashboard)/page.tsx
@@ -1,7 +1,7 @@
"use client"
import React from "react";
-import {ApplicationPage} from "@edition/dashboard/application/ApplicationPage";
+import {ApplicationPage} from "@edition/application/ApplicationPage";
export default function Page() {
return
diff --git a/src/app/(dashboard)/runtimes/[runtimeId]/settings/page.tsx b/src/app/(dashboard)/runtimes/[runtimeId]/settings/page.tsx
index 94cdc737..1b30dce7 100644
--- a/src/app/(dashboard)/runtimes/[runtimeId]/settings/page.tsx
+++ b/src/app/(dashboard)/runtimes/[runtimeId]/settings/page.tsx
@@ -1,4 +1,4 @@
-import {RuntimeSettingsPage} from "@edition/dashboard/runtime/RuntimeSettingsPage";
+import {RuntimeSettingsPage} from "@edition/runtime/pages/RuntimeSettingsPage";
export default function Page() {
return
diff --git a/src/app/(dashboard)/runtimes/create/page.tsx b/src/app/(dashboard)/runtimes/create/page.tsx
index 9063cb15..e149b9e1 100644
--- a/src/app/(dashboard)/runtimes/create/page.tsx
+++ b/src/app/(dashboard)/runtimes/create/page.tsx
@@ -1,4 +1,4 @@
-import {RuntimeCreatePage} from "@edition/dashboard/runtime/RuntimeCreatePage";
+import {RuntimeCreatePage} from "@edition/runtime/pages/RuntimeCreatePage";
export default function Page() {
return
diff --git a/src/app/(dashboard)/runtimes/page.tsx b/src/app/(dashboard)/runtimes/page.tsx
index 48bde090..b0f607f2 100644
--- a/src/app/(dashboard)/runtimes/page.tsx
+++ b/src/app/(dashboard)/runtimes/page.tsx
@@ -1,6 +1,6 @@
"use client"
-import {RuntimesPage} from "@edition/dashboard/runtime/RuntimesPage";
+import {RuntimesPage} from "@edition/runtime/pages/RuntimesPage";
export default function Page() {
return
diff --git a/src/app/(dashboard)/settings/page.tsx b/src/app/(dashboard)/settings/page.tsx
index 6c13d1d1..47813a23 100644
--- a/src/app/(dashboard)/settings/page.tsx
+++ b/src/app/(dashboard)/settings/page.tsx
@@ -1,7 +1,7 @@
"use client"
import React from "react";
-import {ApplicationSettingsPage} from "@edition/dashboard/application/ApplicationSettingsPage";
+import {ApplicationSettingsPage} from "@edition/application/ApplicationSettingsPage";
export default function Page() {
diff --git a/src/app/(dashboard)/users/page.tsx b/src/app/(dashboard)/users/page.tsx
index 14e72354..57051d43 100644
--- a/src/app/(dashboard)/users/page.tsx
+++ b/src/app/(dashboard)/users/page.tsx
@@ -1,4 +1,4 @@
-import {UsersPage} from "@edition/dashboard/user/UsersPage";
+import {UsersPage} from "@edition/user/pages/UsersPage";
export default function Page() {
return
diff --git a/src/app/(flow)/@bar/default.tsx b/src/app/(flow)/@bar/default.tsx
new file mode 100644
index 00000000..99055c6e
--- /dev/null
+++ b/src/app/(flow)/@bar/default.tsx
@@ -0,0 +1,10 @@
+"use client";
+
+import React from "react";
+import {ApplicationBarView} from "@edition/application/ApplicationBarView";
+
+const Page = () => {
+ return
+}
+
+export default Page
\ No newline at end of file
diff --git a/src/app/(flow)/@tab/default.tsx b/src/app/(flow)/@tab/default.tsx
new file mode 100644
index 00000000..ac3dd7f7
--- /dev/null
+++ b/src/app/(flow)/@tab/default.tsx
@@ -0,0 +1,5 @@
+"use client"
+
+export default function Page() {
+ return null
+}
\ No newline at end of file
diff --git a/src/app/(flow)/@tab/namespace/[namespaceId]/project/[projectId]/default.tsx b/src/app/(flow)/@tab/namespace/[namespaceId]/project/[projectId]/default.tsx
new file mode 100644
index 00000000..d2a8b584
--- /dev/null
+++ b/src/app/(flow)/@tab/namespace/[namespaceId]/project/[projectId]/default.tsx
@@ -0,0 +1,61 @@
+"use client"
+
+import {Tab, TabList, TabTrigger} from "@code0-tech/pictor/dist/components/tab/Tab";
+import {Button, Text, Tooltip, TooltipContent, TooltipPortal, TooltipTrigger} from "@code0-tech/pictor";
+import {IconBuilding, IconHome, IconSettings} from "@tabler/icons-react";
+import React from "react";
+import {useParams, usePathname, useRouter} from "next/navigation";
+import {hashToColor} from "@code0-tech/pictor/dist/components/d-flow/DFlow.util";
+
+export default function Page() {
+
+
+ const pathname = usePathname()
+ const router = useRouter()
+ const params = useParams()
+ const defaultValue = pathname.includes("flow") ? "flow"
+ : "settings"
+
+ const namespaceId = params.namespaceId as any
+ const projectId = params.projectId as any
+ const flowId = params.flowId as any
+
+ return
+
+
+
+
+ {
+ if (!flowId) {
+ router.push(`/namespace/${namespaceId}/project/${projectId}/flow`)
+ } else {
+ router.push(`/namespace/${namespaceId}/project/${projectId}/flow/${flowId}`)
+ }
+ }}>
+
+
+
+
+
+
+ Flow builder
+
+
+
+
+
+
+ router.push(`/namespace/${namespaceId}/project/${projectId}/settings`)}>
+
+
+
+
+
+
+ Settings
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/src/app/(flow)/layout.tsx b/src/app/(flow)/layout.tsx
new file mode 100644
index 00000000..a2d853ff
--- /dev/null
+++ b/src/app/(flow)/layout.tsx
@@ -0,0 +1,126 @@
+"use client"
+
+import {useApolloClient} from "@apollo/client/react";
+import {useParams, useRouter} from "next/navigation";
+import {
+ AuroraBackground,
+ ContextStoreProvider,
+ DataTypeView,
+ DLayout,
+ DNamespaceMemberView,
+ DNamespaceProjectView,
+ DNamespaceRoleView,
+ DNamespaceView,
+ DOrganizationView,
+ DRuntimeView,
+ DUserView,
+ Flex,
+ FlowTypeView,
+ FunctionDefinitionView,
+ useUserSession
+} from "@code0-tech/pictor";
+import React from "react";
+import {GraphqlClient} from "@core/util/graphql-client";
+import {Flow, Namespace, NamespaceProject} from "@code0-tech/sagittarius-graphql-types";
+import {usePersistentReactiveArrayService} from "@/hooks/usePersistentReactiveArrayService";
+import {UserService} from "@edition/user/services/User.service";
+import {OrganizationService} from "@edition/organization/services/Organization.service";
+import {MemberService} from "@edition/member/services/Member.service";
+import {NamespaceService} from "@edition/namespace/services/Namespace.service";
+import {RuntimeService} from "@edition/runtime/services/Runtime.service";
+import {ProjectService} from "@edition/project/services/Project.service";
+import {RoleService} from "@edition/role/services/Role.service";
+import {FlowService} from "@edition/flow/services/Flow.service";
+import {FunctionService} from "@edition/function/services/Function.service";
+import {DatatypeService} from "@edition/datatype/services/Datatype.service";
+import {FlowTypeService} from "@edition/flowtype/services/FlowTypeService";
+import {FileTabsView} from "@code0-tech/pictor/dist/components/file-tabs/FileTabs.view";
+import {FileTabsService} from "@code0-tech/pictor/dist/components/file-tabs/FileTabs.service";
+import Image from "next/image";
+
+export default function Layout({bar, tab, children}: {
+ bar: React.ReactNode,
+ tab: React.ReactNode,
+ children: React.ReactNode
+}) {
+ const client = useApolloClient()
+ const router = useRouter()
+ const params = useParams()
+ const currentSession = useUserSession()
+
+ const graphqlClient = React.useMemo(() => new GraphqlClient(client), [client])
+
+ const namespaceIndex = params.namespaceId as any as number
+ const projectIndex = params.projectId as any as number
+ const flowIndex = params.flowId as any as number
+ const namespaceId: Namespace['id'] = `gid://sagittarius/Namespace/${namespaceIndex}`
+ const projectId: NamespaceProject['id'] = `gid://sagittarius/NamespaceProject/${projectIndex}`
+ const flowId: Flow['id'] = `gid://sagittarius/Flow/${flowIndex}`
+
+ if (currentSession === null) router.push("/login")
+
+ const user = usePersistentReactiveArrayService
(`dashboard::users::${currentSession?.id}`, (store) => new UserService(graphqlClient, store))
+ const organization = usePersistentReactiveArrayService(`dashboard::organizations::${currentSession?.id}`, (store) => new OrganizationService(graphqlClient, store))
+ const member = usePersistentReactiveArrayService(`dashboard::members::${currentSession?.id}`, (store) => new MemberService(graphqlClient, store))
+ const namespace = usePersistentReactiveArrayService(`dashboard::namespaces::${currentSession?.id}`, (store) => new NamespaceService(graphqlClient, store))
+ const runtime = usePersistentReactiveArrayService(`dashboard::global_runtimes::${currentSession?.id}`, (store) => new RuntimeService(graphqlClient, store))
+ const project = usePersistentReactiveArrayService(`dashboard::projects::${currentSession?.id}`, (store) => new ProjectService(graphqlClient, store))
+ const role = usePersistentReactiveArrayService(`dashboard::roles::${currentSession?.id}`, (store) => new RoleService(graphqlClient, store))
+ const flow = usePersistentReactiveArrayService(`dashboard::flows::${currentSession?.id}`, (store) => new FlowService(graphqlClient, store))
+ const functions = usePersistentReactiveArrayService(`dashboard::functions::${currentSession?.id}`, (store) => new FunctionService(graphqlClient, store))
+ const datatype = usePersistentReactiveArrayService(`dashboard::datatypes::${currentSession?.id}`, (store) => new DatatypeService(graphqlClient, store))
+ const flowtype = usePersistentReactiveArrayService(`dashboard::flowtypes::${currentSession?.id}`, (store) => new FlowTypeService(graphqlClient, store))
+ const file = usePersistentReactiveArrayService(`dashboard::files::${flowId}`, FileTabsService, [])
+
+
+ const runtimeId = React.useMemo(() => project[1].getById(projectId, {namespaceId})?.primaryRuntime?.id, [projectId, project[0], namespaceId])
+
+ React.useEffect(() => {
+ if (!currentSession) return
+
+ flow[1].values({namespaceId, projectId})
+ functions[1].values({namespaceId, projectId, runtimeId})
+ datatype[1].values({namespaceId, projectId, runtimeId})
+ flowtype[1].values({namespaceId, projectId, runtimeId})
+ }, [runtimeId, namespaceId, projectId, currentSession, flow, functions, datatype, flowtype])
+
+ return
+
+
+
+ {tab}
+
+ }>
+ {bar}>}>
+
+ <>
+ {children}
+ >
+
+
+
+
+}
\ No newline at end of file
diff --git a/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/[flowId]/page.tsx b/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/[flowId]/page.tsx
new file mode 100644
index 00000000..3ac5b622
--- /dev/null
+++ b/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/[flowId]/page.tsx
@@ -0,0 +1,59 @@
+"use client"
+
+import {
+ Button,
+ DFlow,
+ DFlowTabs,
+ DLayout,
+ DResizableHandle,
+ DResizablePanel,
+ DResizablePanelGroup,
+ Flex,
+ Text
+} from "@code0-tech/pictor";
+import React from "react";
+import {IconDatabase, IconFile, IconMessageChatbot} from "@tabler/icons-react";
+import {useParams} from "next/navigation";
+import {Flow} from "@code0-tech/sagittarius-graphql-types";
+
+export default function Page() {
+
+ const params = useParams()
+
+ const flowIndex = params.flowId as any as number
+ const flowId: Flow['id'] = `gid://sagittarius/Flow/${flowIndex}`
+
+ const [show, setShow] = React.useState(false);
+
+ return
+
+ setShow(prevState => !prevState)} variant={"none"}
+ paddingSize={"xs"}>
+
+
+
+
+
+
+
+
+
+ }>
+
+
+
+
+ {show && (
+ <>
+
+
+
+
+ >
+ )}
+
+
+
+}
\ No newline at end of file
diff --git a/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/layout.tsx b/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/layout.tsx
new file mode 100644
index 00000000..ae24ca0d
--- /dev/null
+++ b/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/layout.tsx
@@ -0,0 +1,22 @@
+"use client"
+
+import React from "react";
+import {DResizableHandle, DResizablePanel, DResizablePanelGroup} from "@code0-tech/pictor";
+import {FlowFolderView} from "@edition/flow/views/FlowFolderView";
+
+interface ApplicationLayoutProps {
+ children: React.ReactNode
+}
+
+const ApplicationLayout: React.FC = ({children}) => {
+
+ return
+
+
+
+
+ {children}
+
+}
+
+export default ApplicationLayout
diff --git a/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/page.tsx b/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/page.tsx
new file mode 100644
index 00000000..2022222c
--- /dev/null
+++ b/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/page.tsx
@@ -0,0 +1,71 @@
+"use client"
+
+import {
+ Button,
+ Col,
+ DResizablePanel,
+ Flex,
+ ScrollArea,
+ ScrollAreaScrollbar,
+ ScrollAreaThumb,
+ ScrollAreaViewport,
+ Spacing,
+ Text
+} from "@code0-tech/pictor";
+import React from "react";
+import Link from "next/link";
+import {useParams} from "next/navigation";
+
+export default function Page() {
+
+ const params = useParams()
+
+ const namespaceIndex = params?.namespaceId as any as number
+
+ return
+
+
+
+
+ Create or select a flow
+
+
+ Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor
+ invidunt ut
+ labore et dolore magna aliquyam erat, sed diam voluptua.
+
+
+
+
+
+ Go back to project overview
+
+
+
+ Create new flow
+
+
+
+ Or start from a template
+
+
+
+
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/settings/page.tsx b/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/settings/page.tsx
new file mode 100644
index 00000000..17a4168e
--- /dev/null
+++ b/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/settings/page.tsx
@@ -0,0 +1,8 @@
+"use client"
+
+import React from "react";
+import {ProjectSettingsPage} from "@edition/project/pages/ProjectSettingsPage";
+
+export default function Page() {
+ return
+}
diff --git a/src/app/global.scss b/src/app/global.scss
index 90ae320d..97d13f8e 100644
--- a/src/app/global.scss
+++ b/src/app/global.scss
@@ -3,7 +3,7 @@
}
html, body {
- background: #030014;
+ background: rgb(25.4, 23.6, 37.1);
overflow-x: hidden;
height: 100%;
width: 100%;
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index e926e7a5..4e229cad 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -68,6 +68,7 @@ const ErrorCodeDescription: Record = {
* Handles error toasts based on error type
*/
const toastHandler = (error: Error | GraphQLFormattedError) => {
+ console.error("[ERROR]", error)
if (error && "errorCode" in error) {
toast({
title: ErrorCodeDescription[(error.errorCode as string)],
diff --git a/src/hooks/usePersistentReactiveArrayService.ts b/src/hooks/usePersistentReactiveArrayService.ts
index a9e4d52c..0b4bf707 100644
--- a/src/hooks/usePersistentReactiveArrayService.ts
+++ b/src/hooks/usePersistentReactiveArrayService.ts
@@ -3,17 +3,18 @@
import React from "react";
import type {ArrayService} from "@code0-tech/pictor/dist/utils/arrayService";
import type {ReactiveArrayStore} from "@code0-tech/pictor/dist/utils/reactiveArrayService";
+import {Payload, View} from "@code0-tech/pictor/dist/utils/view";
-export type ArrayServiceCtor> = new (store: ReactiveArrayStore) => S;
-export type ServiceFactory> = ArrayServiceCtor | ((store: ReactiveArrayStore) => S);
-type InitialArg> = K[] | ((svc: S) => K[]);
+export type ArrayServiceCtor, R>> = new (store: ReactiveArrayStore>) => S;
+export type ServiceFactory, R>> = ArrayServiceCtor | ((store: ReactiveArrayStore>) => S);
+type InitialArg, R>> = View[] | ((svc: S) => View[]);
type Listener = () => void;
-type PersistentEntry> = {
+type PersistentEntry, R>> = {
service: S;
- store: ReactiveArrayStore;
- getSnapshot: () => K[];
+ store: ReactiveArrayStore>;
+ getSnapshot: () => View[];
listeners: Set;
};
@@ -31,30 +32,30 @@ const registry = (): Registry => {
return globalThis.__sculptorPersistentReactiveServices;
};
-function isConstructor>(value: ServiceFactory): value is ArrayServiceCtor {
+function isConstructor, R>>(value: ServiceFactory): value is ArrayServiceCtor {
return typeof value === "function" && !!value.prototype && value.prototype.constructor === value;
}
-function instantiateService>(factory: ServiceFactory, store: ReactiveArrayStore): S {
+function instantiateService, R>>(factory: ServiceFactory, store: ReactiveArrayStore>): S {
return isConstructor(factory)
? new factory(store)
: factory(store);
}
-function ensureEntry>(key: string, factory: ServiceFactory, initial: InitialArg): PersistentEntry {
+function ensureEntry, R>>(key: string, factory: ServiceFactory, initial: InitialArg): PersistentEntry {
const map = registry();
const existing = map.get(key);
if (existing) {
- return existing as PersistentEntry;
+ return existing as PersistentEntry;
}
const listeners = new Set();
- let snapshot: K[] = Array.isArray(initial) ? [...initial] : [];
+ let snapshot: View[] = Array.isArray(initial) ? [...initial] : [];
- const store: ReactiveArrayStore = {
+ const store: ReactiveArrayStore> = {
getState: () => snapshot,
setState: (update) => {
- snapshot = typeof update === "function" ? (update as (prev: K[]) => K[])(snapshot) : update;
+ snapshot = typeof update === "function" ? (update as (prev: View[]) => View[])(snapshot) : update;
listeners.forEach((listener) => listener());
},
};
@@ -65,7 +66,7 @@ function ensureEntry>(key: string, factory: Service
snapshot = initial(service);
}
- const entry: PersistentEntry = {
+ const entry: PersistentEntry = {
service,
store,
getSnapshot: () => snapshot,
@@ -76,11 +77,11 @@ function ensureEntry>(key: string, factory: Service
return entry;
}
-export function usePersistentReactiveArrayService>(
+export function usePersistentReactiveArrayService, R>>(
key: string,
- factory: ServiceFactory,
- initial: InitialArg = []
-): [K[], S] {
+ factory: ServiceFactory,
+ initial: InitialArg = []
+): [View[], S] {
const entry = React.useMemo(() => ensureEntry(key, factory, initial), [key, factory, initial]);
const subscribe = React.useCallback((listener: Listener) => {
diff --git a/src/packages/ce/src/dashboard/application/ApplicationBarView.tsx b/src/packages/ce/src/application/ApplicationBarView.tsx
similarity index 56%
rename from src/packages/ce/src/dashboard/application/ApplicationBarView.tsx
rename to src/packages/ce/src/application/ApplicationBarView.tsx
index 52822885..f16099f9 100644
--- a/src/packages/ce/src/dashboard/application/ApplicationBarView.tsx
+++ b/src/packages/ce/src/application/ApplicationBarView.tsx
@@ -1,35 +1,39 @@
"use client"
import {
+ Avatar,
Badge,
- Breadcrumb, Button,
- Container,
+ Button,
Flex,
MenuItem,
MenuSeparator,
- Text, TextInput,
+ TextInput,
useService,
+ useStore,
useUserSession
} from "@code0-tech/pictor";
-import {UserService} from "@edition/user/User.service";
+import {UserService} from "@edition/user/services/User.service";
import {useRouter} from "next/navigation";
import React from "react";
import DUserMenu from "@code0-tech/pictor/dist/components/d-user/DUserMenu";
import Link from "next/link";
import {IconBuilding, IconFolders, IconInbox, IconLogout, IconSearch} from "@tabler/icons-react";
-import Image from "next/image";
-import {ApplicationBreadcrumbView} from "@edition/dashboard/application/ApplicationBreadcrumbView";
+import {ApplicationBreadcrumbView} from "@edition/application/ApplicationBreadcrumbView";
export const ApplicationBarView: React.FC = () => {
+
const currentSession = useUserSession()
const userService = useService(UserService)
+ const userStore = useStore(UserService)
const router = useRouter()
const [loading, startTransition] = React.useTransition()
+ const currentUser = React.useMemo(() => userService.getById(currentSession?.user?.id), [userStore, currentSession])
+ const namespaceIndex = React.useMemo(() => currentUser?.namespace?.id?.match(/Namespace\/(\d+)$/)?.[1], [currentUser])
const userMenu = React.useMemo(() => {
- if (!currentSession?.token) {
+ if (!currentUser || !currentSession) {
return null
}
@@ -44,8 +48,6 @@ export const ApplicationBarView: React.FC = () => {
})
}
- const namespaceIndex = currentSession.user?.namespace?.id?.match(/Namespace\/(\d+)$/)?.[1]
-
return
@@ -62,25 +64,18 @@ export const ApplicationBarView: React.FC = () => {
Logout
- }, [currentSession])
+ }, [currentUser, currentSession, namespaceIndex])
- return
-
-
-
-
-
-
-
- } right={⌘K } rightType={"icon"}
- placeholder={"Search..."}/>
-
-
-
- {userMenu}
-
-
+ return
+
+
+
+
+
+
+
+
+ {userMenu}
-
+
}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/application/ApplicationBreadcrumbView.tsx b/src/packages/ce/src/application/ApplicationBreadcrumbView.tsx
similarity index 92%
rename from src/packages/ce/src/dashboard/application/ApplicationBreadcrumbView.tsx
rename to src/packages/ce/src/application/ApplicationBreadcrumbView.tsx
index 19d1357c..e7320fd0 100644
--- a/src/packages/ce/src/dashboard/application/ApplicationBreadcrumbView.tsx
+++ b/src/packages/ce/src/application/ApplicationBreadcrumbView.tsx
@@ -4,12 +4,12 @@ import React from "react";
import {Badge, Breadcrumb, Text, useService, useStore} from "@code0-tech/pictor";
import {useParams, usePathname, useRouter} from "next/navigation";
import type {Namespace, NamespaceProject, NamespaceRole, Runtime, User} from "@code0-tech/sagittarius-graphql-types";
-import {NamespaceService} from "@edition/namespace/Namespace.service";
-import {RuntimeService} from "@edition/runtime/Runtime.service";
-import {UserService} from "@edition/user/User.service";
-import {ProjectService} from "@edition/project/Project.service";
-import {RoleService} from "@edition/role/Role.service";
-import {OrganizationService} from "@edition/organization/Organization.service";
+import {NamespaceService} from "@edition/namespace/services/Namespace.service";
+import {RuntimeService} from "@edition/runtime/services/Runtime.service";
+import {UserService} from "@edition/user/services/User.service";
+import {ProjectService} from "@edition/project/services/Project.service";
+import {RoleService} from "@edition/role/services/Role.service";
+import {OrganizationService} from "@edition/organization/services/Organization.service";
export const ApplicationBreadcrumbView: React.FC = () => {
@@ -195,12 +195,13 @@ export const ApplicationBreadcrumbView: React.FC = () => {
}, [pathSegments, collectionSegments, getSegmentLabel, resolveDestination])
return
+ router.push("/")}>
+ Home
+
{breadcrumbs.map((crumb, index) => {
- return crumb.isBadge ? router.push(crumb.destination)}>
-
- {crumb.label}
-
- : router.push(crumb.destination)}>
+ return crumb.isBadge ? router.push(crumb.destination)}>
+ {crumb.label}
+ : router.push(crumb.destination)}>
{crumb.label}
})}
diff --git a/src/packages/ce/src/application/ApplicationPage.tsx b/src/packages/ce/src/application/ApplicationPage.tsx
new file mode 100644
index 00000000..4b2dc5f0
--- /dev/null
+++ b/src/packages/ce/src/application/ApplicationPage.tsx
@@ -0,0 +1,85 @@
+import {PersonalProjectsView} from "@edition/project/views/PersonalProjectsView";
+import {OrganizationsView} from "@edition/organization/views/OrganizationsView";
+import {
+ AuroraBackground,
+ Avatar,
+ Badge,
+ Card,
+ Col,
+ Flex,
+ Row,
+ ScrollArea,
+ ScrollAreaScrollbar,
+ ScrollAreaThumb,
+ ScrollAreaViewport,
+ Spacing,
+ Text,
+ useService,
+ useStore,
+ useUserSession
+} from "@code0-tech/pictor";
+import {UserService} from "@edition/user/services/User.service";
+import React from "react";
+import {OrganizationsTopView} from "@edition/organization/views/OrganizationsTopView";
+
+export const ApplicationPage = () => {
+
+ const userStore = useStore(UserService)
+ const userService = useService(UserService)
+
+ const userSession = useUserSession()
+ const currentUser = React.useMemo(() => userService.getById(userSession?.user?.id), [userStore, userSession])
+
+ return
+
+
+
+
+
+
+
+ Hi, @{currentUser?.username ?? "user"},
+
+ let's write some flows
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/application/ApplicationSettingsPage.tsx b/src/packages/ce/src/application/ApplicationSettingsPage.tsx
similarity index 66%
rename from src/packages/ce/src/dashboard/application/ApplicationSettingsPage.tsx
rename to src/packages/ce/src/application/ApplicationSettingsPage.tsx
index c301b0c7..93c4a382 100644
--- a/src/packages/ce/src/dashboard/application/ApplicationSettingsPage.tsx
+++ b/src/packages/ce/src/application/ApplicationSettingsPage.tsx
@@ -5,9 +5,10 @@ import {
Badge,
Button,
Card,
- DLayout,
+ DResizableHandle,
+ DResizablePanel,
+ DResizablePanelGroup,
Flex,
- InputLabel,
Spacing,
SwitchInput,
Text,
@@ -15,10 +16,10 @@ import {
useStore,
useUserSession
} from "@code0-tech/pictor";
-import {UserService} from "@edition/user/User.service";
+import {UserService} from "@edition/user/services/User.service";
import {notFound} from "next/navigation";
import {Tab, TabContent, TabList, TabTrigger} from "@code0-tech/pictor/dist/components/tab/Tab";
-import {IconGavel, IconSettings} from "@tabler/icons-react";
+import {IconLayoutSidebar} from "@tabler/icons-react";
import CardSection from "@code0-tech/pictor/dist/components/card/CardSection";
import process from "node:process";
@@ -32,33 +33,43 @@ export const ApplicationSettingsPage: React.FC = () => {
notFound()
}
- return <>
-
-
-
- Application
-
-
-
- General
-
-
-
-
-
- Restrictions
+ return
+
+
+
+
+ Application settings
+
+
+
-
-
- }>
+
+
+ General settings and restrictions for your Sculptor application. These settings affect all users
+ and organizations within the application.
+
+
+
+
+ General
+
+
+
+
+ Restrictions
+
+
+
+
+
+
+
<>
General
-
-
-
+
@@ -94,10 +105,8 @@ export const ApplicationSettingsPage: React.FC = () => {
Restrictions
-
-
-
-
+
+
Organization creation
@@ -108,11 +117,8 @@ export const ApplicationSettingsPage: React.FC = () => {
-
-
- Danger Zone
-
-
+
+
User registration
@@ -121,12 +127,12 @@ export const ApplicationSettingsPage: React.FC = () => {
-
-
+
+
>
-
-
- >
+
+
+
}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/application/ApplicationTabView.tsx b/src/packages/ce/src/application/ApplicationTabView.tsx
similarity index 66%
rename from src/packages/ce/src/dashboard/application/ApplicationTabView.tsx
rename to src/packages/ce/src/application/ApplicationTabView.tsx
index 5e1115d0..d0ab27c2 100644
--- a/src/packages/ce/src/dashboard/application/ApplicationTabView.tsx
+++ b/src/packages/ce/src/application/ApplicationTabView.tsx
@@ -5,9 +5,10 @@ import {Tab, TabList, TabTrigger} from "@code0-tech/pictor/dist/components/tab/T
import {Badge, Button, Container, useService, useStore, useUserSession} from "@code0-tech/pictor";
import {IconBuilding, IconHome, IconServer, IconSettings, IconUser} from "@tabler/icons-react";
import {usePathname, useRouter} from "next/navigation";
-import {UserService} from "@edition/user/User.service";
-import {RuntimeService} from "@edition/runtime/Runtime.service";
-import {OrganizationService} from "@edition/organization/Organization.service";
+import {UserService} from "@edition/user/services/User.service";
+import {RuntimeService} from "@edition/runtime/services/Runtime.service";
+import {OrganizationService} from "@edition/organization/services/Organization.service";
+import {hashToColor} from "@code0-tech/pictor/dist/components/d-flow/DFlow.util";
export const ApplicationTabView: React.FC = () => {
@@ -32,23 +33,18 @@ export const ApplicationTabView: React.FC = () => {
return currentUser && currentUser.admin ? (
<>
- router.push("/users")}>
+ router.push("/users")}>
- Users
- {userService.values().length}
- router.push("/runtimes")}>
+ router.push("/runtimes")}>
- Runtimes
- {runtimeService.values().filter(runtime => !runtime.namespace?.id).length}
- router.push("/settings")}>
+ router.push("/settings")}>
- Settings
@@ -56,24 +52,19 @@ export const ApplicationTabView: React.FC = () => {
) : null
}, [currentUser, runtimeStore, organizationStore])
- return
-
+ return
- router.push("/")}>
+ router.push("/")}>
- Overview
- router.push("/organizations")}>
+ router.push("/organizations")}>
- Organizations
- {organizationService.values().length}
{adminLinks}
-
}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/application/ApplicationPage.tsx b/src/packages/ce/src/dashboard/application/ApplicationPage.tsx
deleted file mode 100644
index 304d6e78..00000000
--- a/src/packages/ce/src/dashboard/application/ApplicationPage.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import {PersonalProjectsView} from "@edition/dashboard/personal-project/PersonalProjectsView";
-import {OrganizationsView} from "@edition/dashboard/organization/OrganizationsView";
-import {Spacing} from "@code0-tech/pictor";
-
-export const ApplicationPage = () => {
-
- return <>
-
-
-
-
-
- >
-
-}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/member/MemberAddPage.tsx b/src/packages/ce/src/dashboard/member/MemberAddPage.tsx
deleted file mode 100644
index d7809127..00000000
--- a/src/packages/ce/src/dashboard/member/MemberAddPage.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-"use client"
-
-import React from "react";
-import {
- Button,
- Col,
- DUserInput,
- DUserView,
- Flex,
- InputSuggestion,
- Spacing,
- Text,
- useForm,
- useService, useStore
-} from "@code0-tech/pictor";
-import {MemberService} from "@edition/member/Member.service";
-import {useParams, useRouter} from "next/navigation";
-import {Namespace} from "@code0-tech/sagittarius-graphql-types";
-import Link from "next/link";
-import {UserService} from "@edition/user/User.service";
-
-export const MemberAddPage: React.FC = () => {
-
- const params = useParams()
- const memberService = useService(MemberService)
- const memberStore = useStore(MemberService)
- const userService = useService(UserService)
- const userStore = useStore(UserService)
- const router = useRouter()
- const [, startTransition] = React.useTransition()
-
- const namespaceIndex = params?.namespaceId as string
- const namespaceId: Namespace['id'] = `gid://sagittarius/Namespace/${namespaceIndex as unknown as number}`
-
- const members = React.useMemo(() => memberService.values({namespaceId: namespaceId}), [memberStore, userStore])
- const formInitialValues = React.useMemo(() => ({ users: null }), [])
- const filteredUsers = React.useMemo(() => {
- return (user: DUserView) => {
- return !members.find(m => m.user?.id === user.id)
- }
- }, [members])
-
- const [inputs, validate] = useForm<{ users: null | InputSuggestion[] }>({
- initialValues: formInitialValues,
- validate: {
- users: (value) => {
- if (!value) return "Please select at least one user"
- if (value.length <= 0) return "Please select at least one user"
- return null
- }
- },
- onSubmit: (values) => {
- startTransition(async () => {
- for (const value of values.users!!) {
- await memberService.memberInvite({
- namespaceId: namespaceId!!,
- userId: (value.valueData as DUserView).id!!
- })
- }
- router.push(`/namespace/${namespaceIndex}/members`)
- })
- }
- })
-
- //TODO: user abilities for add user as member within namespace
-
- return
-
-
- Add new members
-
-
-
- Global runtimes are shared runtimes that can be used across multiple organizations.
-
-
- {/*@ts-ignore*/}
-
-
-
-
-
- Go back to members
-
-
-
- Add members
-
-
-
-
-}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/member/MembersPage.tsx b/src/packages/ce/src/dashboard/member/MembersPage.tsx
deleted file mode 100644
index a201120d..00000000
--- a/src/packages/ce/src/dashboard/member/MembersPage.tsx
+++ /dev/null
@@ -1,8 +0,0 @@
-"use client"
-
-import {MembersView} from "@edition/dashboard/member/MembersView";
-import React from "react";
-
-export const MembersPage: React.FC = () => {
- return
-}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/member/MembersView.tsx b/src/packages/ce/src/dashboard/member/MembersView.tsx
deleted file mode 100644
index 807a386e..00000000
--- a/src/packages/ce/src/dashboard/member/MembersView.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-"use client"
-
-import React from "react";
-import {useParams} from "next/navigation";
-import {Button, DNamespaceMemberList, Flex, Spacing, Text, useService, useStore} from "@code0-tech/pictor";
-import Link from "next/link";
-import {MemberService} from "@edition/member/Member.service";
-
-export const MembersView: React.FC = () => {
-
- const params = useParams()
- const namespaceId = params.namespaceId as any as number
- const memberService = useService(MemberService)
- const memberStore = useStore(MemberService)
- const [, startTransition] = React.useTransition()
-
- const membersList = React.useMemo(() => {
- return {
- startTransition(() => {
- memberService.memberAssignRoles({
- memberId: member.id!,
- roleIds: roles.map(r => r.id!)
- })
- })
- }} onRemove={(member) => {
- startTransition(() => {
- memberService.memberDelete({
- namespaceMemberId: member.id!
- })
- })
- }} namespaceId={`gid://sagittarius/Namespace/${namespaceId}`}/>
- }, [namespaceId, memberStore])
-
- //TODO: user abilities for add user as member within namespace
-
- return <>
-
-
-
- Members
-
-
-
- Add user
-
-
-
-
- {membersList}
-
- >
-
-}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/namespace/NamespaceOverviewLeftView.tsx b/src/packages/ce/src/dashboard/namespace/NamespaceOverviewLeftView.tsx
deleted file mode 100644
index dd812519..00000000
--- a/src/packages/ce/src/dashboard/namespace/NamespaceOverviewLeftView.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import React from "react";
-import {Avatar, Badge, Button, Flex, Spacing, Text, useService, useStore, useUserSession} from "@code0-tech/pictor";
-import {UserService} from "@edition/user/User.service";
-import {IconMail, IconSparkles, IconUser, IconUserCog} from "@tabler/icons-react";
-import {OrganizationService} from "@edition/organization/Organization.service";
-
-export const NamespaceOverviewLeftView: React.FC = () => {
-
- const userService = useService(UserService)
- const userStore = useStore(UserService)
- const organizationService = useService(OrganizationService)
- const organizationStore = useStore(OrganizationService)
- const currentSession = useUserSession()
-
- const currentUser = React.useMemo(() => userService.getById(currentSession?.user?.id), [userStore, currentSession])
- const organizations = React.useMemo(() => organizationService.values(), [organizationStore])
-
- return
-
-
-
-
- {currentUser?.firstname} {currentUser?.lastname}
- {currentUser?.email}
-
-
-
-
-
-
- @{currentUser?.username}
-
-
-
- {currentUser?.admin ? Admin :
- User }
-
-
-
- {currentUser?.email}
-
-
-
- {currentUser?.admin ? PRO : BASIC }
-
-
-
- Edit Profile
-
-
-
- Organizations
-
-
- {organizations.map(organization => {
- return
- })}
-
-
-
-}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/namespace/NamespaceOverviewPage.tsx b/src/packages/ce/src/dashboard/namespace/NamespaceOverviewPage.tsx
deleted file mode 100644
index d09fceea..00000000
--- a/src/packages/ce/src/dashboard/namespace/NamespaceOverviewPage.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-"use client"
-
-import React from "react";
-import {NamespaceProjectsView} from "@edition/dashboard/namespace/NamespaceProjectsView";
-import {DLayout, useService, useStore, useUserSession} from "@code0-tech/pictor";
-import {NamespaceOverviewRightView} from "@edition/dashboard/namespace/NamespaceOverviewRightView";
-import {NamespaceOverviewLeftView} from "@edition/dashboard/namespace/NamespaceOverviewLeftView";
-import {useParams} from "next/navigation";
-import {UserService} from "@edition/user/User.service";
-
-export const NamespaceOverviewPage: React.FC = () => {
-
- const params = useParams()
- const userService = useService(UserService)
- const userStore = useStore(UserService)
- const currentSession = useUserSession()
-
- const currentUser = React.useMemo(() => userService.getById(currentSession?.user?.id), [userStore, currentSession])
- const namespaceIndexCurrentUser = currentUser?.namespace?.id?.match(/Namespace\/(\d+)$/)?.[1]
- const namespaceId = params.namespaceId as any as string
-
- return : undefined}
- rightContent={ }>
-
-
-}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/namespace/NamespaceProjectsPage.tsx b/src/packages/ce/src/dashboard/namespace/NamespaceProjectsPage.tsx
deleted file mode 100644
index c66e04c2..00000000
--- a/src/packages/ce/src/dashboard/namespace/NamespaceProjectsPage.tsx
+++ /dev/null
@@ -1,8 +0,0 @@
-"use client"
-
-import {NamespaceProjectsView} from "@edition/dashboard/namespace/NamespaceProjectsView";
-import React from "react";
-
-export const NamespaceProjectsPage: React.FC = () => {
- return
-}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/namespace/NamespaceProjectsView.tsx b/src/packages/ce/src/dashboard/namespace/NamespaceProjectsView.tsx
deleted file mode 100644
index 5a72aa44..00000000
--- a/src/packages/ce/src/dashboard/namespace/NamespaceProjectsView.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-"use client"
-
-
-import React from "react";
-import {Button, DNamespaceProjectList, Flex, Spacing, Text} from "@code0-tech/pictor";
-import Link from "next/link";
-import {useParams} from "next/navigation";
-
-export const NamespaceProjectsView: React.FC = () => {
-
- const params = useParams()
- const namespaceId = params.namespaceId as any as number
-
- const projectsList = React.useMemo(() => {
- return
- }, [namespaceId])
-
- //TODO: user abilities for project creation within namespace
-
- return <>
-
-
-
- Projects
-
-
-
- Create project
-
-
-
-
- {projectsList}
-
- >
-}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/namespace/NamespaceTabView.tsx b/src/packages/ce/src/dashboard/namespace/NamespaceTabView.tsx
deleted file mode 100644
index 8c21c806..00000000
--- a/src/packages/ce/src/dashboard/namespace/NamespaceTabView.tsx
+++ /dev/null
@@ -1,103 +0,0 @@
-"use client"
-
-import React from "react";
-import {Badge, Button, Container, useService, useStore} from "@code0-tech/pictor";
-import {Tab, TabList, TabTrigger} from "@code0-tech/pictor/dist/components/tab/Tab";
-import {IconFolders, IconHome, IconServer, IconSettings, IconUserCog, IconUsers} from "@tabler/icons-react";
-import {useParams, usePathname, useRouter} from "next/navigation";
-import {NamespaceService} from "@edition/namespace/Namespace.service";
-import {OrganizationService} from "@edition/organization/Organization.service";
-import {ProjectService} from "@edition/project/Project.service";
-import {RoleService} from "@edition/role/Role.service";
-import {MemberService} from "@edition/member/Member.service";
-import {RuntimeService} from "@edition/runtime/Runtime.service";
-
-export const NamespaceTabView: React.FC = () => {
-
- const pathname = usePathname()
- const router = useRouter()
- const params = useParams()
- const namespaceId = params.namespaceId as any
-
- const namespaceService = useService(NamespaceService)
- const namespaceStore = useStore(NamespaceService)
- const organizationService = useService(OrganizationService)
- const organizationStore = useStore(OrganizationService)
- const projectService = useService(ProjectService)
- const roleService = useService(RoleService)
- const memberService = useService(MemberService)
- const runtimeService = useService(RuntimeService)
- const runtimeStore = useStore(RuntimeService)
-
- const namespace = React.useMemo(() => namespaceService.getById(`gid://sagittarius/Namespace/${namespaceId}`), [namespaceStore, namespaceId])
- const parentOrganization = React.useMemo(() => namespace?.parent?.__typename === "Organization" ? organizationService.getById(namespace?.parent?.id) : null, [organizationStore, namespace])
-
- const baseLink = `/namespace/${namespaceId}`
- const defaultValue = pathname.includes("projects") ? "projects"
- : pathname.includes("members") ? "members"
- : pathname.includes("roles") ? "roles"
- : pathname.includes("runtimes") ? "runtimes"
- : pathname.includes("settings") ? "settings"
- : "overview"
-
- const settings = React.useMemo(() => {
- return namespace?.parent?.__typename == "Organization"
- && parentOrganization
- && (
- parentOrganization.userAbilities?.deleteOrganization
- || parentOrganization?.userAbilities?.updateOrganization
- //TODO add license check for enterprise features
- ) ? (
-
- router.push(`${baseLink}/settings`)}>
-
- Settings
-
-
- ) : null
- }, [namespace, parentOrganization])
-
- return
-
-
-
- router.push(baseLink)}>
-
- Overview
-
-
-
- router.push(`${baseLink}/projects`)}>
-
- Projects
- {projectService.values({namespaceId: `gid://sagittarius/Namespace/${namespaceId}`}).length}
-
-
-
- router.push(`${baseLink}/members`)}>
-
- Members
- {memberService.values({namespaceId: `gid://sagittarius/Namespace/${namespaceId}`}).length}
-
-
-
- router.push(`${baseLink}/roles`)}>
-
- Roles
- {roleService.values({namespaceId: `gid://sagittarius/Namespace/${namespaceId}`}).length}
-
-
-
- router.push(`${baseLink}/runtimes`)}>
-
- Runtimes
- {React.useMemo(() => {
- return {runtimeService.values({namespaceId: `gid://sagittarius/Namespace/${namespaceId}`}).length}
- }, [runtimeStore, namespaceId])}
-
-
- {settings}
-
-
-
-}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/organization/OrganizationCreatePage.tsx b/src/packages/ce/src/dashboard/organization/OrganizationCreatePage.tsx
deleted file mode 100644
index 9970b594..00000000
--- a/src/packages/ce/src/dashboard/organization/OrganizationCreatePage.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-"use client"
-
-import {Button, Col, Flex, Spacing, Text, TextInput, useForm, useService} from "@code0-tech/pictor";
-import Link from "next/link";
-import {OrganizationService} from "@edition/organization/Organization.service";
-import React from "react";
-import {useRouter} from "next/navigation";
-
-export const OrganizationCreatePage = () => {
-
- const organizationService = useService(OrganizationService)
- const [, startTransition] = React.useTransition()
- const router = useRouter()
-
- const [inputs, validate] = useForm({
- initialValues: {
- name: null,
- },
- validate: {
- name: (value) => {
- if (!value) return "Name is required"
- return null
- }
- },
- onSubmit: (values) => {
- startTransition(() => {
- organizationService.organizationCreate({
- name: values.name as unknown as string
- }).then(payload => {
- if ((payload?.errors?.length ?? 0) <= 0) {
- router.push("/organizations")
- }
- })
- })
- }
- })
-
- return
-
-
- Create new organization
-
-
-
- Organizations are helpfully if managing a group of users and plenty of projects.
-
-
-
-
-
-
-
- Go back to organizations
-
-
-
- Create organization
-
-
-
-
-}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/organization/OrganizationSettingsPage.tsx b/src/packages/ce/src/dashboard/organization/OrganizationSettingsPage.tsx
deleted file mode 100644
index 92c36b27..00000000
--- a/src/packages/ce/src/dashboard/organization/OrganizationSettingsPage.tsx
+++ /dev/null
@@ -1,117 +0,0 @@
-"use client"
-
-import React from "react";
-import {useParams} from "next/navigation";
-import {Namespace} from "@code0-tech/sagittarius-graphql-types";
-import {
- AuroraBackground,
- Avatar,
- Badge,
- Button,
- DLayout,
- Flex,
- Spacing,
- Text,
- useService,
- useStore
-} from "@code0-tech/pictor";
-import {NamespaceService} from "@edition/namespace/Namespace.service";
-import {OrganizationService} from "@edition/organization/Organization.service";
-import {IconFolders, IconServer, IconUserCog, IconUsers} from "@tabler/icons-react";
-import {Tab, TabContent, TabList, TabTrigger} from "@code0-tech/pictor/dist/components/tab/Tab";
-import {UsageView} from "@edition/dashboard/usage/UsageView";
-import {OrganizationUpgradeView} from "@edition/dashboard/organization/OrganizationUpgradeView";
-import {OrganizationDeleteView} from "@edition/dashboard/organization/OrganizationDeleteView";
-import {OrganizationGeneralSettingsView} from "@edition/dashboard/organization/OrganizationGeneralSettingsView";
-
-export const OrganizationSettingsPage: React.FC = () => {
-
- //TODO: add ability check for organization settings access for every settings tab
-
- const params = useParams()
- const namespaceService = useService(NamespaceService)
- const namespaceStore = useStore(NamespaceService)
- const organizationService = useService(OrganizationService)
- const organizationStore = useStore(OrganizationService)
-
- const namespaceIndex = params.namespaceId as any as number
- const namespaceId: Namespace['id'] = `gid://sagittarius/Namespace/${namespaceIndex}`
- const namespace = React.useMemo(() => namespaceService.getById(namespaceId), [namespaceStore, namespaceId])
- const parentOrganization = React.useMemo(() => namespace?.parent?.__typename === "Organization" ? organizationService.getById(namespace?.parent?.id) : null, [organizationStore, namespace])
-
- return <>
-
- {React.useMemo(() => {
- return
-
-
-
- {parentOrganization?.name}
-
-
-
-
- {namespace?.members?.count}
-
-
-
- {namespace?.projects?.count}
-
-
-
- {namespace?.roles?.count}
-
-
-
- {namespace?.runtimes?.count}
-
-
-
-
- }, [parentOrganization])}
-
-
-
-
-
- General adjustments
-
-
-
-
- Upgrade to Team
-
-
-
-
-
- Runtime usage
-
-
-
-
- Delete organization forever
-
-
-
- }>
- <>
-
-
-
-
-
-
-
-
-
-
-
-
- >
-
-
- >
-}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/organization/OrganizationsPage.tsx b/src/packages/ce/src/dashboard/organization/OrganizationsPage.tsx
deleted file mode 100644
index cf663f27..00000000
--- a/src/packages/ce/src/dashboard/organization/OrganizationsPage.tsx
+++ /dev/null
@@ -1,8 +0,0 @@
-"use client"
-
-import React from "react";
-import {OrganizationsView} from "@edition/dashboard/organization/OrganizationsView";
-
-export const OrganizationsPage = () => {
- return
-}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/organization/OrganizationsView.tsx b/src/packages/ce/src/dashboard/organization/OrganizationsView.tsx
deleted file mode 100644
index c43533f5..00000000
--- a/src/packages/ce/src/dashboard/organization/OrganizationsView.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-"use client"
-
-import {Button, DOrganizationList, Flex, Spacing, Text, TextInput} from "@code0-tech/pictor";
-import {IconSearch} from "@tabler/icons-react";
-import Link from "next/link";
-import React from "react";
-import {useRouter} from "next/navigation";
-
-export const OrganizationsView = () => {
-
- const router = useRouter()
-
- return <>
-
-
- Organizations
-
-
-
- Create Organization
-
-
-
-
- {/**TODO: use namespaceId*/}
- {
- const number = organization.namespace?.id?.match(/Namespace\/(\d+)$/)?.[1]
- router.push(`/namespace/${number}`)
- }}/>
- >
-
-}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/personal-project/PersonalProjectsView.tsx b/src/packages/ce/src/dashboard/personal-project/PersonalProjectsView.tsx
deleted file mode 100644
index 9eeb08cd..00000000
--- a/src/packages/ce/src/dashboard/personal-project/PersonalProjectsView.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import React from "react";
-import {
- Button,
- DNamespaceProjectList,
- Flex,
- Spacing,
- Text,
- TextInput,
- useService,
- useStore,
- useUserSession
-} from "@code0-tech/pictor";
-import {UserService} from "@edition/user/User.service";
-import {IconSearch} from "@tabler/icons-react";
-import Link from "next/link";
-
-export const PersonalProjectsView: React.FC = () => {
-
- const userSession = useUserSession()
- const userStore = useStore(UserService)
- const userService = useService(UserService)
-
- const currentUser = React.useMemo(() => userService.getById(userSession?.user?.id), [userStore, userSession])
- const namespaceId = currentUser?.namespace?.id
- const namespaceIndex = namespaceId?.match(/Namespace\/(\d+)$/)?.[1]
-
- const projectsList = React.useMemo(() => {
- if (!currentUser || !currentUser.namespace) return null
- return
-
- }, [currentUser])
-
- return <>
-
-
-
- Personal projects
-
-
-
- Create personal project
-
-
-
-
- {projectsList}
-
- >
-
-}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/role/RoleCreatePage.tsx b/src/packages/ce/src/dashboard/role/RoleCreatePage.tsx
deleted file mode 100644
index 0fa649b0..00000000
--- a/src/packages/ce/src/dashboard/role/RoleCreatePage.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-"use client"
-
-import React from "react";
-import {Button, Col, Flex, Spacing, Text, TextInput} from "@code0-tech/pictor";
-import Link from "next/link";
-
-export const RoleCreatePage: React.FC = () => {
-
- //TODO: user abilities for add role within namespace
-
- return
-
-
- Create new role
-
-
-
- Global runtimes are shared runtimes that can be used across multiple organizations.
-
-
-
-
-
-
-
- Go back to roles
-
-
-
- Create role
-
-
-
-
-}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/role/RoleSettingsPage.tsx b/src/packages/ce/src/dashboard/role/RoleSettingsPage.tsx
deleted file mode 100644
index 3f0b5808..00000000
--- a/src/packages/ce/src/dashboard/role/RoleSettingsPage.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-"use client"
-
-import React from "react";
-import type {Namespace, NamespaceRole} from "@code0-tech/sagittarius-graphql-types";
-import {useParams} from "next/navigation";
-import {Button, DLayout, Flex, Spacing, Text, useService, useStore} from "@code0-tech/pictor";
-import {RoleService} from "@edition/role/Role.service";
-import {Tab, TabList, TabTrigger} from "@code0-tech/pictor/dist/components/tab/Tab";
-import {RoleProjectView} from "@edition/dashboard/role/RoleProjectView";
-import {RolePermissionView} from "@edition/dashboard/role/RolePermissionView";
-import {RoleGeneralAdjustmentView} from "@edition/dashboard/role/RoleGeneralAdjustmentView";
-import {RoleDeleteView} from "@edition/dashboard/role/RoleDeleteView";
-
-export const RoleSettingsPage: React.FC = () => {
-
- //TODO: limit tabs based on user abilities for roles
- //TODO: add general adjustments tab for role name update
- //TODO: delete role functionality as tab
-
- const params = useParams()
- const roleService = useService(RoleService)
- const roleStore = useStore(RoleService)
-
- const namespaceIndex = params.namespaceId as any as number
- const roleIndex = params.roleId as any as number
- const namespaceId: Namespace['id'] = `gid://sagittarius/Namespace/${namespaceIndex}`
- const roleId: NamespaceRole['id'] = `gid://sagittarius/NamespaceRole/${roleIndex}`
-
- const role = React.useMemo(() => roleService.getById(roleId, {namespaceId: namespaceId}), [roleStore, roleId, namespaceId])
-
- return <>
-
-
-
- {role?.name}
-
-
-
-
-
-
-
- General adjustments
-
-
-
-
- Permission adjustments
-
-
-
-
- Limit to projects
-
-
-
-
- Delete role forever
-
-
-
- }>
- <>
-
-
-
-
- >
-
-
- >
-}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/role/RolesView.tsx b/src/packages/ce/src/dashboard/role/RolesView.tsx
deleted file mode 100644
index d157f5f8..00000000
--- a/src/packages/ce/src/dashboard/role/RolesView.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-"use client"
-
-import React from "react";
-import {useParams, useRouter} from "next/navigation";
-import {Button, DNamespaceMemberList, DNamespaceRoleList, Flex, Spacing, Text, TextInput} from "@code0-tech/pictor";
-import {IconSearch} from "@tabler/icons-react";
-import Link from "next/link";
-
-export const RolesView: React.FC = () => {
-
- const params = useParams()
- const router = useRouter()
- const namespaceId = params.namespaceId as any as number
-
- const rolesList = React.useMemo(() => {
- return {
- const roleIndex = role.id?.match(/NamespaceRole\/(\d+)$/)?.[1]
- router.push(`/namespace/${namespaceId}/roles/${roleIndex}/settings`)
- }} namespaceId={`gid://sagittarius/Namespace/${namespaceId}`}/>
- }, [namespaceId])
-
- //TODO: user abilities for add role within namespace
-
- return <>
-
-
-
- Roles
-
-
-
- Create role
-
-
-
-
- {rolesList}
-
- >
-
-}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/runtime/RuntimesPage.tsx b/src/packages/ce/src/dashboard/runtime/RuntimesPage.tsx
deleted file mode 100644
index ce7dd952..00000000
--- a/src/packages/ce/src/dashboard/runtime/RuntimesPage.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-"use client"
-
-import React from "react";
-import {Button, DRuntimeList, Flex, Spacing, Text, useService, useStore, useUserSession} from "@code0-tech/pictor";
-import Link from "next/link";
-import {notFound, useParams, useRouter} from "next/navigation";
-import {UserService} from "@edition/user/User.service";
-
-export const RuntimesPage: React.FC = () => {
-
- const currentSession = useUserSession()
- const userStore = useStore(UserService)
- const userService = useService(UserService)
- const currentUser = React.useMemo(() => userService.getById(currentSession?.user?.id), [userStore, currentSession])
-
- const params = useParams()
- const namespaceIndex = params.namespaceId as any as number
-
- if (!namespaceIndex && currentUser && !currentUser.admin) {
- notFound()
- }
-
- const router = useRouter()
- return <>
-
-
- Runtimes
-
-
-
- Create runtime
-
-
-
-
- namespaceIndex ? true : !runtime?.namespace?.id} onSetting={(runtime) => {
- const number = runtime.id?.match(/Runtime\/(\d+)$/)?.[1]
- router.push(namespaceIndex ? `/namespace/${namespaceIndex}/runtimes/${number}/settings` : `/runtimes/${number}/settings`)
- }}/>
- >
-}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/user/UsersPage.tsx b/src/packages/ce/src/dashboard/user/UsersPage.tsx
deleted file mode 100644
index d015ff55..00000000
--- a/src/packages/ce/src/dashboard/user/UsersPage.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-"use client"
-
-import React from "react";
-import {
- Button,
- DUserList,
- Flex,
- Spacing,
- Text,
- TextInput, useService, useStore, useUserSession,
-} from "@code0-tech/pictor";
-import {IconSearch} from "@tabler/icons-react";
-import {UserService} from "@edition/user/User.service";
-import {notFound} from "next/navigation";
-export const UsersPage: React.FC = () => {
-
- const currentSession = useUserSession()
- const userStore = useStore(UserService)
- const userService = useService(UserService)
- const currentUser = React.useMemo(() => userService.getById(currentSession?.user?.id), [userStore, currentSession])
-
- if (currentUser && !currentUser.admin) {
- notFound()
- }
-
- return <>
-
-
- Users
-
-
- Invite user
-
-
-
-
- >
-}
\ No newline at end of file
diff --git a/src/packages/ce/src/datatype/Datatype.service.ts b/src/packages/ce/src/datatype/Datatype.service.ts
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/packages/ce/src/datatype/services/Datatype.service.ts b/src/packages/ce/src/datatype/services/Datatype.service.ts
new file mode 100644
index 00000000..61f546ec
--- /dev/null
+++ b/src/packages/ce/src/datatype/services/Datatype.service.ts
@@ -0,0 +1,64 @@
+import {
+ DataTypeView, DFlowDataTypeDependencies,
+ DFlowDataTypeReactiveService,
+ ReactiveArrayStore
+} from "@code0-tech/pictor";
+import {GraphqlClient} from "@core/util/graphql-client";
+import {DataType, Query} from "@code0-tech/sagittarius-graphql-types";
+import dataTypeQuery from "@edition/datatype/services/queries/DataTypes.query.graphql"
+import {View} from "@code0-tech/pictor/dist/utils/view";
+
+export class DatatypeService extends DFlowDataTypeReactiveService {
+
+ private readonly client: GraphqlClient
+ private i = 0
+
+ constructor(client: GraphqlClient, store: ReactiveArrayStore>) {
+ super(store)
+ this.client = client
+ }
+
+ values(dependencies?: DFlowDataTypeDependencies): DataTypeView[] {
+ const dataTypes = super.values()
+ if (!dependencies?.namespaceId || !dependencies.projectId || !dependencies.runtimeId) return dataTypes
+
+ const namespaceId = dependencies.namespaceId
+ const projectId = dependencies.projectId
+ const runtimeId = dependencies.runtimeId
+ const filtered = dataTypes.filter(dataType => dataType.runtime?.id === runtimeId)
+
+ if (filtered.length <= 0) {
+ this.client.query({
+ query: dataTypeQuery,
+ variables: {
+ namespaceId: namespaceId,
+ projectId: projectId,
+
+ firstDataType: 50,
+ afterDataType: null,
+
+ firstDataTypeIdentifier: 50,
+ afterDataTypeIdentifier: null,
+
+ firstRule: 50,
+ afterRule: null,
+ }
+ }).then(res => {
+ const nodes = res.data?.namespace?.project?.primaryRuntime?.dataTypes?.nodes ?? []
+ nodes.forEach(dataType => {
+ if (dataType && !this.hasById(dataType.id)) {
+ this.set(this.i++, new View(new DataTypeView(dataType)))
+ }
+ })
+ })
+ }
+
+ return filtered
+ }
+
+ hasById(id: DataType["id"]): boolean {
+ const dataType = super.values().find(d => d.id === id)
+ return dataType !== undefined
+ }
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/datatype/services/fragments/DataType.basic.fragment.graphql b/src/packages/ce/src/datatype/services/fragments/DataType.basic.fragment.graphql
new file mode 100644
index 00000000..161c9b35
--- /dev/null
+++ b/src/packages/ce/src/datatype/services/fragments/DataType.basic.fragment.graphql
@@ -0,0 +1,30 @@
+fragment BasicDataType on DataType {
+ __typename
+ id
+ createdAt
+ updatedAt
+ identifier
+ genericKeys
+ variant
+
+
+ name {
+ __typename
+ code
+ content
+ }
+ aliases {
+ __typename
+ code
+ content
+ }
+ displayMessages {
+ __typename
+ code
+ content
+ }
+
+ runtime {
+ id
+ }
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/datatype/services/fragments/DataType.fragment.graphql b/src/packages/ce/src/datatype/services/fragments/DataType.fragment.graphql
new file mode 100644
index 00000000..b2f6cc6e
--- /dev/null
+++ b/src/packages/ce/src/datatype/services/fragments/DataType.fragment.graphql
@@ -0,0 +1,58 @@
+#import "@edition/function/services/fragments/DataTypeIdentifier.fragment.graphql"
+#import "./DataTypeRule.fragment.graphql"
+
+fragment DataType on DataType {
+ __typename
+ id
+ createdAt
+ updatedAt
+ identifier
+ genericKeys
+ variant
+
+
+ name {
+ __typename
+ code
+ content
+ }
+ aliases {
+ __typename
+ code
+ content
+ }
+ displayMessages {
+ __typename
+ code
+ content
+ }
+
+ runtime {
+ id
+ }
+
+ dataTypeIdentifiers (first: $firstDataTypeIdentifier, after: $afterDataTypeIdentifier) {
+ __typename
+ count
+ nodes {
+ ...DataTypeIdentifier
+ }
+ pageInfo {
+ endCursor
+ hasNextPage
+ }
+ }
+
+ rules (first: $firstRule, after: $afterRule) {
+ __typename
+ count
+ nodes {
+ ...DataTypeRule
+ }
+ pageInfo {
+ endCursor
+ hasNextPage
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/datatype/services/fragments/DataTypeRule.fragment.graphql b/src/packages/ce/src/datatype/services/fragments/DataTypeRule.fragment.graphql
new file mode 100644
index 00000000..ccfd5ba6
--- /dev/null
+++ b/src/packages/ce/src/datatype/services/fragments/DataTypeRule.fragment.graphql
@@ -0,0 +1,48 @@
+fragment DataTypeRule on DataTypeRule {
+ __typename
+ id
+ variant
+ updatedAt
+ createdAt
+ config {
+ ... on DataTypeRulesContainsKeyConfig {
+ __typename
+ dataTypeIdentifierId
+ key
+ }
+ ... on DataTypeRulesContainsTypeConfig {
+ __typename
+ dataTypeIdentifierId
+ }
+ ... on DataTypeRulesParentTypeConfig {
+ __typename
+ dataTypeIdentifierId
+ }
+ ... on DataTypeRulesReturnTypeConfig {
+ __typename
+ dataTypeIdentifierId
+ }
+ ... on DataTypeRulesInputTypesConfig {
+ __typename
+ inputTypes {
+ __typename
+ inputIdentifier
+ dataTypeIdentifierId
+ }
+ }
+ ... on DataTypeRulesItemOfCollectionConfig {
+ __typename
+ items
+ }
+ ... on DataTypeRulesNumberRangeConfig {
+ __typename
+ from
+ steps
+ to
+ }
+ ... on DataTypeRulesRegexConfig {
+ __typename
+ pattern
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/datatype/services/queries/DataTypes.query.graphql b/src/packages/ce/src/datatype/services/queries/DataTypes.query.graphql
new file mode 100644
index 00000000..003e1255
--- /dev/null
+++ b/src/packages/ce/src/datatype/services/queries/DataTypes.query.graphql
@@ -0,0 +1,30 @@
+#import "../fragments/DataType.fragment.graphql"
+
+query DataTypes (
+ $namespaceId: NamespaceID!
+ $projectId: NamespaceProjectID!
+ $firstDataType: Int!
+ $afterDataType: String
+ $firstDataTypeIdentifier: Int!
+ $afterDataTypeIdentifier: String
+ $firstRule: Int!
+ $afterRule: String
+) {
+ namespace(id: $namespaceId) {
+ project(id: $projectId) {
+ primaryRuntime {
+ dataTypes (first: $firstDataType, after: $afterDataType) {
+ __typename
+ count
+ nodes {
+ ...DataType
+ }
+ pageInfo {
+ endCursor
+ hasNextPage
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/flow/Flow.service.ts b/src/packages/ce/src/flow/Flow.service.ts
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/packages/ce/src/flow/components/FlowCreateDialogComponent.tsx b/src/packages/ce/src/flow/components/FlowCreateDialogComponent.tsx
new file mode 100644
index 00000000..44f46e47
--- /dev/null
+++ b/src/packages/ce/src/flow/components/FlowCreateDialogComponent.tsx
@@ -0,0 +1,226 @@
+import React, {startTransition} from "react";
+import {FlowType, NamespaceProject} from "@code0-tech/sagittarius-graphql-types";
+import {IconArrowDown, IconArrowUp, IconCornerDownLeft, IconFile} from "@tabler/icons-react";
+import {
+ Badge,
+ Button,
+ Card,
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogOverlay,
+ DialogPortal,
+ Flex,
+ InputDescription,
+ InputLabel,
+ InputMessage,
+ Menu,
+ MenuContent,
+ MenuItem,
+ MenuLabel,
+ MenuPortal,
+ MenuTrigger,
+ Spacing,
+ Text,
+ toast,
+ useForm,
+ useService,
+ useStore
+} from "@code0-tech/pictor";
+import {hashToColor} from "@code0-tech/pictor/dist/components/d-flow/DFlow.util";
+import {FlowTypeService} from "@edition/flowtype/services/FlowTypeService";
+import {FlowNameInputComponent} from "@edition/flow/components/FlowNameInputComponent";
+import {ButtonGroup} from "@code0-tech/pictor/dist/components/button-group/ButtonGroup";
+import {useParams, useRouter} from "next/navigation";
+import {ProjectService} from "@edition/project/services/Project.service";
+import {RuntimeService} from "@edition/runtime/services/Runtime.service";
+import {FlowService} from "@edition/flow/services/Flow.service";
+
+export interface FlowCreateDialogComponentProps {
+ open?: boolean
+ onOpenChange?: (open: boolean) => void
+ flowTypeId?: FlowType['id']
+}
+
+export const FlowCreateDialogComponent: React.FC = (props) => {
+
+ const {open, onOpenChange, flowTypeId} = props
+
+ const params = useParams()
+ const router = useRouter()
+ const flowService = useService(FlowService)
+ const flowTypeService = useService(FlowTypeService)
+ const flowTypeStore = useStore(FlowTypeService)
+ const projectService = useService(ProjectService)
+ const projectStore = useStore(ProjectService)
+ const runtimeService = useService(RuntimeService)
+ const runtimeStore = useStore(RuntimeService)
+
+ const projectIndex = params.projectId as any as number
+ const projectId: NamespaceProject['id'] = `gid://sagittarius/NamespaceProject/${projectIndex}`
+
+ const [createDialogOpen, setCreateDialogOpen] = React.useState(open)
+ const [selectedFlowTypeId, setSelectedFlowTypeId] = React.useState(flowTypeId)
+
+ const selectedFlowType = React.useMemo(() => {
+ return flowTypeService.getById(selectedFlowTypeId)
+ }, [selectedFlowTypeId, flowTypeStore])
+
+ const initialValues = React.useMemo(
+ () => ({name: null, flowTypeId: flowTypeId ?? null}),
+ []
+ )
+
+ const project = React.useMemo(
+ () => projectService.getById(projectId),
+ [projectStore, projectId]
+ )
+
+ const primaryRuntime = React.useMemo(
+ () => runtimeService.values({namespaceId: project?.namespace?.id}).find(runtime => runtime.id == project?.primaryRuntime?.id),
+ [project, runtimeStore]
+ )
+
+ const flowTypes = React.useMemo(
+ () => flowTypeService.values({runtimeId: primaryRuntime?.id, projectId: projectId, namespaceId: project?.namespace?.id}),
+ [flowTypeStore]
+ )
+
+ const createFlow = React.useCallback((name: string, type: FlowType['id']) => {
+ if (!type) return
+ startTransition(() => {
+ flowService.flowCreate({
+ // @ts-ignore
+ flow: {
+ settings: [],
+ name: name,
+ type: type,
+ nodes: [],
+ },
+ projectId: projectId
+ }).then(payload => {
+ if ((payload?.errors?.length ?? 0) <= 0) {
+ toast({
+ title: "The flow was successfully created.",
+ color: "success",
+ dismissible: true,
+ })
+
+ }
+ })
+ })
+ }, [])
+
+ React.useEffect(() => {
+ setCreateDialogOpen(open)
+ }, [open])
+
+ const [inputs, validate] = useForm({
+ initialValues: initialValues,
+ validate: {
+ name: (value) => {
+ if (!value) return "Name is required"
+ return null
+ },
+ flowTypeId: (value) => {
+ if (!value) return "Flow type is required"
+ return null
+ }
+ },
+ onSubmit: (values) => {
+ createFlow?.(values.name!, values.flowTypeId)
+ onOpenChange?.(false)
+ }
+ })
+
+ return onOpenChange?.(open)}>
+
+
+
+
+ {!primaryRuntime ? <>
+
+ You don't have a primary runtime yet, assign and/or create a runtime to be able to create flows.
+
+
+
+
+ No, go back!
+
+
+
+ Assign
+
+
+ Create
+
+
+
+ > : (
+ <>
+ Type of flow
+ You can choose a flow type here
+
+
+
+ {selectedFlowTypeId ?
+ : null}
+ {selectedFlowType ? selectedFlowType?.names?.[0].content : "Select flow type"}
+
+
+
+
+
+ {flowTypes.map((flowType) => (
+ {
+ inputs.getInputProps("flowTypeId").formValidation?.setValue(flowType.id)
+ setSelectedFlowTypeId(flowType.id)
+ }}>
+ {flowType.names?.[0].content}
+
+ ))}
+
+
+
+
+
+
+
+
+ move
+
+
+
+
+ select
+
+
+
+
+
+
+ {!inputs.getInputProps("flowTypeId").formValidation?.valid ?
+ {inputs.getInputProps("flowTypeId").formValidation?.notValidMessage!} : null}
+
+
+
+
+
+ No, go back!
+
+ Yes, create!
+
+ >
+ )}
+
+
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/flow/components/FlowNameInputComponent.tsx b/src/packages/ce/src/flow/components/FlowNameInputComponent.tsx
new file mode 100644
index 00000000..6aff18fc
--- /dev/null
+++ b/src/packages/ce/src/flow/components/FlowNameInputComponent.tsx
@@ -0,0 +1,47 @@
+import React from "react";
+import {TextInputProps, InputSuggestion, InputSyntaxSegment, TextInput, Badge} from "@code0-tech/pictor"
+
+export interface FlowNameInputComponentProps extends TextInputProps {
+
+}
+
+export const FlowNameInputComponent: React.FC = (props) => {
+
+ const {...rest} = props
+
+ const transformSyntax = (
+ value?: string | null,
+ _: (InputSuggestion | any)[] = [],
+ ): InputSyntaxSegment[] => {
+
+ const normalizePath = (s: string) => {
+ const r = [], b = ""
+ let buf = b
+ s.split("").forEach(c =>
+ c === "/"
+ ? (buf && r.push(buf), buf = "", r.push("/"))
+ : buf += c
+ )
+ buf && r.push(buf)
+ return r
+ };
+ const splitValue = normalizePath(value ?? "")
+ let cursor = 0
+
+ return splitValue.map((value, index) => {
+ const segment = {
+ type: splitValue.length - 1 !== index ? "block" : "text",
+ value: value,
+ start: cursor,
+ end: cursor + value.length,
+ visualLength: splitValue.length - 1 !== index ? 1 : value.length,
+ content: splitValue.length - 1 !== index ? {value} : value,
+ }
+ cursor += value.length
+ return [segment]
+ }).flat() as InputSyntaxSegment[]
+
+ }
+
+ return
+}
diff --git a/src/packages/ce/src/flow/services/Flow.service.ts b/src/packages/ce/src/flow/services/Flow.service.ts
new file mode 100644
index 00000000..f7403d0a
--- /dev/null
+++ b/src/packages/ce/src/flow/services/Flow.service.ts
@@ -0,0 +1,190 @@
+import {DFlowDependencies, DFlowReactiveService, ReactiveArrayStore} from "@code0-tech/pictor";
+import {
+ Flow, LiteralValue,
+ Mutation,
+ NamespacesProjectsFlowsCreateInput,
+ NamespacesProjectsFlowsCreatePayload,
+ NamespacesProjectsFlowsDeleteInput,
+ NamespacesProjectsFlowsDeletePayload,
+ NamespacesProjectsFlowsUpdateInput,
+ NamespacesProjectsFlowsUpdatePayload,
+ NodeFunction, NodeParameter,
+ Query, ReferenceValue
+} from "@code0-tech/sagittarius-graphql-types";
+import {GraphqlClient} from "@core/util/graphql-client";
+import flowsQuery from "@edition/flow/services/queries/Flows.query.graphql";
+import flowCreateMutation from "@edition/flow/services/mutations/Flow.create.mutation.graphql";
+import flowDeleteMutation from "@edition/flow/services/mutations/Flow.delete.mutation.graphql";
+import flowUpdateMutation from "@edition/flow/services/mutations/Flow.update.mutation.graphql";
+import {View} from "@code0-tech/pictor/dist/utils/view";
+
+
+export class FlowService extends DFlowReactiveService {
+
+ private readonly client: GraphqlClient
+ private flowUpdateQueue: Array
+ private i
+
+ constructor(client: GraphqlClient, store: ReactiveArrayStore>) {
+ super(store)
+ this.client = client
+ this.flowUpdateQueue = []
+ this.i = 0
+ }
+
+ values(dependencies?: DFlowDependencies): Flow[] {
+ const flows = super.values()
+ if (!dependencies?.namespaceId || !dependencies.projectId) return flows
+
+ const namespaceId = dependencies.namespaceId
+ const projectId = dependencies.projectId
+ const filtered = flows.filter(flow => flow.project?.id === projectId)
+
+ if (filtered.length <= 0) {
+ this.client.query({
+ query: flowsQuery,
+ variables: {
+ namespaceId: namespaceId,
+ projectId: projectId,
+
+ firstFlow: 50,
+ afterFlow: null,
+
+ firstNode: 50,
+ afterNode: null,
+
+ firstNodeParameter: 50,
+ afterNodeParameter: null,
+
+ firstSetting: 50,
+ afterSetting: null,
+
+ // firstInputDataTypeIdentifier: 50,
+ // afterInputDataTypeIdentifier: null,
+ // firstInputRule: 50,
+ // afterInputRule: null,
+ //
+ // firstReturnDataTypeIdentifier: 50,
+ // afterReturnDataTypeIdentifier: null,
+ // firstReturnRule: 50,
+ // afterReturnRule: null
+ }
+ }).then(res => {
+ const nodes = res.data?.namespace?.project?.flows?.nodes ?? []
+ nodes.forEach(flow => {
+ if (flow && !this.hasById(flow.id)) {
+ this.set(this.i++, new View(flow))
+ }
+ })
+ })
+ }
+
+ return filtered
+ }
+
+ hasById(id: Flow["id"]): boolean {
+ const flow = super.values().find(f => f.id === id)
+ return flow !== undefined
+ }
+
+
+ async addNextNodeById(flowId: Flow["id"], parentNodeId: NodeFunction["id"] | null, nextNode: NodeFunction): Promise {
+ await super.addNextNodeById(flowId, parentNodeId, nextNode)
+ await this.syncFlow(flowId)
+ }
+
+
+ async deleteNodeById(flowId: Flow["id"], nodeId: NodeFunction["id"]): Promise {
+ await super.deleteNodeById(flowId, nodeId)
+ await this.syncFlow(flowId)
+ }
+
+
+ async setParameterValue(flowId: Flow["id"], nodeId: NodeFunction["id"], parameterId: NodeParameter["id"], value?: LiteralValue | ReferenceValue | NodeFunction): Promise {
+ await super.setParameterValue(flowId, nodeId, parameterId, value)
+ await this.syncFlow(flowId)
+ }
+
+ private async syncFlow(flowId: Flow["id"]) {
+
+ const alreadyQueued = this.flowUpdateQueue.includes(flowId)
+ if (alreadyQueued) return Promise.resolve()
+
+ this.flowUpdateQueue.push(flowId)
+
+ setTimeout(async () => {
+ const flow = this.values().find(f => f.id === flowId)
+ const flowInput = this.getPayloadById(flowId)
+
+ if (!flow || !flowInput || !flowId) return Promise.reject()
+
+ await this.flowUpdate({
+ flowId: flowId,
+ flowInput: flowInput
+ })
+
+ this.flowUpdateQueue.splice(this.flowUpdateQueue.indexOf(flowId), 1)
+ }, 1000*60) // 1 min
+ }
+
+ async flowCreate(payload: NamespacesProjectsFlowsCreateInput): Promise {
+ const result = await this.client.mutate({
+ mutation: flowCreateMutation,
+ variables: {
+ ...payload
+ }
+ })
+
+ if (result.data && result.data.namespacesProjectsFlowsCreate && result.data.namespacesProjectsFlowsCreate.flow) {
+ const flow = result.data.namespacesProjectsFlowsCreate.flow
+ if (!this.hasById(flow.id)) {
+ flow.nodes = {nodes: []} //TODO: to avoid issues, when fixed in pictor
+ this.add(new View(flow))
+ }
+ }
+
+ return result.data?.namespacesProjectsFlowsCreate ?? undefined
+ }
+
+ async flowDelete(payload: NamespacesProjectsFlowsDeleteInput): Promise {
+ const result = await this.client.mutate({
+ mutation: flowDeleteMutation,
+ variables: {
+ ...payload
+ }
+ })
+
+ if (result.data && result.data.namespacesProjectsFlowsDelete && result.data.namespacesProjectsFlowsDelete.flow) {
+ const flow = result.data.namespacesProjectsFlowsDelete.flow
+ const index = this.values().findIndex(f => f.id === flow.id)
+ this.delete(index)
+
+ }
+
+ return result.data?.namespacesProjectsFlowsDelete ?? undefined
+ }
+
+ async flowUpdate(payload: NamespacesProjectsFlowsUpdateInput): Promise {
+
+ const flow = this.getById(payload.flowId)
+
+ if (!flow) return Promise.reject()
+
+ const result = await this.client.mutate({
+ mutation: flowUpdateMutation,
+ variables: {
+ ...payload
+ }
+ })
+
+ if (result.data && result.data.namespacesProjectsFlowsUpdate && result.data.namespacesProjectsFlowsUpdate.flow) {
+ const flowIndex = this.values().findIndex(f => f.id === payload.flowId)
+ flow.updatedAt = result.data.namespacesProjectsFlowsUpdate.flow.updatedAt
+ flow.editedAt = undefined
+ this.set(flowIndex, new View(flow))
+ }
+
+ return result.data?.namespacesProjectsFlowsUpdate ?? undefined
+ }
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/flow/services/fragments/Flow.basic.fragment.graphql b/src/packages/ce/src/flow/services/fragments/Flow.basic.fragment.graphql
new file mode 100644
index 00000000..3825a5a7
--- /dev/null
+++ b/src/packages/ce/src/flow/services/fragments/Flow.basic.fragment.graphql
@@ -0,0 +1,28 @@
+fragment BasicFlow on Flow {
+ __typename
+ id
+ createdAt
+ inputType {
+ id
+ }
+ name
+ project {
+ __typename
+ id
+ }
+ returnType {
+ __typename
+ id
+ }
+ settings {
+ __typename
+ }
+ startingNodeId
+ type {
+ id
+ }
+ updatedAt
+ userAbilities {
+ deleteFlow
+ }
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/flow/services/fragments/Flow.fragment.graphql b/src/packages/ce/src/flow/services/fragments/Flow.fragment.graphql
new file mode 100644
index 00000000..4ee62d16
--- /dev/null
+++ b/src/packages/ce/src/flow/services/fragments/Flow.fragment.graphql
@@ -0,0 +1,96 @@
+fragment Flow on Flow {
+ __typename
+ id
+ createdAt
+ name
+ nodes (first: $firstNode, after: $afterNode) {
+ __typename
+ nodes {
+ __typename
+ id
+ nextNodeId
+ createdAt
+ updatedAt
+ parameters (first: $firstNodeParameter, after: $afterNodeParameter) {
+ __typename
+ count
+ nodes {
+ __typename
+ id
+ updatedAt
+ createdAt
+ parameterDefinition {
+ __typename
+ id
+ identifier
+ createdAt
+ updatedAt
+ }
+ value {
+ __typename
+ ... on LiteralValue {
+ __typename
+ value
+ }
+ ... on NodeFunctionIdWrapper {
+ __typename
+ id
+ }
+ ... on ReferenceValue {
+ __typename
+ id
+ updatedAt
+ createdAt
+ nodeFunctionId
+ referencePath {
+ __typename
+ id
+ createdAt
+ updatedAt
+ arrayIndex
+ path
+ }
+ }
+ }
+ }
+ pageInfo {
+ endCursor
+ hasNextPage
+ }
+ }
+ functionDefinition {
+ __typename
+ id
+ identifier
+ }
+ }
+ }
+ project {
+ __typename
+ id
+ }
+ settings (first: $firstSetting, after: $afterSetting) {
+ __typename
+ count
+ nodes {
+ __typename
+ id
+ createdAt
+ updatedAt
+ flowSettingIdentifier
+ value
+ }
+ pageInfo {
+ endCursor
+ hasNextPage
+ }
+ }
+ startingNodeId
+ type {
+ id
+ }
+ updatedAt
+ userAbilities {
+ deleteFlow
+ }
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/flow/services/mutations/Flow.create.mutation.graphql b/src/packages/ce/src/flow/services/mutations/Flow.create.mutation.graphql
new file mode 100644
index 00000000..80230877
--- /dev/null
+++ b/src/packages/ce/src/flow/services/mutations/Flow.create.mutation.graphql
@@ -0,0 +1,20 @@
+#import "../fragments/Flow.basic.fragment.graphql"
+
+mutation flowCreate($flow: FlowInput!, $projectId: NamespaceProjectID!) {
+ namespacesProjectsFlowsCreate(input: {
+ flow: $flow,
+ projectId: $projectId
+ }) {
+ errors {
+ ...on Error {
+ errorCode,
+ details {
+ __typename
+ }
+ }
+ }
+ flow {
+ ...BasicFlow
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/flow/services/mutations/Flow.delete.mutation.graphql b/src/packages/ce/src/flow/services/mutations/Flow.delete.mutation.graphql
new file mode 100644
index 00000000..4e35e339
--- /dev/null
+++ b/src/packages/ce/src/flow/services/mutations/Flow.delete.mutation.graphql
@@ -0,0 +1,19 @@
+#import "../fragments/Flow.basic.fragment.graphql"
+
+mutation flowDelete($flowId: FlowID!) {
+ namespacesProjectsFlowsDelete(input: {
+ flowId: $flowId
+ }) {
+ errors {
+ ...on Error {
+ errorCode,
+ details {
+ __typename
+ }
+ }
+ }
+ flow {
+ ...BasicFlow
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/flow/services/mutations/Flow.update.mutation.graphql b/src/packages/ce/src/flow/services/mutations/Flow.update.mutation.graphql
new file mode 100644
index 00000000..c5a86084
--- /dev/null
+++ b/src/packages/ce/src/flow/services/mutations/Flow.update.mutation.graphql
@@ -0,0 +1,21 @@
+#import "../fragments/Flow.basic.fragment.graphql"
+
+mutation flowUpdate($flowId: FlowID!, $flowInput: FlowInput!) {
+ namespacesProjectsFlowsUpdate(input: {
+ flowId: $flowId
+ flowInput: $flowInput
+ }) {
+ errors {
+ ...on Error {
+ errorCode,
+ details {
+ ...on ActiveModelError { attribute type }
+ __typename
+ }
+ }
+ }
+ flow {
+ ...BasicFlow
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/flow/services/queries/Flows.query.graphql b/src/packages/ce/src/flow/services/queries/Flows.query.graphql
new file mode 100644
index 00000000..a86754f9
--- /dev/null
+++ b/src/packages/ce/src/flow/services/queries/Flows.query.graphql
@@ -0,0 +1,40 @@
+#import "../fragments/Flow.fragment.graphql"
+
+query Flows (
+ $namespaceId: NamespaceID!
+ $projectId: NamespaceProjectID!
+ $firstFlow: Int!
+ $afterFlow: String
+ $firstNode: Int!,
+ $afterNode: String,
+ $firstNodeParameter: Int!,
+ $afterNodeParameter: String,
+
+ $firstSetting: Int!,
+ $afterSetting: String,
+
+ #$firstInputDataTypeIdentifier: Int!
+ #$afterInputDataTypeIdentifier: String
+ #$firstInputRule: Int!
+ #$afterInputRule: String
+
+ #$firstReturnDataTypeIdentifier: Int!
+ #$afterReturnDataTypeIdentifier: String
+ #$firstReturnRule: Int!
+ #$afterReturnRule: String
+) {
+ namespace(id: $namespaceId) {
+ project(id: $projectId) {
+ flows (first: $firstFlow, after: $afterFlow) {
+ count
+ nodes {
+ ...Flow
+ }
+ pageInfo {
+ endCursor
+ hasNextPage
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/flow/views/FlowFolderView.tsx b/src/packages/ce/src/flow/views/FlowFolderView.tsx
new file mode 100644
index 00000000..47f6638d
--- /dev/null
+++ b/src/packages/ce/src/flow/views/FlowFolderView.tsx
@@ -0,0 +1,173 @@
+"use client"
+
+import React from "react";
+import {
+ Button,
+ DFlowFolder,
+ DFlowFolderDeleteDialog,
+ DFlowFolderHandle,
+ DLayout,
+ Flex,
+ Text,
+ toast,
+ Tooltip,
+ TooltipArrow,
+ TooltipContent,
+ TooltipPortal,
+ TooltipTrigger,
+ useService
+} from "@code0-tech/pictor";
+import {IconArrowsMaximize, IconArrowsMinimize, IconCircleDot, IconLayoutSidebar, IconPlus} from "@tabler/icons-react";
+import {useParams, useRouter} from "next/navigation";
+import {Flow, FlowType} from "@code0-tech/sagittarius-graphql-types";
+import {FlowService} from "@edition/flow/services/Flow.service";
+import {
+ DFlowFolderContextMenuGroupData,
+ DFlowFolderContextMenuItemData
+} from "@code0-tech/pictor/dist/components/d-flow-folder/DFlowFolderContextMenu";
+import {ButtonGroup} from "@code0-tech/pictor/dist/components/button-group/ButtonGroup";
+import {FlowCreateDialogComponent} from "@edition/flow/components/FlowCreateDialogComponent";
+
+export const FlowFolderView: React.FC = () => {
+
+ const router = useRouter()
+ const params = useParams()
+ const flowService = useService(FlowService)
+
+ const namespaceIndex = params.namespaceId as any as number
+ const projectIndex = params.projectId as any as number
+ const flowIndex = params.flowId as any as number
+ const flowId: Flow['id'] = `gid://sagittarius/Flow/${flowIndex}`
+
+ const [, startTransition] = React.useTransition()
+ const ref = React.useRef(null)
+
+ const [createDialogOpen, setCreateDialogOpen] = React.useState(false)
+ const [flowTypeId, setFlowTypeId] = React.useState(undefined)
+
+ const [deleteDialogOpen, setDeleteDialogOpen] = React.useState(false)
+ const [contextData, setContextData] = React.useState({
+ flow: [],
+ name: "",
+ type: "folder"
+ })
+
+ const deleteFlow = React.useCallback((flow: Flow) => {
+ if (!flow?.id) return
+ startTransition(() => {
+ flowService.flowDelete({
+ flowId: flow.id!
+ }).then(payload => {
+ if ((payload?.errors?.length ?? 0) <= 0) {
+ toast({
+ title: "The flow was successfully deleted.",
+ color: "success",
+ dismissible: true,
+ })
+ router.push(`/namespace/${namespaceIndex}/project/${projectIndex}/`)
+ }
+ })
+ })
+ }, [])
+
+ return <>
+
+ setCreateDialogOpen(open)}
+ flowTypeId={flowTypeId}/>
+
+ setDeleteDialogOpen(open)}
+ contextData={contextData}
+ onDelete={deleteFlow}/>
+
+
+
+ Explorer
+
+
+
+
+
+
+
+ {
+ setCreateDialogOpen(true)
+ setFlowTypeId(undefined)
+ }}>
+
+
+
+
+
+ Add new flow
+
+
+
+
+
+
+
+ ref.current?.openActivePath()}>
+
+
+
+
+
+ Open active flow
+
+
+
+
+
+
+ ref.current?.closeAll()}>
+
+
+
+
+
+ Close all
+
+
+
+
+
+
+ ref.current?.openAll()}>
+
+
+
+
+
+ Open all
+
+
+
+
+
+
+
+ }>
+ {
+ const number = flow.id?.match(/Flow\/(\d+)$/)?.[1]
+ router.push(`/namespace/${namespaceIndex}/project/${projectIndex}/flow/${number}`)
+ }}
+ onCreate={flowTypeId => {
+ setCreateDialogOpen(true)
+ setFlowTypeId(flowTypeId)
+ }}
+ onDelete={contextData => {
+ setDeleteDialogOpen(true)
+ setContextData(contextData)
+ }}
+ namespaceId={`gid://sagittarius/Namespace/${namespaceIndex}`}
+ projectId={`gid://sagittarius/NamespaceProject/${projectIndex}`}/>
+
+ >
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/flowtype/Flowtype.service.ts b/src/packages/ce/src/flowtype/Flowtype.service.ts
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/packages/ce/src/flowtype/services/FlowTypeService.ts b/src/packages/ce/src/flowtype/services/FlowTypeService.ts
new file mode 100644
index 00000000..15824221
--- /dev/null
+++ b/src/packages/ce/src/flowtype/services/FlowTypeService.ts
@@ -0,0 +1,64 @@
+import {DFlowTypeDependencies, DFlowTypeReactiveService, FlowTypeView, ReactiveArrayStore} from "@code0-tech/pictor";
+import {GraphqlClient} from "@core/util/graphql-client";
+import {FlowType, Query} from "@code0-tech/sagittarius-graphql-types";
+import flowTypesQuery from "@edition/flowtype/services/queries/FlowTypes.query.graphql"
+import {View} from "@code0-tech/pictor/dist/utils/view";
+
+export class FlowTypeService extends DFlowTypeReactiveService {
+
+ private readonly client: GraphqlClient
+ private i = 0
+
+ constructor(client: GraphqlClient, store: ReactiveArrayStore>) {
+ super(store)
+ this.client = client
+ }
+
+ values(dependencies?: DFlowTypeDependencies): FlowTypeView[] {
+ const functions = super.values()
+ if (!dependencies?.runtimeId) return functions
+
+ const namespaceId = dependencies.namespaceId
+ const projectId = dependencies.projectId
+ const runtimeId = dependencies.runtimeId
+ const filtered = functions.filter(flowType => flowType.runtime?.id === runtimeId)
+
+ if (filtered.length <= 0 && namespaceId && projectId) {
+ this.client.query({
+ query: flowTypesQuery,
+ variables: {
+ namespaceId: namespaceId,
+ projectId: projectId,
+
+ firstFlowType: 50,
+ afterFlowType: null,
+
+ firstInputDataTypeIdentifier: 50,
+ afterInputDataTypeIdentifier: null,
+ firstInputRule: 50,
+ afterInputRule: null,
+
+ firstReturnDataTypeIdentifier: 50,
+ afterReturnDataTypeIdentifier: null,
+ firstReturnRule: 50,
+ afterReturnRule: null
+ }
+ }).then(res => {
+ const nodes = res.data?.namespace?.project?.primaryRuntime?.flowTypes?.nodes ?? []
+ nodes.forEach(flowType => {
+ if (flowType && !this.hasById(flowType.id)) {
+ this.set(this.i++, new View(new FlowTypeView(flowType)))
+ }
+ })
+ })
+ }
+
+ return filtered
+ }
+
+ hasById(id: FlowType["id"]): boolean {
+ const flowType = super.values().find(f => f.id === id)
+ return flowType !== undefined
+ }
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/flowtype/services/fragments/FlowType.fragment.graphql b/src/packages/ce/src/flowtype/services/fragments/FlowType.fragment.graphql
new file mode 100644
index 00000000..b22d4754
--- /dev/null
+++ b/src/packages/ce/src/flowtype/services/fragments/FlowType.fragment.graphql
@@ -0,0 +1,112 @@
+#import "@edition/function/services/fragments/DataTypeIdentifier.fragment.graphql"
+#import "@edition/datatype/services/fragments/DataType.basic.fragment.graphql"
+#import "@edition/datatype/services/fragments/DataTypeRule.fragment.graphql"
+
+fragment FlowType on FlowType {
+ __typename
+ id
+ updatedAt
+ createdAt
+ identifier
+ names {
+ code
+ content
+ __typename
+ }
+ aliases {
+ code
+ content
+ __typename
+ }
+ displayMessages {
+ code
+ content
+ __typename
+ }
+ descriptions {
+ code
+ content
+ __typename
+ }
+ runtime {
+ id
+ }
+ inputType {
+ ...BasicDataType
+ dataTypeIdentifiers (first: $firstInputDataTypeIdentifier, after: $afterInputDataTypeIdentifier) {
+ __typename
+ count
+ nodes {
+ ...DataTypeIdentifier
+ }
+ pageInfo {
+ endCursor
+ hasNextPage
+ }
+ }
+ rules (first: $firstInputRule, after: $afterInputRule) {
+ __typename
+ count
+ nodes {
+ ...DataTypeRule
+ }
+ pageInfo {
+ endCursor
+ hasNextPage
+ }
+ }
+
+ }
+ returnType {
+ ...BasicDataType
+ dataTypeIdentifiers (first: $firstReturnDataTypeIdentifier, after: $afterReturnDataTypeIdentifier) {
+ __typename
+ count
+ nodes {
+ ...DataTypeIdentifier
+ }
+ pageInfo {
+ endCursor
+ hasNextPage
+ }
+ }
+ rules (first: $firstReturnRule, after: $afterReturnRule) {
+ __typename
+ count
+ nodes {
+ ...DataTypeRule
+ }
+ pageInfo {
+ endCursor
+ hasNextPage
+ }
+ }
+ }
+ flowTypeSettings {
+ __typename
+ id
+ createdAt
+ updatedAt
+ identifier
+ unique
+
+
+ dataType {
+ id
+ }
+
+ names {
+ __typename
+ code
+ content
+ }
+ descriptions {
+ __typename
+ code
+ content
+ }
+
+ }
+ editable
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/flowtype/services/queries/FlowTypes.query.graphql b/src/packages/ce/src/flowtype/services/queries/FlowTypes.query.graphql
new file mode 100644
index 00000000..ef27cea5
--- /dev/null
+++ b/src/packages/ce/src/flowtype/services/queries/FlowTypes.query.graphql
@@ -0,0 +1,35 @@
+#import "../fragments/FlowType.fragment.graphql"
+
+query FlowTypes (
+ $namespaceId: NamespaceID!
+ $projectId: NamespaceProjectID!
+ $firstFlowType: Int!
+ $afterFlowType: String
+
+ $firstInputDataTypeIdentifier: Int!
+ $afterInputDataTypeIdentifier: String
+ $firstInputRule: Int!
+ $afterInputRule: String
+
+ $firstReturnDataTypeIdentifier: Int!
+ $afterReturnDataTypeIdentifier: String
+ $firstReturnRule: Int!
+ $afterReturnRule: String
+) {
+ namespace(id: $namespaceId) {
+ project(id: $projectId) {
+ primaryRuntime {
+ flowTypes (first: $firstFlowType, after: $afterFlowType) {
+ count
+ nodes {
+ ...FlowType
+ }
+ pageInfo {
+ endCursor
+ hasNextPage
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/function/Function.service.ts b/src/packages/ce/src/function/Function.service.ts
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/packages/ce/src/function/services/Function.service.ts b/src/packages/ce/src/function/services/Function.service.ts
new file mode 100644
index 00000000..c88beb79
--- /dev/null
+++ b/src/packages/ce/src/function/services/Function.service.ts
@@ -0,0 +1,65 @@
+import {
+ DFlowFunctionDependencies,
+ DFlowFunctionReactiveService,
+ FunctionDefinitionView,
+ ReactiveArrayStore
+} from "@code0-tech/pictor";
+import {GraphqlClient} from "@core/util/graphql-client";
+import {FunctionDefinition, Query} from "@code0-tech/sagittarius-graphql-types";
+import functionsQuery from "@edition/function/services/queries/Functions.query.graphql";
+import {View} from "@code0-tech/pictor/dist/utils/view";
+
+export class FunctionService extends DFlowFunctionReactiveService {
+
+ private readonly client: GraphqlClient
+ private i = 0
+
+ constructor(client: GraphqlClient, store: ReactiveArrayStore>) {
+ super(store)
+ this.client = client
+ }
+
+ values(dependencies?: DFlowFunctionDependencies): FunctionDefinitionView[] {
+ const functions = super.values()
+ if (!dependencies?.namespaceId || !dependencies.projectId || !dependencies.runtimeId) return functions
+
+ const namespaceId = dependencies.namespaceId
+ const projectId = dependencies.projectId
+ const runtimeId = dependencies.runtimeId
+ const filtered = functions.filter(functionD => functionD.runtimeFunctionDefinition?.runtime?.id === runtimeId)
+
+ if (filtered.length <= 0) {
+ this.client.query({
+ query: functionsQuery,
+ variables: {
+ namespaceId: namespaceId,
+ projectId: projectId,
+
+ firstFunction: 50,
+ afterFunction: null,
+
+ firstDataTypeIdentifier: 50,
+ afterDataTypeIdentifier: null,
+
+ firstParameterDefinition: 50,
+ afterParameterDefinition: null,
+ }
+ }).then(res => {
+ const nodes = res.data?.namespace?.project?.primaryRuntime?.functionDefinitions?.nodes ?? []
+ nodes.forEach(functionD => {
+ if (functionD && !this.hasById(functionD.id)) {
+ this.set(this.i++, new View(new FunctionDefinitionView(functionD)))
+ }
+ })
+ })
+ }
+
+ return filtered
+ }
+
+ hasById(id: FunctionDefinition["id"]): boolean {
+ const functionD = super.values().find(f => f.id === id)
+ return functionD !== undefined
+ }
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/function/services/fragments/DataTypeIdentifier.fragment.graphql b/src/packages/ce/src/function/services/fragments/DataTypeIdentifier.fragment.graphql
new file mode 100644
index 00000000..22e34b04
--- /dev/null
+++ b/src/packages/ce/src/function/services/fragments/DataTypeIdentifier.fragment.graphql
@@ -0,0 +1,36 @@
+fragment DataTypeIdentifier on DataTypeIdentifier {
+ id
+ createdAt
+ updatedAt
+ genericKey
+ genericType {
+ id
+ updatedAt
+ createdAt
+ genericMappers {
+ id
+ target
+ sourceDataTypeIdentifierIds
+ updatedAt
+ createdAt
+ genericCombinationStrategies {
+ id
+ type
+ createdAt
+ updatedAt
+ __typename
+ }
+ __typename
+ }
+ dataType {
+ identifier
+ id
+ __typename
+ }
+ }
+ dataType {
+ id
+ identifier
+ }
+ __typename
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/function/services/fragments/Function.fragment.graphql b/src/packages/ce/src/function/services/fragments/Function.fragment.graphql
new file mode 100644
index 00000000..47e49bd4
--- /dev/null
+++ b/src/packages/ce/src/function/services/fragments/Function.fragment.graphql
@@ -0,0 +1,126 @@
+fragment DataTypeIdentifier on DataTypeIdentifier {
+ id
+ createdAt
+ updatedAt
+ genericKey
+ genericType {
+ id
+ updatedAt
+ createdAt
+ genericMappers {
+ id
+ target
+ sourceDataTypeIdentifierIds
+ updatedAt
+ createdAt
+ genericCombinationStrategies {
+ id
+ type
+ createdAt
+ updatedAt
+ __typename
+ }
+ __typename
+ }
+ dataType {
+ identifier
+ id
+ __typename
+ }
+ }
+ dataType {
+ id
+ identifier
+ }
+ __typename
+}
+
+fragment Function on FunctionDefinition {
+ id
+ createdAt
+ updatedAt
+ dataTypeIdentifiers (first: $firstDataTypeIdentifier, after: $afterDataTypeIdentifier) {
+ count
+ nodes {
+ ...DataTypeIdentifier
+ }
+ pageInfo {
+ endCursor
+ hasNextPage
+ }
+ __typename
+ }
+ aliases {
+ code
+ content
+ __typename
+ }
+ deprecationMessages {
+ code
+ content
+ __typename
+ }
+ descriptions {
+ code
+ content
+ __typename
+ }
+ documentations {
+ code
+ content
+ __typename
+ }
+ genericKeys
+ displayMessages {
+ code
+ content
+ __typename
+ }
+ identifier
+ names {
+ code
+ content
+ __typename
+ }
+ returnType {
+ ...DataTypeIdentifier
+ }
+ parameterDefinitions (first: $firstParameterDefinition, after: $afterParameterDefinition) {
+ nodes {
+ id
+ identifier
+ updatedAt
+ createdAt
+ identifier
+ names {
+ code
+ content
+ __typename
+ }
+ descriptions {
+ code
+ content
+ __typename
+ }
+ documentations {
+ code
+ content
+ __typename
+ }
+ dataTypeIdentifier {
+ ...DataTypeIdentifier
+ }
+ __typename
+ }
+ }
+ #throwsError
+ runtimeFunctionDefinition {
+ id
+ identifier
+ runtime {
+ id
+ }
+ __typename
+ }
+ __typename
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/function/services/queries/Functions.query.graphql b/src/packages/ce/src/function/services/queries/Functions.query.graphql
new file mode 100644
index 00000000..55301aeb
--- /dev/null
+++ b/src/packages/ce/src/function/services/queries/Functions.query.graphql
@@ -0,0 +1,29 @@
+#import "../fragments/Function.fragment.graphql"
+
+query Functions (
+ $namespaceId: NamespaceID!
+ $projectId: NamespaceProjectID!
+ $firstFunction: Int!
+ $afterFunction: String
+ $firstDataTypeIdentifier: Int!
+ $afterDataTypeIdentifier: String
+ $firstParameterDefinition: Int!
+ $afterParameterDefinition: String
+) {
+ namespace(id: $namespaceId) {
+ project(id: $projectId) {
+ primaryRuntime {
+ functionDefinitions (first: $firstFunction, after: $afterFunction) {
+ count
+ nodes {
+ ...Function
+ }
+ pageInfo {
+ endCursor
+ hasNextPage
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/member/components/MemberDataTableComponent.tsx b/src/packages/ce/src/member/components/MemberDataTableComponent.tsx
new file mode 100644
index 00000000..b5278dfd
--- /dev/null
+++ b/src/packages/ce/src/member/components/MemberDataTableComponent.tsx
@@ -0,0 +1,57 @@
+import React from "react";
+import {DataTable, DataTableColumn, Text, useService, useStore} from "@code0-tech/pictor";
+import {DataTableFilterProps, DataTableSortProps} from "@code0-tech/pictor/dist/components/data-table/DataTable";
+import {
+ Namespace,
+ NamespaceMember,
+ NamespaceProject,
+ NamespaceRole,
+ Runtime,
+ User
+} from "@code0-tech/sagittarius-graphql-types";
+import {ProjectService} from "@edition/project/services/Project.service";
+import {ProjectDataTableRowComponent} from "@edition/project/components/ProjectDataTableRowComponent";
+import {RoleService} from "@edition/role/services/Role.service";
+import {RoleDataTableRowComponent} from "@edition/role/components/RoleDataTableRowComponent";
+import {RuntimeService} from "@edition/runtime/services/Runtime.service";
+import {RuntimeDataTableRowComponent} from "@edition/runtime/components/RuntimeDataTableRowComponent";
+import {UserDataTableRowComponent} from "@edition/user/components/UserDataTableRowComponent";
+import {UserService} from "@edition/user/services/User.service";
+import {MemberService} from "@edition/member/services/Member.service";
+import {MemberDataTableRowComponent} from "@edition/member/components/MemberDataTableRowComponent";
+
+export interface MemberDataTableComponentProps {
+ namespaceId: Namespace['id']
+ sort?: DataTableSortProps
+ filter?: DataTableFilterProps
+ preFilter?: (project: NamespaceMember, index: number) => boolean
+ onSelect?: (item: NamespaceMember | undefined) => void
+}
+
+export const MemberDataTableComponent: React.FC = (props) => {
+
+ const {namespaceId, sort, filter, preFilter = () => true, onSelect} = props
+
+ const memberService = useService(MemberService)
+ const memberStore = useStore(MemberService)
+
+ const members = React.useMemo(
+ () => memberService.values({namespaceId: namespaceId}),
+ [memberStore]
+ )
+
+ return
+
+ No member found. Create one to get started.
+
+ }
+ onSelect={(item) => item && onSelect?.(item)}
+ data={members.map(u => u.json()).filter(preFilter)}>
+ {(member, index) => {
+ return
+ }}
+
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/member/components/MemberDataTableFilterInputComponent.tsx b/src/packages/ce/src/member/components/MemberDataTableFilterInputComponent.tsx
new file mode 100644
index 00000000..d62858f3
--- /dev/null
+++ b/src/packages/ce/src/member/components/MemberDataTableFilterInputComponent.tsx
@@ -0,0 +1,102 @@
+import React from "react";
+import {
+ DataTableFilterInput,
+ DataTableFilterSuggestionMenu, DRuntimeView,
+ MenuCheckboxItem,
+ useService,
+ useStore
+} from "@code0-tech/pictor";
+import {IconCheck} from "@tabler/icons-react";
+import {Namespace, Runtime} from "@code0-tech/sagittarius-graphql-types";
+import {DataTableFilterProps} from "@code0-tech/pictor/dist/components/data-table/DataTable";
+import {RoleService} from "@edition/role/services/Role.service";
+import {RuntimeService} from "@edition/runtime/services/Runtime.service";
+import {UserService} from "@edition/user/services/User.service";
+import {MemberService} from "@edition/member/services/Member.service";
+
+export interface MemberDataTableFilterInputComponentProps {
+ namespaceId: Namespace['id']
+ onChange: (filter: DataTableFilterProps) => void
+}
+
+export const MemberDataTableFilterInputComponent: React.FC = (props) => {
+
+ const {namespaceId, onChange} = props
+
+ const memberService = useService(MemberService)
+ const memberStore = useStore(MemberService)
+ const roleService = useService(RoleService)
+ const roleStore = useStore(RoleService)
+
+ const members = React.useMemo(
+ () => memberService.values({namespaceId: namespaceId}),
+ [memberStore]
+ )
+
+ const roles = React.useMemo(
+ () => {
+ const roleIds = members.map(m => m.roles?.nodes?.map(r => r?.id)).flat()
+ return roleIds.map(id => roleService.getById(id)).filter(Boolean)
+ },
+ [roleStore]
+ )
+
+ return {
+
+ const split = currentValue.split(",").map(s => s.trim()).filter(Boolean)
+
+ return
+ {members.map(member => {
+
+ const isChecked = split.includes(member?.user?.username!)
+
+ return {
+ e.preventDefault();
+ e.stopPropagation();
+ const updated = isChecked
+ ? split.filter(name => name !== member?.user?.username)
+ : [...split, member?.user?.username];
+ applySuggestion(updated.join(","), true);
+ }}>
+ {isChecked ? : }
+ @{member?.user?.username}
+
+ })}
+
+ }
+ },
+ {
+ token: "Roles",
+ key: "roles.nodes.name",
+ operators: ["isOneOf"],
+ suggestion: (context, operator, currentValue, applySuggestion) => {
+
+ const split = currentValue.split(",").map(s => s.trim()).filter(Boolean)
+
+ return
+ {roles.map(role => {
+
+ const isChecked = split.includes(role?.name!)
+
+ return {
+ e.preventDefault();
+ e.stopPropagation();
+ const updated = isChecked
+ ? split.filter(name => name !== role?.name)
+ : [...split, role?.name];
+ applySuggestion(updated.join(","), true);
+ }}>
+ {isChecked ? : }
+ {role?.name}
+
+ })}
+
+ }
+ }
+ ]}/>
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/member/components/MemberDataTableRowComponent.tsx b/src/packages/ce/src/member/components/MemberDataTableRowComponent.tsx
new file mode 100644
index 00000000..aa769778
--- /dev/null
+++ b/src/packages/ce/src/member/components/MemberDataTableRowComponent.tsx
@@ -0,0 +1,252 @@
+import React, {startTransition} from "react";
+import {NamespaceMember} from "@code0-tech/sagittarius-graphql-types";
+import {
+ Avatar,
+ Badge,
+ Button,
+ Card,
+ DataTableColumn,
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogOverlay,
+ DialogPortal,
+ Flex,
+ Menu,
+ MenuContent,
+ MenuItem,
+ MenuPortal,
+ MenuTrigger,
+ Spacing,
+ Text,
+ Tooltip,
+ TooltipContent,
+ TooltipPortal,
+ TooltipTrigger,
+ useService,
+ useStore
+} from "@code0-tech/pictor";
+import {formatDistanceToNow} from "date-fns";
+import {UserService} from "@edition/user/services/User.service";
+import {MemberService} from "@edition/member/services/Member.service";
+import {RoleService} from "@edition/role/services/Role.service";
+import {RolePermissionComponent} from "@edition/role/components/RolePermissionComponent";
+import {IconDotsVertical, IconUserCog, IconUserOff, IconX} from "@tabler/icons-react";
+
+export interface MemberDataTableRowComponentProps {
+ memberId: NamespaceMember['id']
+}
+
+export const MemberDataTableRowComponent: React.FC = (props) => {
+
+ const {memberId} = props
+
+ const memberService = useService(MemberService)
+ const memberStore = useStore(MemberService)
+ const userService = useService(UserService)
+ const userStore = useStore(UserService)
+ const roleService = useService(RoleService)
+ const roleStore = useStore(RoleService)
+
+ const member = React.useMemo(
+ () => memberService.getById(memberId),
+ [memberStore, memberId]
+ )
+
+ const user = React.useMemo(
+ () => userService.getById(member?.user?.id),
+ [userStore, member]
+ )
+
+ const assignedRoles = React.useMemo(
+ () => member?.roles?.nodes?.map(role => roleService.getById(role?.id, {namespaceId: member?.namespace?.id})) || [],
+ [roleStore, member]
+ )
+
+ const [localAssignedRoles, setLocalAssignedRoles] = React.useState(assignedRoles)
+ const [openRemovedMemberDialog, setOpenRemovedMemberDialog] = React.useState(false)
+ const [openAssignRolesDialog, setOpenAssignRolesDialog] = React.useState(false)
+
+ const rolesToAssign = roleService
+ .values({namespaceId: member?.namespace?.id})
+ .filter(role => !localAssignedRoles.find(aRole => aRole?.id === role.id))
+
+ React.useEffect(() => {
+ setLocalAssignedRoles(assignedRoles)
+ }, [assignedRoles])
+
+
+ const memberAssignRoles = React.useCallback(() => {
+ startTransition(() => {
+ memberService.memberAssignRoles({
+ memberId: member?.id!,
+ roleIds: localAssignedRoles.map(r => r?.id!)
+ })
+ })
+ }, [localAssignedRoles, member])
+
+ const memberDelete = React.useCallback(() => {
+ startTransition(() => {
+ memberService.memberDelete({
+ namespaceMemberId: member?.id!
+ })
+ })
+ }, [member])
+
+ return <>
+ setOpenRemovedMemberDialog(open)}>
+
+
+
+
+
+ Are you sure you want to remove {" "}
+
+ @{user?.username}
+ {" "}
+ from this organization/workspace?
+
+
+
+
+ No, go back!
+
+
+ Yes, remove!
+
+
+
+
+
+ setOpenAssignRolesDialog(open)}>
+
+
+
+
+
+ Assign, remove and manage the roles of a active
+ member
+
+
+ Assign roles
+
+
+
+ {rolesToAssign.map((role, index) => {
+ return <>
+ {
+ setLocalAssignedRoles(prevState => [...prevState, role])
+ }}>
+
+ {role?.name}
+
+
+
+ >
+ })}
+
+
+
+
+
+
+ {localAssignedRoles.map(role => {
+ return
+
+ {role?.name}
+
+
+ {
+ setLocalAssignedRoles(prevState => prevState.filter(aRole => aRole?.id != role?.id))
+ }}>
+
+
+
+
+ })}
+
+
+
+
+ No, go back!
+
+
+
+ Yes, save!
+
+
+
+
+
+
+
+
+
+
+
+
+ @{user?.username}
+
+ {user?.admin ? Owner : null}
+ {user?.emailVerifiedAt ? Email verified : null}
+
+
+ {user?.email ?? ""}
+
+
+
+
+ Member since {formatDistanceToNow(member?.createdAt!)} ago
+
+
+
+
+
+ {assignedRoles.map(role => {
+ return
+
+
+ {role?.name}
+
+
+
+
+
+
+
+
+ })}
+
+
+
+ {member?.userAbilities?.deleteMember || member?.userAbilities?.assignMemberRoles ? (
+
+
+
+
+
+
+
+
+ {member?.userAbilities?.deleteMember && (
+ setOpenRemovedMemberDialog(true)}>
+
+ Remove member
+
+ )}
+ {member?.userAbilities?.assignMemberRoles && (
+ setOpenAssignRolesDialog(true)}>
+
+ Assign role
+
+ )}
+
+
+
+ ) : null}
+
+ >
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/member/pages/MemberAddPage.tsx b/src/packages/ce/src/member/pages/MemberAddPage.tsx
new file mode 100644
index 00000000..980136ea
--- /dev/null
+++ b/src/packages/ce/src/member/pages/MemberAddPage.tsx
@@ -0,0 +1,105 @@
+"use client"
+
+import React from "react";
+import {
+ Button,
+ Col,
+ DUserInput,
+ DUserView,
+ Flex,
+ Spacing,
+ Text,
+ useForm,
+ useService,
+ useStore
+} from "@code0-tech/pictor";
+import {MemberService} from "@edition/member/services/Member.service";
+import {useParams, useRouter} from "next/navigation";
+import {Namespace} from "@code0-tech/sagittarius-graphql-types";
+import Link from "next/link";
+import {UserService} from "@edition/user/services/User.service";
+import {InputSyntaxSegment} from "@code0-tech/pictor/dist/components/form/Input.syntax.hook";
+
+export const MemberAddPage: React.FC = () => {
+
+ const params = useParams()
+ const memberService = useService(MemberService)
+ const memberStore = useStore(MemberService)
+ const userService = useService(UserService)
+ const userStore = useStore(UserService)
+ const router = useRouter()
+ const [, startTransition] = React.useTransition()
+
+ const namespaceIndex = params?.namespaceId as string
+ const namespaceId: Namespace['id'] = `gid://sagittarius/Namespace/${namespaceIndex as unknown as number}`
+
+ const members = React.useMemo(() => memberService.values({namespaceId: namespaceId}), [memberStore, userStore])
+ const formInitialValues = React.useMemo(() => ({users: null}), [])
+ const filteredUsers = React.useMemo(() => {
+ return (user: DUserView) => {
+ return !members.find(m => m.user?.id === user.id)
+ }
+ }, [members])
+
+ const [inputs, validate] = useForm<{ users: null | InputSyntaxSegment[] }>({
+ initialValues: formInitialValues,
+ validate: {
+ users: (value) => {
+ if (!value) return "Please select at least one user"
+ if (value.length <= 0) return "Please select at least one user"
+ return null
+ }
+ },
+ onSubmit: (values) => {
+ startTransition(async () => {
+ for (const value of values.users!!) {
+ await memberService.memberInvite({
+ namespaceId: namespaceId!!,
+ userId: (value.value)._id!!
+ })
+ }
+ router.push(`/namespace/${namespaceIndex}/members`)
+ })
+ }
+ })
+
+ //TODO: user abilities for add user as member within namespace
+
+ return
+
+
+
+ Add new members
+
+
+
+ Global runtimes are shared runtimes that can be used across multiple organizations.
+
+
+ {/*@ts-ignore*/}
+
+
+
+
+
+ Go back to members
+
+
+
+ Add members
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/member/pages/MembersPage.tsx b/src/packages/ce/src/member/pages/MembersPage.tsx
new file mode 100644
index 00000000..8aea85be
--- /dev/null
+++ b/src/packages/ce/src/member/pages/MembersPage.tsx
@@ -0,0 +1,16 @@
+"use client"
+
+import {MembersView} from "@edition/member/views/MembersView";
+import React from "react";
+
+export const MembersPage: React.FC = () => {
+ return
+
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/member/Member.service.ts b/src/packages/ce/src/member/services/Member.service.ts
similarity index 93%
rename from src/packages/ce/src/member/Member.service.ts
rename to src/packages/ce/src/member/services/Member.service.ts
index 08f6c42e..4838c579 100644
--- a/src/packages/ce/src/member/Member.service.ts
+++ b/src/packages/ce/src/member/services/Member.service.ts
@@ -20,13 +20,14 @@ import membersQuery from "./queries/Members.query.graphql"
import memberAssignRoleMutation from "./mutations/Member.assignRoles.mutation.graphql"
import memberDeleteMutation from "./mutations/Member.delete.mutation.graphql"
import memberInviteMutation from "./mutations/Member.invite.mutation.graphql"
+import {View} from "@code0-tech/pictor/dist/utils/view";
export class MemberService extends DNamespaceMemberReactiveService {
private readonly client: GraphqlClient
private i = 0
- constructor(client: GraphqlClient, store: ReactiveArrayStore) {
+ constructor(client: GraphqlClient, store: ReactiveArrayStore>) {
super(store)
this.client = client
}
@@ -52,7 +53,7 @@ export class MemberService extends DNamespaceMemberReactiveService {
const nodes = res.data?.namespace?.members?.nodes ?? []
nodes.forEach(member => {
if (member && !this.hasById(member.id)) {
- this.set(this.i++, new DNamespaceMemberView(member))
+ this.set(this.i++, new View(new DNamespaceMemberView(member)))
}
})
})
@@ -87,7 +88,7 @@ export class MemberService extends DNamespaceMemberReactiveService {
}
})
- this.set(index, newMember)
+ this.set(index, new View(newMember))
}
return result.data?.namespacesMembersAssignRoles ?? undefined
@@ -120,7 +121,7 @@ export class MemberService extends DNamespaceMemberReactiveService {
if (result.data && result.data.namespacesMembersInvite && result.data.namespacesMembersInvite.namespaceMember) {
const member = result.data.namespacesMembersInvite.namespaceMember
- this.set(this.i++, new DNamespaceMemberView(member))
+ this.set(this.i++, new View(new DNamespaceMemberView(member)))
}
return result.data?.namespacesMembersInvite ?? undefined
diff --git a/src/packages/ce/src/member/fragments/Member.basic.fragment.graphql b/src/packages/ce/src/member/services/fragments/Member.basic.fragment.graphql
similarity index 61%
rename from src/packages/ce/src/member/fragments/Member.basic.fragment.graphql
rename to src/packages/ce/src/member/services/fragments/Member.basic.fragment.graphql
index e5643d88..f68783d2 100644
--- a/src/packages/ce/src/member/fragments/Member.basic.fragment.graphql
+++ b/src/packages/ce/src/member/services/fragments/Member.basic.fragment.graphql
@@ -1,5 +1,5 @@
-#import "../../user/fragments/User.id.fragment.graphql"
-#import "../../namespace/fragments/Namespace.id.fragment.graphql"
+#import "@edition/user/services/fragments/User.id.fragment.graphql"
+#import "@edition/namespace/services/fragments/Namespace.id.fragment.graphql"
fragment BasicMember on NamespaceMember {
id
roles {
@@ -7,6 +7,7 @@ fragment BasicMember on NamespaceMember {
}
user {
...UserId
+ username
}
namespace {
...NamespaceId
diff --git a/src/packages/ce/src/member/fragments/Member.fragment.graphql b/src/packages/ce/src/member/services/fragments/Member.fragment.graphql
similarity index 70%
rename from src/packages/ce/src/member/fragments/Member.fragment.graphql
rename to src/packages/ce/src/member/services/fragments/Member.fragment.graphql
index 2ff63a7d..647cf062 100644
--- a/src/packages/ce/src/member/fragments/Member.fragment.graphql
+++ b/src/packages/ce/src/member/services/fragments/Member.fragment.graphql
@@ -1,5 +1,5 @@
-#import "../../user/fragments/User.id.fragment.graphql"
-#import "../../namespace/fragments/Namespace.id.fragment.graphql"
+#import "@edition/user/services/fragments/User.id.fragment.graphql"
+#import "@edition/namespace/services/fragments/Namespace.id.fragment.graphql"
fragment Member on NamespaceMember {
id
roles (first: $firstRole, after: $afterRole) {
@@ -7,6 +7,7 @@ fragment Member on NamespaceMember {
nodes {
__typename
id
+ name
}
pageInfo {
endCursor
@@ -16,6 +17,7 @@ fragment Member on NamespaceMember {
}
user {
...UserId
+ username
}
namespace {
...NamespaceId
diff --git a/src/packages/ce/src/member/mutations/Member.assignRoles.mutation.graphql b/src/packages/ce/src/member/services/mutations/Member.assignRoles.mutation.graphql
similarity index 100%
rename from src/packages/ce/src/member/mutations/Member.assignRoles.mutation.graphql
rename to src/packages/ce/src/member/services/mutations/Member.assignRoles.mutation.graphql
diff --git a/src/packages/ce/src/member/mutations/Member.delete.mutation.graphql b/src/packages/ce/src/member/services/mutations/Member.delete.mutation.graphql
similarity index 100%
rename from src/packages/ce/src/member/mutations/Member.delete.mutation.graphql
rename to src/packages/ce/src/member/services/mutations/Member.delete.mutation.graphql
diff --git a/src/packages/ce/src/member/mutations/Member.invite.mutation.graphql b/src/packages/ce/src/member/services/mutations/Member.invite.mutation.graphql
similarity index 100%
rename from src/packages/ce/src/member/mutations/Member.invite.mutation.graphql
rename to src/packages/ce/src/member/services/mutations/Member.invite.mutation.graphql
diff --git a/src/packages/ce/src/member/queries/Members.query.graphql b/src/packages/ce/src/member/services/queries/Members.query.graphql
similarity index 100%
rename from src/packages/ce/src/member/queries/Members.query.graphql
rename to src/packages/ce/src/member/services/queries/Members.query.graphql
diff --git a/src/packages/ce/src/member/views/MembersView.tsx b/src/packages/ce/src/member/views/MembersView.tsx
new file mode 100644
index 00000000..41a4fa67
--- /dev/null
+++ b/src/packages/ce/src/member/views/MembersView.tsx
@@ -0,0 +1,110 @@
+"use client"
+
+import React from "react";
+import {useParams} from "next/navigation";
+import {Button, DataTableFilterProps, DataTableSortProps, Flex, Spacing, Text, ButtonGroup, Menu, MenuTrigger, MenuPortal, MenuContent, MenuCheckboxItem} from "@code0-tech/pictor";
+import Link from "next/link";
+import {MemberDataTableComponent} from "@edition/member/components/MemberDataTableComponent";
+import {MemberDataTableFilterInputComponent} from "@edition/member/components/MemberDataTableFilterInputComponent";
+import {IconMinus, IconSortAscending, IconSortDescending} from "@tabler/icons-react";
+
+//TODO: user abilities for add user as member within namespace
+export const MembersView: React.FC = () => {
+
+ const params = useParams()
+ const namespaceId = params.namespaceId as any as number
+
+ const [filter, setFilter] = React.useState({})
+ const [sort, setSort] = React.useState({})
+
+ return <>
+
+
+
+
+ Members
+
+
+ Manage members that belong to this namespace. You can add new members and manage their permissions.
+
+
+
+
+ Add user
+
+
+
+ Sort
+
+
+
+ {
+ if (sort["user.username"] === undefined)
+ setSort(prev => ({...prev, "user.username": "asc"}))
+ else if (sort["user.username"] === "asc")
+ setSort(prev => ({...prev, "user.username": "desc"}))
+ else
+ setSort(prev => ({...prev, "user.username": undefined}))
+
+ event.preventDefault()
+ event.stopPropagation()
+ }}>
+ {sort["user.username"] === undefined ? : sort["user.username"] === "asc" ?
+ : }
+ Username
+
+ {
+ if (sort["createdAt"] === undefined)
+ setSort(prev => ({...prev, createdAt: "asc"}))
+ else if (sort["createdAt"] === "asc")
+ setSort(prev => ({...prev, createdAt: "desc"}))
+ else
+ setSort(prev => ({...prev, createdAt: undefined}))
+
+ event.preventDefault()
+ event.stopPropagation()
+ }}>
+ {sort["createdAt"] === undefined ?
+ : sort["createdAt"] === "asc" ?
+ : }
+ Created At
+
+ {
+ if (sort["updatedAt"] === undefined)
+ setSort(prev => ({...prev, updatedAt: "asc"}))
+ else if (sort["updatedAt"] === "asc")
+ setSort(prev => ({...prev, updatedAt: "desc"}))
+ else
+ setSort(prev => ({...prev, updatedAt: undefined}))
+
+ event.preventDefault()
+ event.stopPropagation()
+ }}>
+ {sort["updatedAt"] === undefined ?
+ : sort["updatedAt"] === "asc" ?
+ : }
+ Updated At
+
+
+
+
+
+
+
+
+ setFilter(filter)}/>
+
+
+
+
+ >
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/namespace/pages/NamespaceOverviewPage.tsx b/src/packages/ce/src/namespace/pages/NamespaceOverviewPage.tsx
new file mode 100644
index 00000000..c4f8bdf4
--- /dev/null
+++ b/src/packages/ce/src/namespace/pages/NamespaceOverviewPage.tsx
@@ -0,0 +1,42 @@
+"use client"
+
+import React from "react";
+import {ProjectsView} from "@edition/project/views/ProjectsView";
+import {DLayout, useService, useStore, useUserSession, ScrollArea, ScrollAreaScrollbar, ScrollAreaThumb, ScrollAreaViewport} from "@code0-tech/pictor";
+import {NamespaceOverviewPersonalLeftView} from "@edition/namespace/views/NamespaceOverviewPersonalLeftView";
+import {useParams} from "next/navigation";
+import {UserService} from "@edition/user/services/User.service";
+import {NamespaceOverviewOrganizationLeftView} from "@edition/namespace/views/NamespaceOverviewOrganizationLeftView";
+
+export const NamespaceOverviewPage: React.FC = () => {
+
+ const params = useParams()
+ const userService = useService(UserService)
+ const userStore = useStore(UserService)
+ const currentSession = useUserSession()
+
+ const currentUser = React.useMemo(() => userService.getById(currentSession?.user?.id), [userStore, currentSession])
+ const namespaceIndexCurrentUser = currentUser?.namespace?.id?.match(/Namespace\/(\d+)$/)?.[1]
+ const namespaceId = params.namespaceId as any as string
+
+ return :
+ }>
+
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/namespace/NamespaceProjectsCreatePage.tsx b/src/packages/ce/src/namespace/pages/NamespaceProjectsCreatePage.tsx
similarity index 51%
rename from src/packages/ce/src/dashboard/namespace/NamespaceProjectsCreatePage.tsx
rename to src/packages/ce/src/namespace/pages/NamespaceProjectsCreatePage.tsx
index 0135fedc..cab32b00 100644
--- a/src/packages/ce/src/dashboard/namespace/NamespaceProjectsCreatePage.tsx
+++ b/src/packages/ce/src/namespace/pages/NamespaceProjectsCreatePage.tsx
@@ -4,7 +4,7 @@ import React from "react";
import {Button, Col, Flex, Spacing, Text, TextInput, toast, useForm, useService} from "@code0-tech/pictor";
import Link from "next/link";
import {useParams, useRouter} from "next/navigation";
-import {ProjectService} from "@edition/project/Project.service";
+import {ProjectService} from "@edition/project/services/Project.service";
import {Namespace} from "@code0-tech/sagittarius-graphql-types";
export const NamespaceProjectsCreatePage: React.FC = () => {
@@ -57,38 +57,46 @@ export const NamespaceProjectsCreatePage: React.FC = () => {
}
})
- return
-
-
- Create new project
-
-
-
- Global runtimes are shared runtimes that can be used across multiple organizations.
-
-
-
-
-
-
-
-
-
- Go back to projects
+ return
+
+
+
+ Create new project
+
+
+
+ Global runtimes are shared runtimes that can be used across multiple organizations.
+
+
+
+
+
+
+
+
+
+ Go back to projects
+
+
+
+ Create project
-
-
- Create project
-
-
-
-
+
+
+
+
}
\ No newline at end of file
diff --git a/src/packages/ce/src/namespace/pages/NamespaceProjectsPage.tsx b/src/packages/ce/src/namespace/pages/NamespaceProjectsPage.tsx
new file mode 100644
index 00000000..ea4da2bd
--- /dev/null
+++ b/src/packages/ce/src/namespace/pages/NamespaceProjectsPage.tsx
@@ -0,0 +1,16 @@
+"use client"
+
+import {ProjectsView} from "@edition/project/views/ProjectsView";
+import React from "react";
+
+export const NamespaceProjectsPage: React.FC = () => {
+ return
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/namespace/Namespace.service.ts b/src/packages/ce/src/namespace/services/Namespace.service.ts
similarity index 86%
rename from src/packages/ce/src/namespace/Namespace.service.ts
rename to src/packages/ce/src/namespace/services/Namespace.service.ts
index 5a7a1f5f..532587df 100644
--- a/src/packages/ce/src/namespace/Namespace.service.ts
+++ b/src/packages/ce/src/namespace/services/Namespace.service.ts
@@ -2,12 +2,13 @@ import {DNamespaceReactiveService, DNamespaceView, ReactiveArrayStore} from "@co
import {GraphqlClient} from "@core/util/graphql-client";
import {Namespace, Query} from "@code0-tech/sagittarius-graphql-types";
import namespaceQuery from "./queries/Namespace.query.graphql";
+import {View} from "@code0-tech/pictor/dist/utils/view";
export class NamespaceService extends DNamespaceReactiveService {
private readonly client: GraphqlClient
- constructor(client: GraphqlClient, store: ReactiveArrayStore) {
+ constructor(client: GraphqlClient, store: ReactiveArrayStore>) {
super(store);
this.client = client
}
@@ -24,7 +25,7 @@ export class NamespaceService extends DNamespaceReactiveService {
if (!data) return
if (data.namespace) {
- this.add(new DNamespaceView(data.namespace))
+ this.add(new View(new DNamespaceView(data.namespace)))
}
})
diff --git a/src/packages/ce/src/namespace/fragments/Namespace.fragment.graphql b/src/packages/ce/src/namespace/services/fragments/Namespace.fragment.graphql
similarity index 81%
rename from src/packages/ce/src/namespace/fragments/Namespace.fragment.graphql
rename to src/packages/ce/src/namespace/services/fragments/Namespace.fragment.graphql
index 3e54c883..26755415 100644
--- a/src/packages/ce/src/namespace/fragments/Namespace.fragment.graphql
+++ b/src/packages/ce/src/namespace/services/fragments/Namespace.fragment.graphql
@@ -1,5 +1,5 @@
-#import "../../organization/fragments/Organization.id.fragment.graphql"
-#import "../../user/fragments/User.id.fragment.graphql"
+#import "@edition/organization/services/fragments/Organization.id.fragment.graphql"
+#import "@edition/user/services/fragments/User.id.fragment.graphql"
fragment Namespace on Namespace {
__typename
id
diff --git a/src/packages/ce/src/namespace/fragments/Namespace.id.fragment.graphql b/src/packages/ce/src/namespace/services/fragments/Namespace.id.fragment.graphql
similarity index 100%
rename from src/packages/ce/src/namespace/fragments/Namespace.id.fragment.graphql
rename to src/packages/ce/src/namespace/services/fragments/Namespace.id.fragment.graphql
diff --git a/src/packages/ce/src/namespace/queries/Namespace.query.graphql b/src/packages/ce/src/namespace/services/queries/Namespace.query.graphql
similarity index 100%
rename from src/packages/ce/src/namespace/queries/Namespace.query.graphql
rename to src/packages/ce/src/namespace/services/queries/Namespace.query.graphql
diff --git a/src/packages/ce/src/namespace/views/NamespaceOverviewOrganizationLeftView.tsx b/src/packages/ce/src/namespace/views/NamespaceOverviewOrganizationLeftView.tsx
new file mode 100644
index 00000000..3aa6c1e3
--- /dev/null
+++ b/src/packages/ce/src/namespace/views/NamespaceOverviewOrganizationLeftView.tsx
@@ -0,0 +1,116 @@
+import React from "react";
+import {
+ AuroraBackground,
+ Avatar,
+ Button,
+ Flex,
+ hashToColor,
+ ScrollArea,
+ ScrollAreaScrollbar,
+ ScrollAreaThumb,
+ ScrollAreaViewport,
+ Spacing,
+ Text,
+ Tooltip,
+ TooltipContent,
+ TooltipPortal,
+ TooltipTrigger,
+ useService,
+ useStore
+} from "@code0-tech/pictor";
+import {UserService} from "@edition/user/services/User.service";
+import {OrganizationService} from "@edition/organization/services/Organization.service";
+import Link from "next/link";
+import {useParams} from "next/navigation";
+import {MemberService} from "@edition/member/services/Member.service";
+import {NamespaceService} from "@edition/namespace/services/Namespace.service";
+import {RuntimeDataTableComponent} from "@edition/runtime/components/RuntimeDataTableComponent";
+
+export const NamespaceOverviewOrganizationLeftView: React.FC = () => {
+
+ const params = useParams()
+ const userService = useService(UserService)
+ const userStore = useStore(UserService)
+ const organizationService = useService(OrganizationService)
+ const organizationStore = useStore(OrganizationService)
+ const memberService = useService(MemberService)
+ const memberStore = useStore(MemberService)
+ const namespaceService = useService(NamespaceService)
+ const namespaceStore = useStore(NamespaceService)
+
+ const namespaceIndex = params.namespaceId as any as number
+
+ const namespace = React.useMemo(
+ () => namespaceService.getById(`gid://sagittarius/Namespace/${namespaceIndex}`),
+ [namespaceStore, namespaceIndex]
+ )
+ const parentOrganization = React.useMemo(
+ () => namespace?.parent?.__typename === "Organization" ? organizationService.getById(namespace?.parent?.id) : null,
+ [organizationStore, namespace]
+ )
+
+ const members = React.useMemo(
+ () => memberService.values({namespaceId: `gid://sagittarius/Namespace/${namespaceIndex}`}),
+ [memberStore, userStore]
+ )
+
+ return
+
+
+
+
+ {parentOrganization?.name}
+
+ Edit Organization
+
+
+ Upgrade to Team
+
+
+
+
+
+ Members
+
+
+ {members.map(member => {
+ const user = userService.getById(member.user?.id!!)
+ return
+
+
+
+
+
+
+
+ @{user?.username}
+
+
+ {user?.email}
+
+
+
+
+
+ })}
+ {members.length <= 0 &&
+
+ Invite first member
+ }
+
+
+
+
+ Runtimes
+
+
+
+
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/namespace/views/NamespaceOverviewPersonalLeftView.tsx b/src/packages/ce/src/namespace/views/NamespaceOverviewPersonalLeftView.tsx
new file mode 100644
index 00000000..13775728
--- /dev/null
+++ b/src/packages/ce/src/namespace/views/NamespaceOverviewPersonalLeftView.tsx
@@ -0,0 +1,147 @@
+import React from "react";
+import {
+ AuroraBackground,
+ Avatar,
+ Badge,
+ Button,
+ Flex,
+ hashToColor,
+ ScrollArea,
+ ScrollAreaScrollbar,
+ ScrollAreaThumb,
+ ScrollAreaViewport,
+ Spacing,
+ Text,
+ Tooltip,
+ TooltipContent,
+ TooltipPortal,
+ TooltipTrigger,
+ useService,
+ useStore,
+ useUserSession
+} from "@code0-tech/pictor";
+import {UserService} from "@edition/user/services/User.service";
+import {IconMail, IconSparkles, IconUser, IconUserCog} from "@tabler/icons-react";
+import {OrganizationService} from "@edition/organization/services/Organization.service";
+import Link from "next/link";
+import {useParams} from "next/navigation";
+import {MemberService} from "@edition/member/services/Member.service";
+
+export const NamespaceOverviewPersonalLeftView: React.FC = () => {
+
+ const params = useParams()
+ const userService = useService(UserService)
+ const userStore = useStore(UserService)
+ const organizationService = useService(OrganizationService)
+ const organizationStore = useStore(OrganizationService)
+ const memberService = useService(MemberService)
+ const memberStore = useStore(MemberService)
+
+ const namespaceIndex = params.namespaceId as any as number
+
+ const currentSession = useUserSession()
+ const currentUser = React.useMemo(
+ () => userService.getById(currentSession?.user?.id),
+ [userStore, currentSession]
+ )
+ const organizations = React.useMemo(
+ () => organizationService.values(),
+ [organizationStore]
+ )
+
+ const members = React.useMemo(
+ () => memberService.values({namespaceId: `gid://sagittarius/Namespace/${namespaceIndex}`}),
+ [memberStore, userStore]
+ )
+
+ return
+
+
+
+
+ {currentUser?.firstname} {currentUser?.lastname}
+
+
+
+
+ @{currentUser?.username}
+
+
+
+ {currentUser?.admin ? Admin :
+ User }
+
+
+
+ {currentUser?.email}
+
+
+
+ BASIC
+
+
+
+ Edit Profile
+
+
+ Upgrade to Pro
+
+
+
+
+
+ Organizations
+
+
+ {organizations.map(organization => {
+ return
+ })}
+ {organizations.length <= 0 && You aren't a member of any organization }
+
+
+
+
+ Members
+
+
+ {members.map(member => {
+ const user = userService.getById(member.user?.id!!)
+ return
+
+
+
+
+
+
+
+ @{user?.username}
+
+
+ {user?.email}
+
+
+
+
+
+ })}
+ {members.length <= 0 &&
+
+ Invite first member
+ }
+
+
+
+
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/namespace/NamespaceOverviewRightView.tsx b/src/packages/ce/src/namespace/views/NamespaceOverviewRightView.tsx
similarity index 87%
rename from src/packages/ce/src/dashboard/namespace/NamespaceOverviewRightView.tsx
rename to src/packages/ce/src/namespace/views/NamespaceOverviewRightView.tsx
index 5698f9df..445f145a 100644
--- a/src/packages/ce/src/dashboard/namespace/NamespaceOverviewRightView.tsx
+++ b/src/packages/ce/src/namespace/views/NamespaceOverviewRightView.tsx
@@ -18,10 +18,10 @@ import {
useService,
useStore
} from "@code0-tech/pictor";
-import {MemberService} from "@edition/member/Member.service";
+import {MemberService} from "@edition/member/services/Member.service";
import {useParams} from "next/navigation";
-import {NamespaceService} from "@edition/namespace/Namespace.service";
-import {UserService} from "@edition/user/User.service";
+import {NamespaceService} from "@edition/namespace/services/Namespace.service";
+import {UserService} from "@edition/user/services/User.service";
import Link from "next/link";
export const NamespaceOverviewRightView: React.FC = () => {
@@ -38,7 +38,7 @@ export const NamespaceOverviewRightView: React.FC = () => {
const namespace = React.useMemo(() => namespaceService.getById(`gid://sagittarius/Namespace/${namespaceId}`), [namespaceStore, namespaceId])
const members = React.useMemo(() => memberService.values({namespaceId: `gid://sagittarius/Namespace/${namespaceId}`}), [memberStore, userStore])
- return
+ return
Upgrade to Team
@@ -54,19 +54,18 @@ export const NamespaceOverviewRightView: React.FC = () => {
const user = userService.getById(member.user?.id!!)
return
-
+
- {user?.username}
+ @{user?.username}
{user?.email}
-
diff --git a/src/packages/ce/src/namespace/views/NamespaceTabView.tsx b/src/packages/ce/src/namespace/views/NamespaceTabView.tsx
new file mode 100644
index 00000000..7233599f
--- /dev/null
+++ b/src/packages/ce/src/namespace/views/NamespaceTabView.tsx
@@ -0,0 +1,85 @@
+"use client"
+
+import React from "react";
+import {Badge, Button, useService, useStore} from "@code0-tech/pictor";
+import {Tab, TabList, TabTrigger} from "@code0-tech/pictor/dist/components/tab/Tab";
+import {IconFolders, IconHome, IconServer, IconSettings, IconUserCog, IconUsers} from "@tabler/icons-react";
+import {useParams, usePathname, useRouter} from "next/navigation";
+import {NamespaceService} from "@edition/namespace/services/Namespace.service";
+import {OrganizationService} from "@edition/organization/services/Organization.service";
+import {ProjectService} from "@edition/project/services/Project.service";
+import {RoleService} from "@edition/role/services/Role.service";
+import {MemberService} from "@edition/member/services/Member.service";
+import {RuntimeService} from "@edition/runtime/services/Runtime.service";
+import {hashToColor} from "@code0-tech/pictor/dist/components/d-flow/DFlow.util";
+
+export const NamespaceTabView: React.FC = () => {
+
+ const pathname = usePathname()
+ const router = useRouter()
+ const params = useParams()
+ const namespaceId = params.namespaceId as any
+
+ const namespaceService = useService(NamespaceService)
+ const namespaceStore = useStore(NamespaceService)
+ const organizationService = useService(OrganizationService)
+ const organizationStore = useStore(OrganizationService)
+
+ const namespace = React.useMemo(() => namespaceService.getById(`gid://sagittarius/Namespace/${namespaceId}`), [namespaceStore, namespaceId])
+ const parentOrganization = React.useMemo(() => namespace?.parent?.__typename === "Organization" ? organizationService.getById(namespace?.parent?.id) : null, [organizationStore, namespace])
+
+ const baseLink = `/namespace/${namespaceId}`
+ const defaultValue = pathname.includes("projects") ? "projects"
+ : pathname.includes("members") ? "members"
+ : pathname.includes("roles") ? "roles"
+ : pathname.includes("runtimes") ? "runtimes"
+ : pathname.includes("settings") ? "settings"
+ : "overview"
+
+ const settings = React.useMemo(() => {
+ return namespace?.parent?.__typename == "Organization"
+ && parentOrganization
+ && (
+ parentOrganization.userAbilities?.deleteOrganization
+ || parentOrganization?.userAbilities?.updateOrganization
+ //TODO add license check for enterprise features
+ ) ? (
+
+ router.push(`${baseLink}/settings`)}>
+
+
+
+ ) : null
+ }, [namespace, parentOrganization])
+
+ return
+
+
+ router.push(baseLink)}>
+
+
+
+
+ router.push(`${baseLink}/projects`)}>
+
+
+
+
+ router.push(`${baseLink}/members`)}>
+
+
+
+
+ router.push(`${baseLink}/roles`)}>
+
+
+
+
+ router.push(`${baseLink}/runtimes`)}>
+
+
+
+ {settings}
+
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/organization/components/OrganizationDataTableComponent.tsx b/src/packages/ce/src/organization/components/OrganizationDataTableComponent.tsx
new file mode 100644
index 00000000..1c79ae32
--- /dev/null
+++ b/src/packages/ce/src/organization/components/OrganizationDataTableComponent.tsx
@@ -0,0 +1,64 @@
+import React from "react";
+import {DataTable, DataTableColumn, Text, useService, useStore} from "@code0-tech/pictor";
+import {DataTableFilterProps, DataTableSortProps} from "@code0-tech/pictor/dist/components/data-table/DataTable";
+import {Organization} from "@code0-tech/sagittarius-graphql-types";
+import {OrganizationDataTableRowComponent} from "@edition/organization/components/OrganizationDataTableRowComponent";
+import {OrganizationService} from "@edition/organization/services/Organization.service";
+import {MemberService} from "@edition/member/services/Member.service";
+
+export interface OrganizationDataTableComponentProps {
+ sort?: DataTableSortProps
+ filter?: DataTableFilterProps
+ preFilter?: (project: Organization, index: number) => boolean
+ onSelect?: (item: Organization | undefined) => void
+ minimized?: boolean
+}
+
+export const OrganizationDataTableComponent: React.FC = (props) => {
+
+ const {sort, filter, preFilter = () => true, onSelect, minimized} = props
+
+ const organizationService = useService(OrganizationService)
+ const organizationStore = useStore(OrganizationService)
+ const memberService = useService(MemberService)
+ const memberStore = useStore(MemberService)
+
+ const organizations = React.useMemo(
+ () => organizationService.values(),
+ [organizationStore]
+ )
+
+ const data = React.useMemo(
+ () => organizations.map(
+ o => o.json()
+ ).filter(preFilter).map(o => {
+ const members = memberService.values({namespaceId: o.namespace?.id})
+ const organization: Organization = {
+ ...o,
+ namespace: {
+ ...o.namespace,
+ members: {
+ nodes: members.map(m => m.json()),
+ }
+ }
+ }
+ return organization
+ }),
+ [organizations, memberStore, preFilter]
+ )
+
+ return
+
+ No organization found. Create one to get started.
+
+ }
+ onSelect={(item) => item && onSelect?.(item)}
+ data={data}>
+ {(organization, index) => {
+ return
+ }}
+
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/organization/components/OrganizationDataTableFilterInputComponent.tsx b/src/packages/ce/src/organization/components/OrganizationDataTableFilterInputComponent.tsx
new file mode 100644
index 00000000..4abd5813
--- /dev/null
+++ b/src/packages/ce/src/organization/components/OrganizationDataTableFilterInputComponent.tsx
@@ -0,0 +1,106 @@
+import React from "react";
+import {
+ DataTableFilterInput,
+ DataTableFilterSuggestionMenu,
+ MenuCheckboxItem,
+ useService,
+ useStore
+} from "@code0-tech/pictor";
+import {IconCheck} from "@tabler/icons-react";
+import {DataTableFilterProps} from "@code0-tech/pictor/dist/components/data-table/DataTable";
+import {OrganizationService} from "@edition/organization/services/Organization.service";
+import {NamespaceService} from "@edition/namespace/services/Namespace.service";
+import {MemberService} from "@edition/member/services/Member.service";
+
+export interface OrganizationDataTableFilterInputComponentProps {
+ onChange: (filter: DataTableFilterProps) => void
+}
+
+export const OrganizationDataTableFilterInputComponent: React.FC = (props) => {
+
+ const {onChange} = props
+
+ const organizationService = useService(OrganizationService)
+ const organizationStore = useStore(OrganizationService)
+ const namespaceStore = useStore(NamespaceService)
+ const namespaceService = useService(NamespaceService)
+ const memberService = useService(MemberService)
+ const memberStore = useStore(MemberService)
+
+ const organizations = React.useMemo(
+ () => organizationService.values(),
+ [organizationStore]
+ )
+
+ const namespaces = React.useMemo(
+ () => organizations.map(
+ organization => namespaceService.getById(organization?.namespace?.id)
+ ),
+ [namespaceStore, organizations]
+ )
+
+ const members = React.useMemo(
+ () => namespaces.map(
+ namespace => memberService.values({namespaceId: namespace?.id})
+ ).flat(),
+ [namespaces, memberStore, organizations]
+ )
+
+ return {
+
+ const split = currentValue.split(",").map(s => s.trim()).filter(Boolean)
+
+ return
+ {organizations.map(organization => {
+
+ const isChecked = split.includes(organization?.name!)
+
+ return {
+ e.preventDefault();
+ e.stopPropagation();
+ const updated = isChecked
+ ? split.filter(name => name !== organization?.name)
+ : [...split, organization?.name];
+ applySuggestion(updated.join(","), true);
+ }}>
+ {isChecked ? : }
+ {organization?.name}
+
+ })}
+
+ }
+ }, {
+ token: "Member",
+ key: "namespace.members.nodes.user.username",
+ operators: ["isOneOf"],
+ suggestion: (context, operator, currentValue, applySuggestion) => {
+
+ const split = currentValue.split(",").map(s => s.trim()).filter(Boolean)
+
+ return
+ {members.map(member => {
+
+ const isChecked = split.includes(member?.user?.username!)
+
+ return {
+ e.preventDefault();
+ e.stopPropagation();
+ const updated = isChecked
+ ? split.filter(name => name !== member?.user?.username)
+ : [...split, member?.user?.username];
+ applySuggestion(updated.join(","), true);
+ }}>
+ {isChecked ? : }
+ {member?.user?.username}
+
+ })}
+
+ }
+ }
+ ]}/>
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/organization/components/OrganizationDataTableRowComponent.tsx b/src/packages/ce/src/organization/components/OrganizationDataTableRowComponent.tsx
new file mode 100644
index 00000000..f51c8553
--- /dev/null
+++ b/src/packages/ce/src/organization/components/OrganizationDataTableRowComponent.tsx
@@ -0,0 +1,129 @@
+import React from "react";
+import {Organization} from "@code0-tech/sagittarius-graphql-types";
+import {
+ Avatar,
+ Button,
+ DataTableColumn,
+ DOrganizationView,
+ Flex,
+ hashToColor,
+ Text,
+ useService,
+ useStore,
+ useUserSession
+} from "@code0-tech/pictor";
+import {IconLogout} from "@tabler/icons-react";
+import {OrganizationService} from "@edition/organization/services/Organization.service";
+import {NamespaceService} from "@edition/namespace/services/Namespace.service";
+import {MemberService} from "@edition/member/services/Member.service";
+import {UserService} from "@edition/user/services/User.service";
+import {formatDistanceToNow} from "date-fns";
+
+export interface OrganizationDataTableRowComponentProps {
+ organizationId: Organization['id']
+ onLeave?: (organization: DOrganizationView) => void
+ minimized?: boolean
+}
+
+export const OrganizationDataTableRowComponent: React.FC = (props) => {
+
+ const {organizationId, onLeave, minimized} = props
+
+ const organizationService = useService(OrganizationService)
+ const organizationStore = useStore(OrganizationService)
+ const namespaceStore = useStore(NamespaceService)
+ const namespaceService = useService(NamespaceService)
+ const memberService = useService(MemberService)
+ const memberStore = useStore(MemberService)
+ const userService = useService(UserService)
+ const userStore = useStore(UserService)
+
+ const currentSession = useUserSession()
+
+ const currentUser = React.useMemo(
+ () => userService.getById(currentSession?.user?.id),
+ [userStore, currentSession]
+ )
+
+ const organization = React.useMemo(
+ () => organizationService.getById(organizationId),
+ [organizationStore, organizationId]
+ )
+
+ const namespace = React.useMemo(
+ () => organization ? namespaceService.getById(organization.namespace?.id) : null,
+ [namespaceStore, organization]
+ )
+
+ const namespaceMember = React.useMemo(
+ () => namespace && currentUser ? memberService.getByNamespaceIdAndUserId(namespace.id, currentUser.id) : null,
+ [memberStore, namespace, currentUser]
+ )
+
+ const namespaceMembers = React.useMemo(
+ () => memberService.values({namespaceId: namespace?.id}),
+ [memberStore, userStore, namespace]
+ )
+
+ return <>
+
+
+
+
+
+
+ {organization?.name}
+
+
+
+ {!minimized && (
+
+ Updated {formatDistanceToNow(organization?.updatedAt!)} ago
+
+ )}
+
+
+
+ {!minimized && (
+ <>
+
+
+ {namespace?.projects?.count ? (
+
+ {namespace?.projects?.count ?? 0} {" "}
+ projects
+
+ ) : null}
+
+ {namespace?.members?.count ? (
+
+ {namespace?.members?.count ?? 0}{" "}
+ members
+
+ ) : null}
+
+ {namespace?.runtimes?.count ? (
+
+ {namespace?.runtimes?.count ?? 0}{" "}
+ connected runtimes
+
+ ) : null}
+
+
+ {onLeave && namespaceMember && namespaceMember.userAbilities?.deleteMember ? (
+
+ {
+ event.stopPropagation()
+ if (organization) onLeave(organization)
+ }}>
+ Leave
+
+
+ ) : null}
+ >
+ )}
+
+ >
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/organization/pages/OrganizationCreatePage.tsx b/src/packages/ce/src/organization/pages/OrganizationCreatePage.tsx
new file mode 100644
index 00000000..03bcf7d1
--- /dev/null
+++ b/src/packages/ce/src/organization/pages/OrganizationCreatePage.tsx
@@ -0,0 +1,68 @@
+"use client"
+
+import {Button, Col, Flex, Spacing, Text, TextInput, useForm, useService} from "@code0-tech/pictor";
+import Link from "next/link";
+import {OrganizationService} from "@edition/organization/services/Organization.service";
+import React from "react";
+import {useRouter} from "next/navigation";
+
+export const OrganizationCreatePage = () => {
+
+ const organizationService = useService(OrganizationService)
+ const [, startTransition] = React.useTransition()
+ const router = useRouter()
+
+ const [inputs, validate] = useForm({
+ initialValues: {
+ name: null,
+ },
+ validate: {
+ name: (value) => {
+ if (!value) return "Name is required"
+ return null
+ }
+ },
+ onSubmit: (values) => {
+ startTransition(() => {
+ organizationService.organizationCreate({
+ name: values.name as unknown as string
+ }).then(payload => {
+ if ((payload?.errors?.length ?? 0) <= 0) {
+ router.push("/organizations")
+ }
+ })
+ })
+ }
+ })
+
+ return
+
+
+
+ Create new organization
+
+
+
+ Organizations are helpfully if managing a group of users and plenty of projects.
+
+
+
+
+
+
+
+ Go back to organizations
+
+
+
+ Create organization
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/organization/pages/OrganizationSettingsPage.tsx b/src/packages/ce/src/organization/pages/OrganizationSettingsPage.tsx
new file mode 100644
index 00000000..063e30a3
--- /dev/null
+++ b/src/packages/ce/src/organization/pages/OrganizationSettingsPage.tsx
@@ -0,0 +1,94 @@
+"use client"
+
+import React from "react";
+import {useParams} from "next/navigation";
+import {Namespace} from "@code0-tech/sagittarius-graphql-types";
+import {
+ AuroraBackground,
+ Badge,
+ Button,
+ DResizableHandle,
+ DResizablePanel,
+ DResizablePanelGroup,
+ Flex,
+ Text,
+ useService,
+ useStore
+} from "@code0-tech/pictor";
+import {NamespaceService} from "@edition/namespace/services/Namespace.service";
+import {OrganizationService} from "@edition/organization/services/Organization.service";
+import {IconLayoutSidebar} from "@tabler/icons-react";
+import {Tab, TabContent, TabList, TabTrigger} from "@code0-tech/pictor/dist/components/tab/Tab";
+import {UsageView} from "@edition/runtime/views/UsageView";
+import {OrganizationUpgradeView} from "@edition/organization/views/OrganizationUpgradeView";
+import {OrganizationDeleteView} from "@edition/organization/views/OrganizationDeleteView";
+import {OrganizationGeneralSettingsView} from "@edition/organization/views/OrganizationGeneralSettingsView";
+
+export const OrganizationSettingsPage: React.FC = () => {
+
+ //TODO: add ability check for organization settings access for every settings tab
+
+ return
+
+
+
+
+ Organization settings
+
+
+
+
+
+
+ General settings and restrictions for your Sculptor application. These settings affect all users
+ and organizations within the application.
+
+
+
+
+ General
+
+
+
+
+
+ Upgrade to Team
+
+
+
+
+
+
+ Runtime usage
+
+
+
+
+ Delete organization
+
+
+
+
+
+
+
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+
+
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/organization/pages/OrganizationsPage.tsx b/src/packages/ce/src/organization/pages/OrganizationsPage.tsx
new file mode 100644
index 00000000..5717f34d
--- /dev/null
+++ b/src/packages/ce/src/organization/pages/OrganizationsPage.tsx
@@ -0,0 +1,17 @@
+"use client"
+
+import React from "react";
+import {OrganizationsView} from "@edition/organization/views/OrganizationsView";
+
+export const OrganizationsPage = () => {
+ return
+
+
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/organization/Organization.service.ts b/src/packages/ce/src/organization/services/Organization.service.ts
similarity index 92%
rename from src/packages/ce/src/organization/Organization.service.ts
rename to src/packages/ce/src/organization/services/Organization.service.ts
index 50063f9e..3247504d 100644
--- a/src/packages/ce/src/organization/Organization.service.ts
+++ b/src/packages/ce/src/organization/services/Organization.service.ts
@@ -15,13 +15,14 @@ import createOrganizationMutation from "./mutations/Organization.create.mutation
import updateOrganizationMutation from "./mutations/Organization.update.mutation.graphql";
import deleteOrganizationMutation from "./mutations/Organization.delete.mutation.graphql";
import organizationQuery from "./queries/Organization.query.graphql";
+import {View} from "@code0-tech/pictor/dist/utils/view";
export class OrganizationService extends DOrganizationReactiveService {
private readonly client: GraphqlClient
private i = 0;
- constructor(client: GraphqlClient, store: ReactiveArrayStore) {
+ constructor(client: GraphqlClient, store: ReactiveArrayStore>) {
super(store);
this.client = client
}
@@ -36,7 +37,7 @@ export class OrganizationService extends DOrganizationReactiveService {
if (data.organizations && data.organizations.nodes) {
data.organizations.nodes.forEach((organization) => {
- if (organization && !this.hasById(organization.id)) this.set(this.i++, new DOrganizationView(organization))
+ if (organization && !this.hasById(organization.id)) this.set(this.i++, new View(new DOrganizationView(organization)))
})
}
})
@@ -59,7 +60,7 @@ export class OrganizationService extends DOrganizationReactiveService {
if (result.data && result.data.organizationsCreate && result.data.organizationsCreate.organization) {
const organization = result.data.organizationsCreate.organization
if (!this.hasById(organization.id)) {
- this.add(new DOrganizationView(organization))
+ this.add(new View(new DOrganizationView(organization)))
}
}
@@ -95,7 +96,7 @@ export class OrganizationService extends DOrganizationReactiveService {
if (result.data && result.data.organizationsUpdate && result.data.organizationsUpdate.organization) {
const organization = result.data.organizationsUpdate.organization
const index = this.values().findIndex(o => o.id === organization.id)
- this.set(index, new DOrganizationView(organization))
+ this.set(index, new View(new DOrganizationView(organization)))
}
diff --git a/src/packages/ce/src/organization/fragments/Organization.fragment.graphql b/src/packages/ce/src/organization/services/fragments/Organization.fragment.graphql
similarity index 100%
rename from src/packages/ce/src/organization/fragments/Organization.fragment.graphql
rename to src/packages/ce/src/organization/services/fragments/Organization.fragment.graphql
diff --git a/src/packages/ce/src/organization/fragments/Organization.id.fragment.graphql b/src/packages/ce/src/organization/services/fragments/Organization.id.fragment.graphql
similarity index 100%
rename from src/packages/ce/src/organization/fragments/Organization.id.fragment.graphql
rename to src/packages/ce/src/organization/services/fragments/Organization.id.fragment.graphql
diff --git a/src/packages/ce/src/organization/mutations/Organization.create.mutation.graphql b/src/packages/ce/src/organization/services/mutations/Organization.create.mutation.graphql
similarity index 100%
rename from src/packages/ce/src/organization/mutations/Organization.create.mutation.graphql
rename to src/packages/ce/src/organization/services/mutations/Organization.create.mutation.graphql
diff --git a/src/packages/ce/src/organization/mutations/Organization.delete.mutation.graphql b/src/packages/ce/src/organization/services/mutations/Organization.delete.mutation.graphql
similarity index 100%
rename from src/packages/ce/src/organization/mutations/Organization.delete.mutation.graphql
rename to src/packages/ce/src/organization/services/mutations/Organization.delete.mutation.graphql
diff --git a/src/packages/ce/src/organization/mutations/Organization.update.mutation.graphql b/src/packages/ce/src/organization/services/mutations/Organization.update.mutation.graphql
similarity index 100%
rename from src/packages/ce/src/organization/mutations/Organization.update.mutation.graphql
rename to src/packages/ce/src/organization/services/mutations/Organization.update.mutation.graphql
diff --git a/src/packages/ce/src/organization/queries/Organization.query.graphql b/src/packages/ce/src/organization/services/queries/Organization.query.graphql
similarity index 100%
rename from src/packages/ce/src/organization/queries/Organization.query.graphql
rename to src/packages/ce/src/organization/services/queries/Organization.query.graphql
diff --git a/src/packages/ce/src/dashboard/organization/OrganizationDeleteView.tsx b/src/packages/ce/src/organization/views/OrganizationDeleteView.tsx
similarity index 85%
rename from src/packages/ce/src/dashboard/organization/OrganizationDeleteView.tsx
rename to src/packages/ce/src/organization/views/OrganizationDeleteView.tsx
index b261e1d6..9dbd5118 100644
--- a/src/packages/ce/src/dashboard/organization/OrganizationDeleteView.tsx
+++ b/src/packages/ce/src/organization/views/OrganizationDeleteView.tsx
@@ -3,8 +3,8 @@
import React from "react";
import {Button, Card, Flex, Spacing, Text, useService, useStore, toast} from "@code0-tech/pictor";
import {useParams, useRouter} from "next/navigation";
-import {NamespaceService} from "@edition/namespace/Namespace.service";
-import {OrganizationService} from "@edition/organization/Organization.service";
+import {NamespaceService} from "@edition/namespace/services/Namespace.service";
+import {OrganizationService} from "@edition/organization/services/Organization.service";
import {Namespace} from "@code0-tech/sagittarius-graphql-types";
export const OrganizationDeleteView: React.FC = () => {
@@ -44,18 +44,17 @@ export const OrganizationDeleteView: React.FC = () => {
return <>
- Delete organization forever
+ Delete organization
- Delete organization
-
+
This will delete the organization and cannot be undone.
-
+
Delete organization forever
diff --git a/src/packages/ce/src/dashboard/organization/OrganizationGeneralSettingsView.tsx b/src/packages/ce/src/organization/views/OrganizationGeneralSettingsView.tsx
similarity index 83%
rename from src/packages/ce/src/dashboard/organization/OrganizationGeneralSettingsView.tsx
rename to src/packages/ce/src/organization/views/OrganizationGeneralSettingsView.tsx
index b7d7d5c6..2b32ed3b 100644
--- a/src/packages/ce/src/dashboard/organization/OrganizationGeneralSettingsView.tsx
+++ b/src/packages/ce/src/organization/views/OrganizationGeneralSettingsView.tsx
@@ -4,8 +4,8 @@ import React from "react";
import CardSection from "@code0-tech/pictor/dist/components/card/CardSection";
import {Button, Card, Flex, Spacing, Text, TextInput, toast, useForm, useService, useStore} from "@code0-tech/pictor";
import {useParams} from "next/navigation";
-import {NamespaceService} from "@edition/namespace/Namespace.service";
-import {OrganizationService} from "@edition/organization/Organization.service";
+import {NamespaceService} from "@edition/namespace/services/Namespace.service";
+import {OrganizationService} from "@edition/organization/services/Organization.service";
import {Namespace} from "@code0-tech/sagittarius-graphql-types";
export const OrganizationGeneralSettingsView: React.FC = () => {
@@ -55,23 +55,24 @@ export const OrganizationGeneralSettingsView: React.FC = () => {
})
return <>
-
- General adjustments
+
+
+ General
+
+
+ Save changes
+
-
+
- Name
+
+ Name
+
-
-
-
- Update Organization
-
-
>
}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/organization/OrganizationUpgradeView.tsx b/src/packages/ce/src/organization/views/OrganizationUpgradeView.tsx
similarity index 68%
rename from src/packages/ce/src/dashboard/organization/OrganizationUpgradeView.tsx
rename to src/packages/ce/src/organization/views/OrganizationUpgradeView.tsx
index 8f4f84f8..80121499 100644
--- a/src/packages/ce/src/dashboard/organization/OrganizationUpgradeView.tsx
+++ b/src/packages/ce/src/organization/views/OrganizationUpgradeView.tsx
@@ -21,8 +21,8 @@ export const OrganizationUpgradeView: React.FC = () => {
const [value, setValue] = React.useState("annually")
return <>
- Upgrade to team
-
+ Upgrade to Team
+
Upgrade your organization to the Team plan to unlock advanced features, enhanced collaboration tools, and
priority support. Elevate your team's productivity and take advantage of exclusive benefits designed to help
@@ -46,21 +46,18 @@ export const OrganizationUpgradeView: React.FC = () => {
-
-
- Team self-hosted
-
-
- 7,95€
- per member / month billed monthly
-
-
- Get started now
-
+ Team self-hosted
+
+
+ 7,95€
+ per member / month billed monthly
+
+
+ Get started now
@@ -88,26 +85,24 @@ export const OrganizationUpgradeView: React.FC = () => {
-
-
- Team cloud
-
-
-
-
- 3,95€
-
- -27%
-
-
- per member / month
- billed annually
-
-
- Get started now
-
+
+ Team cloud
+
+
+
+
+ 3,95€
+
+ -27%
+
+
+ per member / month
+ billed annually
+
+
+ Get started now
@@ -136,16 +131,14 @@ export const OrganizationUpgradeView: React.FC = () => {
-
- Team cloud (pay as you go)
-
-
- 3,95€
- per member / month billed monthly
-
-
- Get started now
-
+ Team cloud (pay as you go)
+
+
+ 3,95€
+ per member / month billed monthly
+
+
+ Get started now
diff --git a/src/packages/ce/src/organization/views/OrganizationsTopView.tsx b/src/packages/ce/src/organization/views/OrganizationsTopView.tsx
new file mode 100644
index 00000000..60a36763
--- /dev/null
+++ b/src/packages/ce/src/organization/views/OrganizationsTopView.tsx
@@ -0,0 +1,31 @@
+"use client"
+
+import {Flex, Spacing, Text} from "@code0-tech/pictor";
+import React from "react";
+import {useRouter} from "next/navigation";
+import {OrganizationDataTableComponent} from "@edition/organization/components/OrganizationDataTableComponent";
+
+export const OrganizationsTopView = () => {
+
+ const router = useRouter()
+
+ return <>
+
+
+ Top organizations
+
+
+ Manage organizations that you belong to. You can create new organizations and switch between them.
+
+
+
+ {/**TODO: use namespaceId*/}
+ index < 5} onSelect={(organization) => {
+ const number = organization?.namespace?.id?.match(/Namespace\/(\d+)$/)?.[1]
+ router.push(`/namespace/${number}`)
+ }}/>
+ >
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/organization/views/OrganizationsView.tsx b/src/packages/ce/src/organization/views/OrganizationsView.tsx
new file mode 100644
index 00000000..03e6c5d1
--- /dev/null
+++ b/src/packages/ce/src/organization/views/OrganizationsView.tsx
@@ -0,0 +1,122 @@
+"use client"
+
+import {
+ Button,
+ Flex,
+ Menu,
+ MenuCheckboxItem,
+ MenuContent,
+ MenuPortal,
+ MenuTrigger,
+ Spacing,
+ Text
+} from "@code0-tech/pictor";
+import Link from "next/link";
+import React from "react";
+import {useRouter} from "next/navigation";
+import {OrganizationDataTableComponent} from "@edition/organization/components/OrganizationDataTableComponent";
+import {
+ OrganizationDataTableFilterInputComponent
+} from "@edition/organization/components/OrganizationDataTableFilterInputComponent";
+import {DataTableFilterProps, DataTableSortProps} from "@code0-tech/pictor/dist/components/data-table/DataTable";
+import {ButtonGroup} from "@code0-tech/pictor/dist/components/button-group/ButtonGroup";
+import {IconMinus, IconSortAscending, IconSortDescending} from "@tabler/icons-react";
+
+export const OrganizationsView = () => {
+
+ const router = useRouter()
+
+ const [filter, setFilter] = React.useState({})
+ const [sort, setSort] = React.useState({})
+
+ return <>
+
+
+
+ Organizations
+
+
+ Manage organizations that you belong to. You can create new organizations and switch between them.
+
+
+
+
+ Create
+
+
+
+ Sort
+
+
+
+ {
+ if (sort["name"] === undefined)
+ setSort(prev => ({...prev, name: "asc"}))
+ else if (sort["name"] === "asc")
+ setSort(prev => ({...prev, name: "desc"}))
+ else
+ setSort(prev => ({...prev, name: undefined}))
+
+ event.preventDefault()
+ event.stopPropagation()
+ }}>
+ {sort["name"] === undefined ? : sort["name"] === "asc" ?
+ : }
+ Name
+
+ {
+ if (sort["createdAt"] === undefined)
+ setSort(prev => ({...prev, createdAt: "asc"}))
+ else if (sort["createdAt"] === "asc")
+ setSort(prev => ({...prev, createdAt: "desc"}))
+ else
+ setSort(prev => ({...prev, createdAt: undefined}))
+
+ event.preventDefault()
+ event.stopPropagation()
+ }}>
+ {sort["createdAt"] === undefined ?
+ : sort["createdAt"] === "asc" ?
+ : }
+ Created At
+
+ {
+ if (sort["updatedAt"] === undefined)
+ setSort(prev => ({...prev, updatedAt: "asc"}))
+ else if (sort["updatedAt"] === "asc")
+ setSort(prev => ({...prev, updatedAt: "desc"}))
+ else
+ setSort(prev => ({...prev, updatedAt: undefined}))
+
+ event.preventDefault()
+ event.stopPropagation()
+ }}>
+ {sort["updatedAt"] === undefined ?
+ : sort["updatedAt"] === "asc" ?
+ : }
+ Updated At
+
+
+
+
+
+
+
+
+ setFilter(filter)}/>
+
+
+ {/**TODO: use namespaceId*/}
+ {
+ const number = organization?.namespace?.id?.match(/Namespace\/(\d+)$/)?.[1]
+ router.push(`/namespace/${number}`)
+ }}/>
+ >
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/project/Project.service.ts b/src/packages/ce/src/project/Project.service.ts
deleted file mode 100644
index ab94b329..00000000
--- a/src/packages/ce/src/project/Project.service.ts
+++ /dev/null
@@ -1,95 +0,0 @@
-import {
- DNamespaceProjectReactiveService,
- DNamespaceProjectView,
- DProjectDependencies,
- ReactiveArrayStore
-} from "@code0-tech/pictor";
-import {
- Mutation,
- NamespaceProject,
- NamespacesProjectsAssignRuntimesInput,
- NamespacesProjectsAssignRuntimesPayload,
- NamespacesProjectsCreateInput,
- NamespacesProjectsCreatePayload,
- NamespacesProjectsDeleteInput,
- NamespacesProjectsDeletePayload,
- Query
-} from "@code0-tech/sagittarius-graphql-types";
-import {GraphqlClient} from "@core/util/graphql-client";
-import projectsQuery from "./queries/Projects.query.graphql"
-import projectCreateMutation from "./mutations/Project.create.mutation.graphql"
-
-export class ProjectService extends DNamespaceProjectReactiveService {
-
- private readonly client: GraphqlClient
- private i = 0;
-
- constructor(client: GraphqlClient, store: ReactiveArrayStore) {
- super(store);
- this.client = client
- }
-
- values(dependencies: DProjectDependencies): DNamespaceProjectView[] {
- const projects = super.values()
- if (!dependencies?.namespaceId) return projects
-
- const namespaceId = dependencies.namespaceId
- const filtered = projects.filter(p => p.namespace?.id === namespaceId)
-
- if (filtered.length === 0) {
- this.client.query({
- query: projectsQuery,
- variables: {
- namespaceId,
- firstProject: 50,
- afterProject: null,
- },
- }).then(res => {
- const nodes = res.data?.namespace?.projects?.nodes ?? []
- nodes.forEach(project => {
- if (
- project &&
- project.namespace?.id === namespaceId &&
- !this.hasById(project.id)
- ) {
- this.set(this.i++, new DNamespaceProjectView(project))
- }
- })
- })
- }
-
- return filtered
- }
-
- hasById(id: NamespaceProject["id"]): boolean {
- const project = super.values().find(p => p.id === id)
- return project !== undefined
- }
-
- async projectAssignRuntimes(payload: NamespacesProjectsAssignRuntimesInput): Promise {
- return Promise.resolve(undefined);
- }
-
- async projectCreate(payload: NamespacesProjectsCreateInput): Promise {
- const result = await this.client.mutate({
- mutation: projectCreateMutation,
- variables: {
- ...payload
- }
- })
-
- if (result.data && result.data.namespacesProjectsCreate && result.data.namespacesProjectsCreate.namespaceProject) {
- const project = result.data.namespacesProjectsCreate.namespaceProject
- if (!this.hasById(project.id)) {
- this.add(new DNamespaceProjectView(project))
- }
- }
-
- return result.data?.namespacesProjectsCreate ?? undefined
- }
-
- async projectDelete(payload: NamespacesProjectsDeleteInput): Promise {
- return Promise.resolve(undefined);
- }
-
-}
\ No newline at end of file
diff --git a/src/packages/ce/src/project/components/ProjectDataTableComponent.tsx b/src/packages/ce/src/project/components/ProjectDataTableComponent.tsx
new file mode 100644
index 00000000..78ce332b
--- /dev/null
+++ b/src/packages/ce/src/project/components/ProjectDataTableComponent.tsx
@@ -0,0 +1,38 @@
+import React from "react";
+import {DataTable, DataTableColumn, Text, useService, useStore} from "@code0-tech/pictor";
+import {DataTableFilterProps, DataTableSortProps} from "@code0-tech/pictor/dist/components/data-table/DataTable";
+import {Namespace, NamespaceProject} from "@code0-tech/sagittarius-graphql-types";
+import {ProjectService} from "@edition/project/services/Project.service";
+import {ProjectDataTableRowComponent} from "@edition/project/components/ProjectDataTableRowComponent";
+
+export interface ProjectDataTableComponentProps {
+ namespaceId: Namespace["id"]
+ sort?: DataTableSortProps
+ filter?: DataTableFilterProps
+ preFilter?: (project: NamespaceProject, index: number) => boolean
+ onSelect?: (item: NamespaceProject | undefined) => void
+}
+
+export const ProjectDataTableComponent: React.FC = (props) => {
+
+ const {namespaceId, sort, filter, preFilter = () => true, onSelect} = props
+
+ const projectService = useService(ProjectService)
+ const projectStore = useStore(ProjectService)
+ const projects = React.useMemo(() => projectService.values({namespaceId: namespaceId}), [projectStore, namespaceId])
+
+ return
+
+ No project found. Create one to get started.
+
+ }
+ onSelect={(item) => item && onSelect?.(item)}
+ data={projects.map(p => p.json()).filter(preFilter)}>
+ {(project, index) => {
+ return
+ }}
+
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/project/components/ProjectDataTableFilterInputComponent.tsx b/src/packages/ce/src/project/components/ProjectDataTableFilterInputComponent.tsx
new file mode 100644
index 00000000..ea640a80
--- /dev/null
+++ b/src/packages/ce/src/project/components/ProjectDataTableFilterInputComponent.tsx
@@ -0,0 +1,99 @@
+import React from "react";
+import {
+ DataTableFilterInput,
+ DataTableFilterSuggestionMenu,
+ MenuCheckboxItem,
+ useService,
+ useStore
+} from "@code0-tech/pictor";
+import {IconCheck} from "@tabler/icons-react";
+import {Namespace} from "@code0-tech/sagittarius-graphql-types";
+import {DataTableFilterProps} from "@code0-tech/pictor/dist/components/data-table/DataTable";
+import {ProjectService} from "@edition/project/services/Project.service";
+import {RuntimeService} from "@edition/runtime/services/Runtime.service";
+
+export interface ProjectDataTableFilterInputComponentProps {
+ namespaceId: Namespace['id']
+ onChange: (filter: DataTableFilterProps) => void
+}
+
+export const ProjectDataTableFilterInputComponent: React.FC = (props) => {
+
+ const {namespaceId, onChange} = props
+
+ const projectService = useService(ProjectService)
+ const projectStore = useStore(ProjectService)
+ const runtimeService = useService(RuntimeService)
+ const runtimeStore = useStore(RuntimeService)
+
+ const projects = React.useMemo(
+ () => projectService.values({namespaceId: namespaceId}),
+ [projectStore, namespaceId]
+ )
+
+ const allPrimaryRuntimes = React.useMemo(
+ () => {
+ const runtimeIds = projects.map(p => p.primaryRuntime?.id)
+ return runtimeIds.map(id => runtimeService.getById(id)).filter(Boolean)
+ },
+ [projects, runtimeStore]
+ )
+
+ return {
+
+ const split = currentValue.split(",").map(s => s.trim()).filter(Boolean)
+
+ return
+ {projects.map(project => {
+
+ const isChecked = split.includes(project?.name!)
+
+ return {
+ e.preventDefault();
+ e.stopPropagation();
+ const updated = isChecked
+ ? split.filter(name => name !== project?.name)
+ : [...split, project?.name];
+ applySuggestion(updated.join(","), true);
+ }}>
+ {isChecked ? : }
+ {project?.name}
+
+ })}
+
+ }
+ }, {
+ token: "Primary Runtime",
+ key: "primaryRuntime.name",
+ operators: ["isOneOf"],
+ suggestion: (context, operator, currentValue, applySuggestion) => {
+
+ const split = currentValue.split(",").map(s => s.trim()).filter(Boolean)
+
+ return
+ {allPrimaryRuntimes.map(runtime => {
+
+ const isChecked = split.includes(runtime?.name!)
+
+ return {
+ e.preventDefault();
+ e.stopPropagation();
+ const updated = isChecked
+ ? split.filter(name => name !== runtime?.name)
+ : [...split, runtime?.name];
+ applySuggestion(updated.join(","), true);
+ }}>
+ {isChecked ? : }
+ {runtime?.name}
+
+ })}
+
+ }
+ }
+ ]}/>
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/project/components/ProjectDataTableRowComponent.tsx b/src/packages/ce/src/project/components/ProjectDataTableRowComponent.tsx
new file mode 100644
index 00000000..a2ef403b
--- /dev/null
+++ b/src/packages/ce/src/project/components/ProjectDataTableRowComponent.tsx
@@ -0,0 +1,78 @@
+import React from "react";
+import {NamespaceProject} from "@code0-tech/sagittarius-graphql-types";
+import {Avatar, DataTableColumn, Flex, Text, useService, useStore, hashToColor} from "@code0-tech/pictor";
+import {ProjectService} from "@edition/project/services/Project.service";
+import {RuntimeService} from "@edition/runtime/services/Runtime.service";
+import {formatDistanceToNow} from "date-fns";
+
+export interface ProjectDataTableRowComponentProps {
+ projectId: NamespaceProject['id']
+}
+
+export const ProjectDataTableRowComponent: React.FC = (props) => {
+
+ const {projectId} = props
+
+ const projectService = useService(ProjectService)
+ const projectStore = useStore(ProjectService)
+ const runtimeService = useService(RuntimeService)
+ const runtimeStore = useStore(RuntimeService)
+
+ const project = React.useMemo(
+ () => projectService.getById(projectId),
+ [projectStore, projectId]
+ )
+
+ const runtimes = React.useMemo(
+ () => project?.runtimes?.nodes?.map(
+ runtime => runtimeService.getById(runtime?.id)
+ ),
+ [runtimeStore, project]
+ )
+
+ const primaryRuntime = React.useMemo(
+ () => runtimes?.find(
+ runtime => runtime?.id === project?.primaryRuntime?.id
+ ),
+ [runtimes, project]
+ )
+
+ return <>
+
+
+
+
+
+
+ {project?.name}
+
+
+ {project?.description}
+
+
+
+
+ Updated {formatDistanceToNow(project?.updatedAt!)} ago
+
+
+
+
+
+ {project?.flows?.count ? (
+
+ {project?.flows?.count ?? 0}{" "}
+ flows
+
+ ) : null}
+ {project?.runtimes?.count ? (
+
+ {project?.runtimes?.count ?? 0}{" "}
+ assigned runtimes
+
+ ) : null}
+
+
+ >
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/project/pages/ProjectSettingsPage.tsx b/src/packages/ce/src/project/pages/ProjectSettingsPage.tsx
new file mode 100644
index 00000000..edec0b74
--- /dev/null
+++ b/src/packages/ce/src/project/pages/ProjectSettingsPage.tsx
@@ -0,0 +1,59 @@
+import {Tab, TabList, TabTrigger} from "@code0-tech/pictor/dist/components/tab/Tab";
+
+"use clients"
+
+import React from "react";
+import {Button, DResizableHandle, DResizablePanel, DResizablePanelGroup, Flex, Text} from "@code0-tech/pictor";
+import {IconLayoutSidebar} from "@tabler/icons-react";
+import {ProjectSettingsGeneralView} from "@edition/project/views/ProjectSettingsGeneralView";
+import {ProjectSettingsRuntimesView} from "@edition/project/views/ProjectSettingsRuntimesView";
+import {ProjectSettingsDeleteView} from "@edition/project/views/ProjectSettingsDeleteView";
+
+export const ProjectSettingsPage: React.FC = () => {
+ return
+
+
+
+
+ Project settings
+
+
+
+
+
+
+ General settings and restrictions for your Sculptor application. These settings affect all users
+ and organizations within the application.
+
+
+
+
+ General
+
+
+
+
+ Runtimes
+
+
+
+
+ Delete
+
+
+
+
+
+
+
+ <>
+
+
+
+ >
+
+
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/project/services/Project.service.ts b/src/packages/ce/src/project/services/Project.service.ts
new file mode 100644
index 00000000..a3cb0668
--- /dev/null
+++ b/src/packages/ce/src/project/services/Project.service.ts
@@ -0,0 +1,161 @@
+import {
+ DNamespaceProjectReactiveService,
+ DNamespaceProjectView,
+ DProjectDependencies,
+ ReactiveArrayStore
+} from "@code0-tech/pictor";
+import {
+ Mutation,
+ NamespaceProject,
+ NamespacesProjectsAssignRuntimesInput,
+ NamespacesProjectsAssignRuntimesPayload,
+ NamespacesProjectsCreateInput,
+ NamespacesProjectsCreatePayload,
+ NamespacesProjectsDeleteInput,
+ NamespacesProjectsDeletePayload,
+ NamespacesProjectsUpdateInput,
+ NamespacesProjectsUpdatePayload,
+ Query
+} from "@code0-tech/sagittarius-graphql-types";
+import {GraphqlClient} from "@core/util/graphql-client";
+import projectsQuery from "./queries/Projects.query.graphql"
+import projectCreateMutation from "./mutations/Project.create.mutation.graphql"
+import projectDeleteMutation from "./mutations/Project.delete.mutation.graphql"
+import projectUpdateMutation from "./mutations/Project.update.mutation.graphql"
+import projectAssignRuntimesMutation from "./mutations/Project.assignRuntimes.mutation.graphql"
+import {View} from "@code0-tech/pictor/dist/utils/view";
+
+export class ProjectService extends DNamespaceProjectReactiveService {
+
+ private readonly client: GraphqlClient
+ private i = 0;
+
+ constructor(client: GraphqlClient, store: ReactiveArrayStore>) {
+ super(store);
+ this.client = client
+ }
+
+ values(dependencies: DProjectDependencies): DNamespaceProjectView[] {
+ const projects = super.values()
+ if (!dependencies?.namespaceId) return projects
+
+ const namespaceId = dependencies.namespaceId
+ const filtered = projects.filter(p => p.namespace?.id === namespaceId)
+
+ if (filtered.length === 0) {
+ this.client.query({
+ query: projectsQuery,
+ variables: {
+ namespaceId,
+ firstProject: 50,
+ afterProject: null,
+ },
+ }).then(res => {
+ const nodes = res.data?.namespace?.projects?.nodes ?? []
+ nodes.forEach(project => {
+ if (
+ project &&
+ project.namespace?.id === namespaceId &&
+ !this.hasById(project.id)
+ ) {
+ this.set(this.i++, new View(new DNamespaceProjectView(project)))
+ }
+ })
+ })
+ }
+
+ return filtered
+ }
+
+ hasById(id: NamespaceProject["id"]): boolean {
+ const project = super.values().find(p => p.id === id)
+ return project !== undefined
+ }
+
+ async projectAssignRuntimes(payload: NamespacesProjectsAssignRuntimesInput): Promise {
+ const result = await this.client.mutate({
+ mutation: projectAssignRuntimesMutation,
+ variables: {
+ ...payload
+ }
+ })
+
+ if (result.data && result.data.namespacesProjectsAssignRuntimes && result.data.namespacesProjectsAssignRuntimes.namespaceProject) {
+ const project = result.data.namespacesProjectsAssignRuntimes.namespaceProject
+ const index = this.values({namespaceId: project?.namespace?.id}).findIndex(o => o.id === project.id)
+ const projectStored = this.values({namespaceId: project?.namespace?.id}).find(o => o.id === project.id)
+ this.set(index, new View(new DNamespaceProjectView({
+ ...projectStored?.json(),
+ runtimes: {
+ count: payload.runtimeIds.length,
+ nodes: payload.runtimeIds.map(runtimeId => ({ id: runtimeId }))
+ }
+ })))
+
+ }
+
+ return result.data?.namespacesProjectsAssignRuntimes ?? undefined
+ }
+
+ async projectCreate(payload: NamespacesProjectsCreateInput): Promise {
+ const result = await this.client.mutate({
+ mutation: projectCreateMutation,
+ variables: {
+ ...payload
+ }
+ })
+
+ if (result.data && result.data.namespacesProjectsCreate && result.data.namespacesProjectsCreate.namespaceProject) {
+ const project = result.data.namespacesProjectsCreate.namespaceProject
+ if (!this.hasById(project.id)) {
+ this.add(new View(new DNamespaceProjectView(project)))
+ }
+ }
+
+ return result.data?.namespacesProjectsCreate ?? undefined
+ }
+
+ async projectDelete(payload: NamespacesProjectsDeleteInput): Promise {
+ const result = await this.client.mutate({
+ mutation: projectDeleteMutation,
+ variables: {
+ ...payload
+ }
+ })
+
+ if (result.data && result.data.namespacesProjectsDelete && result.data.namespacesProjectsDelete.namespaceProject) {
+ const project = result.data.namespacesProjectsDelete.namespaceProject
+ const index = this.values({namespaceId: project?.namespace?.id}).findIndex(o => o.id === project.id)
+ this.delete(index)
+
+ }
+
+ return result.data?.namespacesProjectsDelete ?? undefined
+ }
+
+ async projectUpdate(payload: NamespacesProjectsUpdateInput): Promise {
+ const result = await this.client.mutate({
+ mutation: projectUpdateMutation,
+ variables: {
+ ...payload
+ }
+ })
+
+ if (result.data && result.data.namespacesProjectsUpdate && result.data.namespacesProjectsUpdate.namespaceProject) {
+ const project = result.data.namespacesProjectsUpdate.namespaceProject
+ const index = this.values({namespaceId: project?.namespace?.id}).findIndex(o => o.id === project.id)
+ const projectStored = this.values({namespaceId: project?.namespace?.id}).find(o => o.id === project.id)
+ this.set(index, new View(new DNamespaceProjectView({
+ ...projectStored?.json(),
+ ...(payload.name ? {name: payload.name} : {}),
+ ...(payload.description ? {description: payload.description} : {}),
+ ...(payload.slug ? {slug: payload.slug} : {}),
+ ...(payload.primaryRuntimeId ? {primaryRuntime: {id: payload.primaryRuntimeId}} : {}),
+ })))
+
+ }
+
+ return result.data?.namespacesProjectsUpdate ?? undefined
+ }
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/project/fragments/Project.fragment.graphql b/src/packages/ce/src/project/services/fragments/Project.basic.fragment.graphql
similarity index 66%
rename from src/packages/ce/src/project/fragments/Project.fragment.graphql
rename to src/packages/ce/src/project/services/fragments/Project.basic.fragment.graphql
index 64fbbfc5..7cc10685 100644
--- a/src/packages/ce/src/project/fragments/Project.fragment.graphql
+++ b/src/packages/ce/src/project/services/fragments/Project.basic.fragment.graphql
@@ -1,5 +1,5 @@
-#import "../../runtime/fragments/Runtime.fragment.graphql"
-fragment Project on NamespaceProject {
+#import "@edition/runtime/services/fragments/Runtime.id.fragment.graphql"
+fragment BasicProject on NamespaceProject {
__typename
id
name
@@ -16,8 +16,12 @@ fragment Project on NamespaceProject {
count
}
primaryRuntime {
- ...Runtime
+ ...RuntimeId
}
+ roles {
+ count
+ }
+ slug
userAbilities {
assignProjectRuntimes
createFlow
diff --git a/src/packages/ce/src/project/services/fragments/Project.fragment.graphql b/src/packages/ce/src/project/services/fragments/Project.fragment.graphql
new file mode 100644
index 00000000..48911624
--- /dev/null
+++ b/src/packages/ce/src/project/services/fragments/Project.fragment.graphql
@@ -0,0 +1,65 @@
+#import "@edition/runtime/services/fragments/Runtime.id.fragment.graphql"
+fragment Project on NamespaceProject {
+ __typename
+ id
+ name
+ description
+ createdAt
+ updatedAt
+ slug
+ namespace {
+ __typename
+ id
+ }
+ flows {
+ __typename
+ count
+ nodes {
+ __typename
+ id
+ }
+ pageInfo {
+ __typename
+ endCursor
+ hasNextPage
+ }
+ }
+ runtimes {
+ __typename
+ count
+ nodes {
+ __typename
+ id
+ }
+ pageInfo {
+ __typename
+ endCursor
+ hasNextPage
+ }
+ }
+ roles {
+ __typename
+ count
+ nodes {
+ __typename
+ id
+ }
+ pageInfo {
+ __typename
+ endCursor
+ hasNextPage
+ }
+ }
+ primaryRuntime {
+ __typename
+ ...RuntimeId
+ name
+ }
+ userAbilities {
+ assignProjectRuntimes
+ createFlow
+ deleteNamespaceProject
+ updateNamespaceProject
+ __typename
+ }
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/project/fragments/Project.id.fragment.graphql b/src/packages/ce/src/project/services/fragments/Project.id.fragment.graphql
similarity index 100%
rename from src/packages/ce/src/project/fragments/Project.id.fragment.graphql
rename to src/packages/ce/src/project/services/fragments/Project.id.fragment.graphql
diff --git a/src/packages/ce/src/project/services/mutations/Project.assignRuntimes.mutation.graphql b/src/packages/ce/src/project/services/mutations/Project.assignRuntimes.mutation.graphql
new file mode 100644
index 00000000..2df58ad8
--- /dev/null
+++ b/src/packages/ce/src/project/services/mutations/Project.assignRuntimes.mutation.graphql
@@ -0,0 +1,20 @@
+#import "../fragments/Project.basic.fragment.graphql"
+mutation projectAssignRuntimes ($namespaceProjectId: NamespaceProjectID!, $runtimeIds: [RuntimeID!]!) {
+ namespacesProjectsAssignRuntimes(input: {
+ namespaceProjectId: $namespaceProjectId,
+ runtimeIds: $runtimeIds
+ }) {
+ errors {
+ ...on Error {
+ errorCode,
+ details {
+ ...on ActiveModelError { attribute type }
+ __typename
+ }
+ }
+ }
+ namespaceProject {
+ ...BasicProject
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/project/mutations/Project.create.mutation.graphql b/src/packages/ce/src/project/services/mutations/Project.create.mutation.graphql
similarity index 75%
rename from src/packages/ce/src/project/mutations/Project.create.mutation.graphql
rename to src/packages/ce/src/project/services/mutations/Project.create.mutation.graphql
index 51a9bbb7..42f16f9f 100644
--- a/src/packages/ce/src/project/mutations/Project.create.mutation.graphql
+++ b/src/packages/ce/src/project/services/mutations/Project.create.mutation.graphql
@@ -1,4 +1,4 @@
-#import "../fragments/Project.fragment.graphql"
+#import "../fragments/Project.basic.fragment.graphql"
mutation projectCreate ($name: String!, $description: String, $namespaceId: NamespaceID!) {
namespacesProjectsCreate(input: {
name: $name,
@@ -9,12 +9,13 @@ mutation projectCreate ($name: String!, $description: String, $namespaceId: Name
...on Error {
errorCode,
details {
+ ...on ActiveModelError { attribute type }
__typename
}
}
}
namespaceProject {
- ...Project
+ ...BasicProject
}
}
}
\ No newline at end of file
diff --git a/src/packages/ce/src/project/services/mutations/Project.delete.mutation.graphql b/src/packages/ce/src/project/services/mutations/Project.delete.mutation.graphql
new file mode 100644
index 00000000..6d7af7da
--- /dev/null
+++ b/src/packages/ce/src/project/services/mutations/Project.delete.mutation.graphql
@@ -0,0 +1,19 @@
+#import "../fragments/Project.basic.fragment.graphql"
+mutation projectDelete ($namespaceProjectId: NamespaceProjectID!) {
+ namespacesProjectsDelete(input: {
+ namespaceProjectId: $namespaceProjectId
+ }) {
+ errors {
+ ...on Error {
+ errorCode,
+ details {
+ ...on ActiveModelError { attribute type }
+ __typename
+ }
+ }
+ }
+ namespaceProject {
+ ...BasicProject
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/project/services/mutations/Project.update.mutation.graphql b/src/packages/ce/src/project/services/mutations/Project.update.mutation.graphql
new file mode 100644
index 00000000..cb4d2a13
--- /dev/null
+++ b/src/packages/ce/src/project/services/mutations/Project.update.mutation.graphql
@@ -0,0 +1,23 @@
+#import "../fragments/Project.basic.fragment.graphql"
+mutation projectUpdate ($namespaceProjectId: NamespaceProjectID!, $name: String, $description: String, $slug: String, $primaryRuntimeId: RuntimeID) {
+ namespacesProjectsUpdate(input: {
+ namespaceProjectId: $namespaceProjectId
+ name: $name
+ description: $description
+ slug: $slug
+ primaryRuntimeId: $primaryRuntimeId
+ }) {
+ errors {
+ ...on Error {
+ errorCode,
+ details {
+ ...on ActiveModelError { attribute type }
+ __typename
+ }
+ }
+ }
+ namespaceProject {
+ ...BasicProject
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/project/queries/Projects.query.graphql b/src/packages/ce/src/project/services/queries/Projects.query.graphql
similarity index 85%
rename from src/packages/ce/src/project/queries/Projects.query.graphql
rename to src/packages/ce/src/project/services/queries/Projects.query.graphql
index f938e1b2..e728f530 100644
--- a/src/packages/ce/src/project/queries/Projects.query.graphql
+++ b/src/packages/ce/src/project/services/queries/Projects.query.graphql
@@ -8,11 +8,14 @@ query Projects (
) {
namespace(id: $namespaceId) {
projects (first: $firstProject, after: $afterProject) {
+ __typename
count
nodes {
+ __typename
...Project
}
pageInfo {
+ __typename
endCursor
hasNextPage
}
diff --git a/src/packages/ce/src/project/views/PersonalProjectsView.tsx b/src/packages/ce/src/project/views/PersonalProjectsView.tsx
new file mode 100644
index 00000000..57b96325
--- /dev/null
+++ b/src/packages/ce/src/project/views/PersonalProjectsView.tsx
@@ -0,0 +1,138 @@
+import React from "react";
+import {
+ Button,
+ Flex,
+ Menu,
+ MenuCheckboxItem,
+ MenuContent,
+ MenuPortal,
+ MenuTrigger,
+ Spacing,
+ Text,
+ useService,
+ useStore,
+ useUserSession
+} from "@code0-tech/pictor";
+import {UserService} from "@edition/user/services/User.service";
+import Link from "next/link";
+import {useRouter} from "next/navigation";
+import {ProjectDataTableComponent} from "@edition/project/components/ProjectDataTableComponent";
+import {DataTableFilterProps, DataTableSortProps} from "@code0-tech/pictor/dist/components/data-table/DataTable";
+import {ProjectDataTableFilterInputComponent} from "@edition/project/components/ProjectDataTableFilterInputComponent";
+import {ButtonGroup} from "@code0-tech/pictor/dist/components/button-group/ButtonGroup";
+import {IconMinus, IconSortAscending, IconSortDescending} from "@tabler/icons-react";
+
+export const PersonalProjectsView: React.FC = () => {
+
+ const router = useRouter()
+ const userSession = useUserSession()
+ const userStore = useStore(UserService)
+ const userService = useService(UserService)
+
+ const currentUser = React.useMemo(() => userService.getById(userSession?.user?.id), [userStore, userSession])
+ const namespaceId = currentUser?.namespace?.id
+ const namespaceIndex = namespaceId?.match(/Namespace\/(\d+)$/)?.[1]
+
+ const [filter, setFilter] = React.useState({})
+ const [sort, setSort] = React.useState({})
+
+ const projectsList = React.useMemo(() => {
+ if (!currentUser || !currentUser.namespace) return null
+ return {
+ const number = project?.id?.match(/NamespaceProject\/(\d+)$/)?.[1]
+ router.push(`/namespace/${namespaceIndex}/project/${number}/flow`)
+ }} namespaceId={currentUser.namespace.id}/>
+
+ }, [currentUser, filter, sort])
+
+ return <>
+
+
+
+
+ Personal projects
+
+
+ Projects created in your personal namespace. You can also create organization projects if you are a
+ member of any organization.
+
+
+
+
+ Create
+
+
+
+ Sort
+
+
+
+ {
+ if (sort["name"] === undefined)
+ setSort(prev => ({...prev, name: "asc"}))
+ else if (sort["name"] === "asc")
+ setSort(prev => ({...prev, name: "desc"}))
+ else
+ setSort(prev => ({...prev, name: undefined}))
+
+ event.preventDefault()
+ event.stopPropagation()
+ }}>
+ {sort["name"] === undefined ? : sort["name"] === "asc" ?
+ : }
+ Name
+
+ {
+ if (sort["createdAt"] === undefined)
+ setSort(prev => ({...prev, createdAt: "asc"}))
+ else if (sort["createdAt"] === "asc")
+ setSort(prev => ({...prev, createdAt: "desc"}))
+ else
+ setSort(prev => ({...prev, createdAt: undefined}))
+
+ event.preventDefault()
+ event.stopPropagation()
+ }}>
+ {sort["createdAt"] === undefined ?
+ : sort["createdAt"] === "asc" ?
+ : }
+ Created At
+
+ {
+ if (sort["updatedAt"] === undefined)
+ setSort(prev => ({...prev, updatedAt: "asc"}))
+ else if (sort["updatedAt"] === "asc")
+ setSort(prev => ({...prev, updatedAt: "desc"}))
+ else
+ setSort(prev => ({...prev, updatedAt: undefined}))
+
+ event.preventDefault()
+ event.stopPropagation()
+ }}>
+ {sort["updatedAt"] === undefined ?
+ : sort["updatedAt"] === "asc" ?
+ : }
+ Updated At
+
+
+
+
+
+
+
+
+
+ {projectsList}
+
+ >
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/project/views/ProjectSettingsDeleteView.tsx b/src/packages/ce/src/project/views/ProjectSettingsDeleteView.tsx
new file mode 100644
index 00000000..092bb139
--- /dev/null
+++ b/src/packages/ce/src/project/views/ProjectSettingsDeleteView.tsx
@@ -0,0 +1,54 @@
+"use client"
+
+import React, {startTransition} from "react";
+import {Button, Card, Flex, Spacing, Text, toast, useService, useStore} from "@code0-tech/pictor";
+import {TabContent} from "@code0-tech/pictor/dist/components/tab/Tab";
+import {useParams, useRouter} from "next/navigation";
+import {ProjectService} from "@edition/project/services/Project.service";
+
+export const ProjectSettingsDeleteView: React.FC = () => {
+
+ const params = useParams()
+ const router = useRouter()
+ const projectService = useService(ProjectService)
+
+ const projectId = params.projectId as any as number
+ const namespaceId = params.namespaceId as any as number
+
+ const deleteProject = React.useCallback(() => {
+ startTransition(() => {
+ projectService.projectDelete({
+ namespaceProjectId: `gid://sagittarius/NamespaceProject/${projectId}`
+ }).then(payload => {
+ if ((payload?.errors?.length ?? 0) <= 0) {
+ toast({
+ title: "The project was successfully deleted.",
+ color: "success",
+ dismissible: true,
+ })
+ }
+ router.push(`/namespace/${namespaceId}`)
+ })
+ })
+ }, [projectId])
+
+
+ return
+
+ Delete
+
+
+
+
+
+
+ This will delete the project and cannot be undone.
+
+
+
+ Delete project
+
+
+
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/project/views/ProjectSettingsGeneralView.tsx b/src/packages/ce/src/project/views/ProjectSettingsGeneralView.tsx
new file mode 100644
index 00000000..63710b0a
--- /dev/null
+++ b/src/packages/ce/src/project/views/ProjectSettingsGeneralView.tsx
@@ -0,0 +1,105 @@
+"use client"
+
+import React, {startTransition} from "react";
+import {
+ Button,
+ Card,
+ Flex,
+ Spacing,
+ TabContent,
+ Text,
+ TextInput,
+ useForm,
+ useService,
+ useStore,
+ toast
+} from "@code0-tech/pictor";
+import CardSection from "@code0-tech/pictor/dist/components/card/CardSection";
+import {useParams} from "next/navigation";
+import {ProjectService} from "@edition/project/services/Project.service";
+
+export const ProjectSettingsGeneralView: React.FC = () => {
+
+ const params = useParams()
+ const projectService = useService(ProjectService)
+ const projectStore = useStore(ProjectService)
+
+ const projectId = params.projectId as any as number
+
+ const project = React.useMemo(
+ () => projectService.getById(`gid://sagittarius/NamespaceProject/${projectId}`),
+ [projectStore, projectId]
+ )
+
+ const initialValues = React.useMemo(
+ () => ({
+ name: project?.name,
+ description: project?.description,
+ slug: project?.slug,
+ }),
+ [project]
+ )
+
+ const [inputs, validate] = useForm({
+ initialValues: initialValues,
+ validate: {
+ name: (value) => {
+ if (!value) return "Name is required"
+ return null
+ },
+ slug: (value) => {
+ if (!value) return "Slug is required"
+ return null
+ }
+ },
+ onSubmit: (values) => {
+ startTransition(() => {
+ projectService.projectUpdate({
+ slug: values.slug,
+ name: values.name,
+ description: values.description,
+ namespaceProjectId: `gid://sagittarius/NamespaceProject/${projectId}`
+ }).then(payload => {
+ if ((payload?.errors?.length ?? 0) <= 0) {
+ toast({
+ title: "The project was successfully deleted.",
+ color: "success",
+ dismissible: true,
+ })
+ }
+ })
+ })
+ }
+ })
+
+ return
+
+ General
+
+ Save changes
+
+
+
+
+
+
+ Name
+
+
+
+
+
+ Description
+
+
+
+
+
+ Slug
+
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/project/views/ProjectSettingsRuntimesView.tsx b/src/packages/ce/src/project/views/ProjectSettingsRuntimesView.tsx
new file mode 100644
index 00000000..694a4ae1
--- /dev/null
+++ b/src/packages/ce/src/project/views/ProjectSettingsRuntimesView.tsx
@@ -0,0 +1,117 @@
+"use client"
+
+import React, {startTransition} from "react"
+import {
+ Avatar,
+ Button,
+ Flex,
+ hashToColor,
+ Menu,
+ MenuContent,
+ MenuItem,
+ MenuPortal,
+ MenuSeparator,
+ MenuTrigger,
+ Spacing,
+ Badge,
+ Text,
+ useService,
+ useStore
+} from "@code0-tech/pictor";
+import {TabContent} from "@code0-tech/pictor/dist/components/tab/Tab";
+import {useParams} from "next/navigation";
+import {RuntimeProjectDataTableComponent} from "@edition/runtime/components/RuntimeProjectDataTableComponent";
+import {RuntimeService} from "@edition/runtime/services/Runtime.service";
+import {ProjectService} from "@edition/project/services/Project.service";
+import {Runtime} from "@code0-tech/sagittarius-graphql-types";
+import {formatDistanceToNow} from "date-fns";
+
+export const ProjectSettingsRuntimesView: React.FC = () => {
+
+ const params = useParams()
+ const runtimeService = useService(RuntimeService)
+ const runtimeStore = useStore(RuntimeService)
+ const projectService = useService(ProjectService)
+ const projectStore = useStore(ProjectService)
+
+ const projectId = params.projectId as any as number
+ const namespaceId = params.namespaceId as any as number
+
+ const availableRuntimes = React.useMemo(
+ () => {
+ const globalRuntimes = runtimeService.values().filter(runtime => !runtime?.namespace?.id)
+ const namespaceRuntimes = runtimeService.values({namespaceId: `gid://sagittarius/Namespace/${namespaceId}`})
+ return [...globalRuntimes, ...namespaceRuntimes]
+ },
+ [runtimeStore, projectId]
+ )
+
+ const project = React.useMemo(
+ () => projectService.getById(`gid://sagittarius/NamespaceProject/${projectId}`),
+ [projectStore, projectId]
+ )
+
+ const assignRuntime = React.useCallback((runtimeId: Runtime['id']) => {
+ const runtimeIds = project?.runtimes?.nodes?.map(r => r?.id!) ?? []
+
+ if (!runtimeId) return
+ if (runtimeIds.includes(runtimeId)) return
+
+ startTransition(() => {
+ projectService.projectAssignRuntimes({
+ runtimeIds: [...runtimeIds, runtimeId],
+ namespaceProjectId: `gid://sagittarius/NamespaceProject/${projectId}`
+ })
+ })
+ }, [projectService, projectId, project])
+
+ return
+
+ Runtimes
+
+
+
+ Add runtime
+
+
+
+
+ {availableRuntimes.map((runtime, index) => {
+ return <>
+ assignRuntime(runtime.id)}>
+
+
+
+
+
+ {runtime?.name}
+
+
+
+ {runtime?.status}
+
+
+
+
+
+
+ Updated {formatDistanceToNow(runtime?.updatedAt!)} ago
+
+
+
+ {index < availableRuntimes.length - 1 ?
+ : null}
+ >
+ })}
+
+
+
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/project/views/ProjectsView.tsx b/src/packages/ce/src/project/views/ProjectsView.tsx
new file mode 100644
index 00000000..65c76a19
--- /dev/null
+++ b/src/packages/ce/src/project/views/ProjectsView.tsx
@@ -0,0 +1,132 @@
+"use client"
+
+
+import React from "react";
+import {
+ Button,
+ Flex,
+ Menu,
+ MenuCheckboxItem,
+ MenuContent,
+ MenuPortal,
+ MenuTrigger,
+ Spacing,
+ Text
+} from "@code0-tech/pictor";
+import Link from "next/link";
+import {useParams, useRouter} from "next/navigation";
+import {ProjectDataTableComponent} from "@edition/project/components/ProjectDataTableComponent";
+import {DataTableFilterProps, DataTableSortProps} from "@code0-tech/pictor/dist/components/data-table/DataTable";
+import {ButtonGroup} from "@code0-tech/pictor/dist/components/button-group/ButtonGroup";
+import {IconMinus, IconSortAscending, IconSortDescending} from "@tabler/icons-react";
+import {ProjectDataTableFilterInputComponent} from "@edition/project/components/ProjectDataTableFilterInputComponent";
+
+//TODO: user abilities for project creation within namespace
+export const ProjectsView: React.FC = () => {
+
+ const params = useParams()
+ const router = useRouter()
+ const namespaceId = params.namespaceId as any as number
+
+ const [filter, setFilter] = React.useState({})
+ const [sort, setSort] = React.useState({})
+
+ const projectsList = React.useMemo(() => {
+ return {
+ const number = project?.id?.match(/NamespaceProject\/(\d+)$/)?.[1]
+ router.push(`/namespace/${namespaceId}/project/${number}/flow`)
+ }}/>
+ }, [namespaceId, filter, sort])
+
+ return <>
+
+
+
+
+ Projects
+
+
+ Manage projects that you belong to. You can create new projects and switch between them.
+
+
+
+
+ Create
+
+
+
+ Sort
+
+
+
+ {
+ if (sort["name"] === undefined)
+ setSort(prev => ({...prev, name: "asc"}))
+ else if (sort["name"] === "asc")
+ setSort(prev => ({...prev, name: "desc"}))
+ else
+ setSort(prev => ({...prev, name: undefined}))
+
+ event.preventDefault()
+ event.stopPropagation()
+ }}>
+ {sort["name"] === undefined ? : sort["name"] === "asc" ?
+ : }
+ Name
+
+ {
+ if (sort["createdAt"] === undefined)
+ setSort(prev => ({...prev, createdAt: "asc"}))
+ else if (sort["createdAt"] === "asc")
+ setSort(prev => ({...prev, createdAt: "desc"}))
+ else
+ setSort(prev => ({...prev, createdAt: undefined}))
+
+ event.preventDefault()
+ event.stopPropagation()
+ }}>
+ {sort["createdAt"] === undefined ?
+ : sort["createdAt"] === "asc" ?
+ : }
+ Created At
+
+ {
+ if (sort["updatedAt"] === undefined)
+ setSort(prev => ({...prev, updatedAt: "asc"}))
+ else if (sort["updatedAt"] === "asc")
+ setSort(prev => ({...prev, updatedAt: "desc"}))
+ else
+ setSort(prev => ({...prev, updatedAt: undefined}))
+
+ event.preventDefault()
+ event.stopPropagation()
+ }}>
+ {sort["updatedAt"] === undefined ?
+ : sort["updatedAt"] === "asc" ?
+ : }
+ Updated At
+
+
+
+
+
+
+
+
+
+ {projectsList}
+
+ >
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/role/components/RoleDataTableComponent.tsx b/src/packages/ce/src/role/components/RoleDataTableComponent.tsx
new file mode 100644
index 00000000..4f2703dd
--- /dev/null
+++ b/src/packages/ce/src/role/components/RoleDataTableComponent.tsx
@@ -0,0 +1,44 @@
+import React from "react";
+import {DataTable, DataTableColumn, Text, useService, useStore} from "@code0-tech/pictor";
+import {DataTableFilterProps, DataTableSortProps} from "@code0-tech/pictor/dist/components/data-table/DataTable";
+import {Namespace, NamespaceProject, NamespaceRole} from "@code0-tech/sagittarius-graphql-types";
+import {ProjectService} from "@edition/project/services/Project.service";
+import {ProjectDataTableRowComponent} from "@edition/project/components/ProjectDataTableRowComponent";
+import {RoleService} from "@edition/role/services/Role.service";
+import {RoleDataTableRowComponent} from "@edition/role/components/RoleDataTableRowComponent";
+
+export interface ProjectDataTableComponentProps {
+ namespaceId: Namespace["id"]
+ sort?: DataTableSortProps
+ filter?: DataTableFilterProps
+ preFilter?: (project: NamespaceRole, index: number) => boolean
+ onSelect?: (item: NamespaceRole | undefined) => void
+}
+
+export const RoleDataTableComponent: React.FC = (props) => {
+
+ const {namespaceId, sort, filter, preFilter = () => true, onSelect} = props
+
+ const roleService = useService(RoleService)
+ const roleStore = useStore(RoleService)
+
+ const roles = React.useMemo(
+ () => roleService.values({namespaceId: namespaceId}),
+ [roleStore, namespaceId]
+ )
+
+ return
+
+ No role found. Create one to get started.
+
+ }
+ onSelect={(item) => item && onSelect?.(item)}
+ data={roles.map(r => r.json()).filter(preFilter)}>
+ {(role, index) => {
+ return
+ }}
+
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/role/components/RoleDataTableFilterInputComponent.tsx b/src/packages/ce/src/role/components/RoleDataTableFilterInputComponent.tsx
new file mode 100644
index 00000000..353a20c8
--- /dev/null
+++ b/src/packages/ce/src/role/components/RoleDataTableFilterInputComponent.tsx
@@ -0,0 +1,94 @@
+import React from "react";
+import {
+ DataTableFilterInput,
+ DataTableFilterSuggestionMenu,
+ MenuCheckboxItem,
+ useService,
+ useStore
+} from "@code0-tech/pictor";
+import {IconCheck} from "@tabler/icons-react";
+import {Namespace} from "@code0-tech/sagittarius-graphql-types";
+import {DataTableFilterProps} from "@code0-tech/pictor/dist/components/data-table/DataTable";
+import {RoleService} from "@edition/role/services/Role.service";
+
+export interface RoleDataTableFilterInputComponentProps {
+ namespaceId: Namespace['id']
+ onChange: (filter: DataTableFilterProps) => void
+}
+
+export const RoleDataTableFilterInputComponent: React.FC = (props) => {
+
+ const {namespaceId, onChange} = props
+
+ const roleService = useService(RoleService)
+ const roleStore = useStore(RoleService)
+
+ const roles = React.useMemo(
+ () => roleService.values({namespaceId: namespaceId}),
+ [roleStore, namespaceId]
+ )
+
+ const abilities = React.useMemo(() => {
+ const all = new Set()
+ roles.forEach(role => role.abilities?.forEach(ability => all.add(ability)))
+ return Array.from(all)
+ }, [roles])
+
+ return {
+
+ const split = currentValue.split(",").map(s => s.trim()).filter(Boolean)
+
+ return
+ {roles.map(role => {
+
+ const isChecked = split.includes(role?.name!)
+
+ return {
+ e.preventDefault();
+ e.stopPropagation();
+ const updated = isChecked
+ ? split.filter(name => name !== role?.name)
+ : [...split, role?.name];
+ applySuggestion(updated.join(","), true);
+ }}>
+ {isChecked ? : }
+ {role?.name}
+
+ })}
+
+ }
+ }, {
+ token: "Permissions",
+ key: "abilities",
+ operators: ["isOneOf"],
+ suggestion: (context, operator, currentValue, applySuggestion) => {
+
+ const split = currentValue.split(",").map(s => s.trim()).filter(Boolean)
+
+ return
+ {abilities.map(ability => {
+
+ const isChecked = split.includes(ability)
+
+ return {
+ e.preventDefault();
+ e.stopPropagation();
+ const updated = isChecked
+ ? split.filter(name => name !== ability)
+ : [...split, ability];
+ applySuggestion(updated.join(","), true);
+ }}>
+ {isChecked ? : }
+ {ability}
+
+ })}
+
+ }
+ }
+ ]}/>
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/role/components/RoleDataTableRowComponent.tsx b/src/packages/ce/src/role/components/RoleDataTableRowComponent.tsx
new file mode 100644
index 00000000..5a9a69b1
--- /dev/null
+++ b/src/packages/ce/src/role/components/RoleDataTableRowComponent.tsx
@@ -0,0 +1,47 @@
+import React from "react";
+import {NamespaceRole} from "@code0-tech/sagittarius-graphql-types";
+import {DataTableColumn, Flex, Text, useService, useStore} from "@code0-tech/pictor";
+import {formatDistanceToNow} from "date-fns";
+import {RoleService} from "@edition/role/services/Role.service";
+import {RolePermissionComponent} from "@edition/role/components/RolePermissionComponent";
+
+export interface ProjectDataTableRowComponentProps {
+ roleId: NamespaceRole['id']
+}
+
+export const RoleDataTableRowComponent: React.FC = (props) => {
+
+ const {roleId} = props
+
+ const roleService = useService(RoleService)
+ const roleStore = useStore(RoleService)
+
+ const role = React.useMemo(
+ () => roleService.getById(roleId),
+ [roleStore, roleId]
+ )
+
+ return <>
+
+
+
+
+ {role?.name}
+
+
+
+
+ Updated {formatDistanceToNow(role?.updatedAt!)} ago
+
+
+
+
+
+
+ {role?.assignedProjects?.count ?? 0}{" "}
+ assigned projects
+
+
+
+ >
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/role/components/RolePermissionComponent.tsx b/src/packages/ce/src/role/components/RolePermissionComponent.tsx
new file mode 100644
index 00000000..6131e221
--- /dev/null
+++ b/src/packages/ce/src/role/components/RolePermissionComponent.tsx
@@ -0,0 +1,179 @@
+import React from "react";
+import {IconCheck} from "@tabler/icons-react";
+import {Tooltip, TooltipArrow, TooltipContent, TooltipPortal, TooltipTrigger, Badge, Flex, Text} from "@code0-tech/pictor";
+
+type AbilityCategory =
+ | "members"
+ | "projects"
+ | "roles"
+ | "runtimes"
+ | "flows"
+ | "license"
+ | "organization"
+ | "admin"
+
+const abilityCategoryByAbility: Record = {
+ ASSIGN_MEMBER_ROLES: "members",
+ INVITE_MEMBER: "members",
+ DELETE_MEMBER: "members",
+
+ CREATE_NAMESPACE_PROJECT: "projects",
+ UPDATE_NAMESPACE_PROJECT: "projects",
+ DELETE_NAMESPACE_PROJECT: "projects",
+ ASSIGN_ROLE_PROJECTS: "projects",
+ READ_NAMESPACE_PROJECT: "projects",
+
+ CREATE_NAMESPACE_ROLE: "roles",
+ UPDATE_NAMESPACE_ROLE: "roles",
+ ASSIGN_ROLE_ABILITIES: "roles",
+
+ CREATE_RUNTIME: "runtimes",
+ UPDATE_RUNTIME: "runtimes",
+ DELETE_RUNTIME: "runtimes",
+ ROTATE_RUNTIME_TOKEN: "runtimes",
+ ASSIGN_PROJECT_RUNTIMES: "runtimes",
+
+ CREATE_FLOW: "flows",
+ UPDATE_FLOW: "flows",
+ DELETE_FLOW: "flows",
+
+ CREATE_NAMESPACE_LICENSE: "license",
+ READ_NAMESPACE_LICENSE: "license",
+ DELETE_NAMESPACE_LICENSE: "license",
+
+ UPDATE_ORGANIZATION: "organization",
+ DELETE_ORGANIZATION: "organization",
+
+ NAMESPACE_ADMINISTRATOR: "admin",
+}
+
+const abilityCategoryLabels: Record = {
+ members: "members",
+ projects: "projects",
+ roles: "roles",
+ runtimes: "runtimes",
+ flows: "flows",
+ license: "licenses",
+ organization: "the organization",
+ admin: "everything",
+}
+
+type AbilityAction = "create" | "update" | "delete" | "assign" | "read" | "manage"
+
+type AbilityCategoryInfo = {
+ category: AbilityCategory
+ label: string
+ abilities: string[]
+ action: AbilityAction
+}
+
+function getAbilityAction(ability: string): AbilityAction {
+ if (ability.startsWith("CREATE_")) return "create"
+ if (ability.startsWith("UPDATE_")) return "update"
+ if (ability.startsWith("DELETE_")) return "delete"
+ if (ability.startsWith("ASSIGN_")) return "assign"
+ if (ability.startsWith("READ_")) return "read"
+ return "manage"
+}
+
+function mergeAbilityActions(actions: AbilityAction[]): AbilityAction {
+ const uniqueActions = Array.from(new Set(actions))
+ if (uniqueActions.length === 1) return uniqueActions[0]
+ return "manage"
+}
+
+function buildAbilityCategories(abilities: string[] | undefined): AbilityCategoryInfo[] {
+ if (!abilities || abilities.length === 0) return []
+
+ const bucket = new Map()
+
+ for (const ability of abilities) {
+ const category = abilityCategoryByAbility[ability]
+ if (!category) continue
+
+ const list = bucket.get(category) ?? []
+ list.push(ability)
+ bucket.set(category, list)
+ }
+
+ return Array.from(bucket.entries()).map(([category, categoryAbilities]) => {
+ const actions = categoryAbilities.map(getAbilityAction)
+ const action = mergeAbilityActions(actions)
+
+ return {
+ category,
+ label: abilityCategoryLabels[category],
+ abilities: categoryAbilities,
+ action,
+ }
+ })
+}
+
+function renderPermissionSummary(categories: AbilityCategoryInfo[]) {
+ if (categories.length === 0) return "No special permissions."
+
+ const renderCategory = (category: AbilityCategoryInfo) => (
+ <>
+ {category.action}{" "}
+
+
+
+ {category.label}
+
+
+
+
+
+ {category.abilities.map(ability => (
+
+
+ {ability}
+
+ ))}
+
+
+
+
+
+ >
+ )
+
+ if (categories.length === 1) {
+ const category = categories[0]
+ return <>Can {renderCategory(category)}>
+ }
+
+ const leadingCategories = categories.slice(0, -1)
+ const lastCategory = categories[categories.length - 1]
+
+ return (
+ <>
+ Can{" "}
+ {leadingCategories.map((category, index) => (
+
+ {index > 0 && ", "}
+ {renderCategory(category)}
+
+ ))}
+ {" "}and {renderCategory(lastCategory)}
+ >
+ )
+}
+
+export interface RolePermissionComponentProps {
+ abilities?: string[]
+}
+
+export const RolePermissionComponent: React.FC = (props) => {
+ const {abilities} = props
+ const categories = React.useMemo(
+ () => buildAbilityCategories(abilities),
+ [abilities]
+ )
+
+ return (
+
+ {renderPermissionSummary(categories)}
+
+ )
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/role/pages/RoleCreatePage.tsx b/src/packages/ce/src/role/pages/RoleCreatePage.tsx
new file mode 100644
index 00000000..e529caa0
--- /dev/null
+++ b/src/packages/ce/src/role/pages/RoleCreatePage.tsx
@@ -0,0 +1,46 @@
+"use client"
+
+import React from "react";
+import {Button, Col, Flex, Spacing, Text, TextInput} from "@code0-tech/pictor";
+import Link from "next/link";
+
+export const RoleCreatePage: React.FC = () => {
+
+ //TODO: user abilities for add role within namespace
+
+ return
+
+
+
+ Create new role
+
+
+
+ Global runtimes are shared runtimes that can be used across multiple organizations.
+
+
+
+
+
+
+
+ Go back to roles
+
+
+
+ Create role
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/role/pages/RoleSettingsPage.tsx b/src/packages/ce/src/role/pages/RoleSettingsPage.tsx
new file mode 100644
index 00000000..feb1f344
--- /dev/null
+++ b/src/packages/ce/src/role/pages/RoleSettingsPage.tsx
@@ -0,0 +1,87 @@
+"use client"
+
+import React from "react";
+import {
+ Button,
+ DResizableHandle,
+ DResizablePanel,
+ DResizablePanelGroup,
+ Flex,
+ ScrollArea,
+ ScrollAreaScrollbar,
+ ScrollAreaThumb,
+ ScrollAreaViewport,
+ Text
+} from "@code0-tech/pictor";
+import {Tab, TabList, TabTrigger} from "@code0-tech/pictor/dist/components/tab/Tab";
+import {RoleProjectView} from "@edition/role/views/RoleProjectView";
+import {RolePermissionView} from "@edition/role/views/RolePermissionView";
+import {RoleGeneralAdjustmentView} from "@edition/role/views/RoleGeneralAdjustmentView";
+import {RoleDeleteView} from "@edition/role/views/RoleDeleteView";
+import {IconLayoutSidebar} from "@tabler/icons-react";
+
+export const RoleSettingsPage: React.FC = () => {
+
+ //TODO: limit tabs based on user abilities for roles
+
+ return <>
+
+
+
+
+
+ Role settings
+
+
+
+
+
+
+ General settings and restrictions for your Sculptor application. These settings affect all
+ users
+ and organizations within the application.
+
+
+
+
+ General
+
+
+
+
+ Permissions
+
+
+
+
+ Limit to projects
+
+
+
+
+ Delete role
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+}
\ No newline at end of file
diff --git a/src/packages/ce/src/dashboard/role/RolesPage.tsx b/src/packages/ce/src/role/pages/RolesPage.tsx
similarity index 64%
rename from src/packages/ce/src/dashboard/role/RolesPage.tsx
rename to src/packages/ce/src/role/pages/RolesPage.tsx
index 0d93b4b7..4e110b45 100644
--- a/src/packages/ce/src/dashboard/role/RolesPage.tsx
+++ b/src/packages/ce/src/role/pages/RolesPage.tsx
@@ -1,7 +1,7 @@
"use client"
import React from "react";
-import {RolesView} from "@edition/dashboard/role/RolesView";
+import {RolesView} from "@edition/role/views/RolesView";
export const RolesPage: React.FC = () => {
return
diff --git a/src/packages/ce/src/role/Role.service.ts b/src/packages/ce/src/role/services/Role.service.ts
similarity index 87%
rename from src/packages/ce/src/role/Role.service.ts
rename to src/packages/ce/src/role/services/Role.service.ts
index db23bca7..581c94ba 100644
--- a/src/packages/ce/src/role/Role.service.ts
+++ b/src/packages/ce/src/role/services/Role.service.ts
@@ -20,18 +20,19 @@ import {
Query
} from "@code0-tech/sagittarius-graphql-types"
import {GraphqlClient} from "@core/util/graphql-client";
-import rolesQuery from "@edition/role/queries/Roles.query.graphql";
-import roleUpdateMutation from "@edition/role/mutations/Role.update.mutation.graphql";
-import roleDeleteMutation from "@edition/role/mutations/Role.delete.mutation.graphql";
-import roleAssignAbilitiesMutation from "@edition/role/mutations/Role.assignAbilities.mutation.graphql";
-import roleAssignProjectsMutation from "@edition/role/mutations/Role.assignProjects.mutation.graphql";
+import rolesQuery from "@edition/role/services/queries/Roles.query.graphql";
+import roleUpdateMutation from "@edition/role/services/mutations/Role.update.mutation.graphql";
+import roleDeleteMutation from "@edition/role/services/mutations/Role.delete.mutation.graphql";
+import roleAssignAbilitiesMutation from "@edition/role/services/mutations/Role.assignAbilities.mutation.graphql";
+import roleAssignProjectsMutation from "@edition/role/services/mutations/Role.assignProjects.mutation.graphql";
+import {View} from "@code0-tech/pictor/dist/utils/view";
export class RoleService extends DNamespaceRoleReactiveService {
private readonly client: GraphqlClient
private i = 0;
- constructor(client: GraphqlClient, store: ReactiveArrayStore) {
+ constructor(client: GraphqlClient, store: ReactiveArrayStore>) {
super(store);
this.client = client
}
@@ -61,7 +62,7 @@ export class RoleService extends DNamespaceRoleReactiveService {
role.namespace?.id === namespaceId &&
!this.hasById(role.id)
) {
- this.set(this.i++, new DNamespaceRoleView(role))
+ this.set(this.i++, new View(new DNamespaceRoleView(role)))
}
})
})
@@ -93,7 +94,7 @@ export class RoleService extends DNamespaceRoleReactiveService {
abilities: payload.abilities
})
- this.set(index, newRole)
+ this.set(index, new View(newRole))
}
return result.data?.namespacesRolesAssignAbilities ?? undefined
@@ -122,7 +123,7 @@ export class RoleService extends DNamespaceRoleReactiveService {
}
})
- this.set(index, newRole)
+ this.set(index, new View(newRole))
}
return result.data?.namespacesRolesAssignProjects ?? undefined
@@ -146,7 +147,7 @@ export class RoleService extends DNamespaceRoleReactiveService {
name: payload.name
})
- this.set(index, newRole)
+ this.set(index, new View(newRole))
}
return result.data?.namespacesRolesUpdate ?? undefined
diff --git a/src/packages/ce/src/role/fragments/Role.basic.fragment.graphql b/src/packages/ce/src/role/services/fragments/Role.basic.fragment.graphql
similarity index 82%
rename from src/packages/ce/src/role/fragments/Role.basic.fragment.graphql
rename to src/packages/ce/src/role/services/fragments/Role.basic.fragment.graphql
index eb91d664..f03ce7ac 100644
--- a/src/packages/ce/src/role/fragments/Role.basic.fragment.graphql
+++ b/src/packages/ce/src/role/services/fragments/Role.basic.fragment.graphql
@@ -1,4 +1,4 @@
-#import "../../namespace/fragments/Namespace.id.fragment.graphql"
+#import "@edition/namespace/services/fragments/Namespace.id.fragment.graphql"
fragment RoleBasic on NamespaceRole {
__typename
id
diff --git a/src/packages/ce/src/role/fragments/Role.fragment.graphql b/src/packages/ce/src/role/services/fragments/Role.fragment.graphql
similarity index 77%
rename from src/packages/ce/src/role/fragments/Role.fragment.graphql
rename to src/packages/ce/src/role/services/fragments/Role.fragment.graphql
index 150f9d3f..177da517 100644
--- a/src/packages/ce/src/role/fragments/Role.fragment.graphql
+++ b/src/packages/ce/src/role/services/fragments/Role.fragment.graphql
@@ -1,5 +1,5 @@
-#import "../../namespace/fragments/Namespace.id.fragment.graphql"
-#import "../../project/fragments/Project.id.fragment.graphql"
+#import "@edition/namespace/services/fragments/Namespace.id.fragment.graphql"
+#import "@edition/project/services/fragments/Project.id.fragment.graphql"
fragment Role on NamespaceRole {
__typename
id
diff --git a/src/packages/ce/src/role/fragments/Role.id.fragment.graphql b/src/packages/ce/src/role/services/fragments/Role.id.fragment.graphql
similarity index 100%
rename from src/packages/ce/src/role/fragments/Role.id.fragment.graphql
rename to src/packages/ce/src/role/services/fragments/Role.id.fragment.graphql
diff --git a/src/packages/ce/src/role/mutations/Role.assignAbilities.mutation.graphql b/src/packages/ce/src/role/services/mutations/Role.assignAbilities.mutation.graphql
similarity index 100%
rename from src/packages/ce/src/role/mutations/Role.assignAbilities.mutation.graphql
rename to src/packages/ce/src/role/services/mutations/Role.assignAbilities.mutation.graphql
diff --git a/src/packages/ce/src/role/mutations/Role.assignProjects.mutation.graphql b/src/packages/ce/src/role/services/mutations/Role.assignProjects.mutation.graphql
similarity index 100%
rename from src/packages/ce/src/role/mutations/Role.assignProjects.mutation.graphql
rename to src/packages/ce/src/role/services/mutations/Role.assignProjects.mutation.graphql
diff --git a/src/packages/ce/src/role/mutations/Role.create.mutation.graphql b/src/packages/ce/src/role/services/mutations/Role.create.mutation.graphql
similarity index 100%
rename from src/packages/ce/src/role/mutations/Role.create.mutation.graphql
rename to src/packages/ce/src/role/services/mutations/Role.create.mutation.graphql
diff --git a/src/packages/ce/src/role/mutations/Role.delete.mutation.graphql b/src/packages/ce/src/role/services/mutations/Role.delete.mutation.graphql
similarity index 100%
rename from src/packages/ce/src/role/mutations/Role.delete.mutation.graphql
rename to src/packages/ce/src/role/services/mutations/Role.delete.mutation.graphql
diff --git a/src/packages/ce/src/role/mutations/Role.update.mutation.graphql b/src/packages/ce/src/role/services/mutations/Role.update.mutation.graphql
similarity index 100%
rename from src/packages/ce/src/role/mutations/Role.update.mutation.graphql
rename to src/packages/ce/src/role/services/mutations/Role.update.mutation.graphql
diff --git a/src/packages/ce/src/role/queries/Roles.query.graphql b/src/packages/ce/src/role/services/queries/Roles.query.graphql
similarity index 100%
rename from src/packages/ce/src/role/queries/Roles.query.graphql
rename to src/packages/ce/src/role/services/queries/Roles.query.graphql
diff --git a/src/packages/ce/src/dashboard/role/RoleDeleteView.tsx b/src/packages/ce/src/role/views/RoleDeleteView.tsx
similarity index 83%
rename from src/packages/ce/src/dashboard/role/RoleDeleteView.tsx
rename to src/packages/ce/src/role/views/RoleDeleteView.tsx
index 987c8d35..3b65d92b 100644
--- a/src/packages/ce/src/dashboard/role/RoleDeleteView.tsx
+++ b/src/packages/ce/src/role/views/RoleDeleteView.tsx
@@ -3,7 +3,7 @@
import React from "react";
import {useParams, useRouter} from "next/navigation";
import {Button, Card, Flex, Spacing, Text, toast, useService} from "@code0-tech/pictor";
-import {RoleService} from "@edition/role/Role.service";
+import {RoleService} from "@edition/role/services/Role.service";
import type {NamespaceRole} from "@code0-tech/sagittarius-graphql-types";
import {TabContent} from "@code0-tech/pictor/dist/components/tab/Tab";
@@ -36,20 +36,19 @@ export const RoleDeleteView: React.FC = () => {
}, [])
return
-
+
Delete role
- Delete role
-
+
This will delete the role and cannot be undone.
-
- Delete role forever
+
+ Delete role
diff --git a/src/packages/ce/src/dashboard/role/RoleGeneralAdjustmentView.tsx b/src/packages/ce/src/role/views/RoleGeneralAdjustmentView.tsx
similarity index 91%
rename from src/packages/ce/src/dashboard/role/RoleGeneralAdjustmentView.tsx
rename to src/packages/ce/src/role/views/RoleGeneralAdjustmentView.tsx
index 409fe687..4efafb52 100644
--- a/src/packages/ce/src/dashboard/role/RoleGeneralAdjustmentView.tsx
+++ b/src/packages/ce/src/role/views/RoleGeneralAdjustmentView.tsx
@@ -4,7 +4,7 @@ import React from "react";
import {Button, Card, Flex, Spacing, Text, TextInput, toast, useForm, useService, useStore} from "@code0-tech/pictor";
import CardSection from "@code0-tech/pictor/dist/components/card/CardSection";
import {TabContent} from "@code0-tech/pictor/dist/components/tab/Tab";
-import {RoleService} from "@edition/role/Role.service";
+import {RoleService} from "@edition/role/services/Role.service";
import type {Namespace, NamespaceRole} from "@code0-tech/sagittarius-graphql-types";
import {useParams} from "next/navigation";
@@ -58,15 +58,15 @@ export const RoleGeneralAdjustmentView: React.FC = () => {
return
-
- General adjustments
+
+ General
- Update general adjustments
+ Save changes
-
+
Name
diff --git a/src/packages/ce/src/dashboard/role/RolePermissionView.tsx b/src/packages/ce/src/role/views/RolePermissionView.tsx
similarity index 95%
rename from src/packages/ce/src/dashboard/role/RolePermissionView.tsx
rename to src/packages/ce/src/role/views/RolePermissionView.tsx
index a0e21c0a..2017bfcc 100644
--- a/src/packages/ce/src/dashboard/role/RolePermissionView.tsx
+++ b/src/packages/ce/src/role/views/RolePermissionView.tsx
@@ -21,7 +21,7 @@ import {DNamespaceRolePermissions} from "@code0-tech/pictor/dist/components/d-ro
import CardSection from "@code0-tech/pictor/dist/components/card/CardSection";
import {TabContent} from "@code0-tech/pictor/dist/components/tab/Tab";
import {useParams} from "next/navigation";
-import {RoleService} from "@edition/role/Role.service";
+import {RoleService} from "@edition/role/services/Role.service";
type Permission = {
label: string
@@ -226,9 +226,14 @@ export const RolePermissionView: React.FC = () => {
})
return
-