diff --git a/package-lock.json b/package-lock.json index 4383075e..507ee59a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@apollo/client": "^4.0.9", "@code0-tech/pictor": "^0.11.0", - "@code0-tech/triangulum": "^0.25.6", + "@code0-tech/triangulum": "^0.25.8", "@codemirror/lang-javascript": "^6.2.5", "@codemirror/lint": "^6.9.5", "@icons-pack/react-simple-icons": "^13.13.0", @@ -1946,9 +1946,9 @@ "peer": true }, "node_modules/@code0-tech/triangulum": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@code0-tech/triangulum/-/triangulum-0.25.6.tgz", - "integrity": "sha512-xCaaFafZyuGKYAzZ3J1W40FrTK0nKY+M25eVQQKEoxmJH5BN5LT6BYB8R3GbMESroGNKuEHibFcyLyzhqV2YLQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@code0-tech/triangulum/-/triangulum-0.25.8.tgz", + "integrity": "sha512-qEte2foHL78W0YW+4hVSjLtNgts1DnLXE6AMymypWspzzpKpzMLXZ0B2aRwBRaZL6yuh5GmG7nsG03Q9OAgxXg==", "peerDependencies": { "@code0-tech/sagittarius-graphql-types": "0.0.0-experimental-2616186698-14d97e4ac998067751f9cbd9f8fc05c6fed628fe", "@typescript/vfs": "^1.6.4", diff --git a/package.json b/package.json index f9d31dd4..162bbe07 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "dependencies": { "@apollo/client": "^4.0.9", "@code0-tech/pictor": "^0.11.0", - "@code0-tech/triangulum": "^0.25.6", + "@code0-tech/triangulum": "^0.25.8", "@codemirror/lang-javascript": "^6.2.5", "@codemirror/lint": "^6.9.5", "@icons-pack/react-simple-icons": "^13.13.0", diff --git a/src/app/(dashboard)/@bar/default.tsx b/src/app/(dashboard)/@bar/default.tsx index 11a620f6..2b1a1ac3 100644 --- a/src/app/(dashboard)/@bar/default.tsx +++ b/src/app/(dashboard)/@bar/default.tsx @@ -1,10 +1,5 @@ "use client"; -import React from "react"; import {ApplicationBarView} from "@edition/application/views/ApplicationBarView"; -const Page = () => { - return -} - -export default Page \ No newline at end of file +export default ApplicationBarView diff --git a/src/app/(dashboard)/@tab/default.ts b/src/app/(dashboard)/@tab/default.ts deleted file mode 100644 index ac3dd7f7..00000000 --- a/src/app/(dashboard)/@tab/default.ts +++ /dev/null @@ -1,5 +0,0 @@ -"use client" - -export default function Page() { - return null -} \ No newline at end of file diff --git a/src/app/(dashboard)/@tab/page.tsx b/src/app/(dashboard)/@tab/default.tsx similarity index 100% rename from src/app/(dashboard)/@tab/page.tsx rename to src/app/(dashboard)/@tab/default.tsx diff --git a/src/app/(dashboard)/@tab/licenses/add/default.tsx b/src/app/(dashboard)/@tab/licenses/add/default.tsx deleted file mode 100644 index ac3dd7f7..00000000 --- a/src/app/(dashboard)/@tab/licenses/add/default.tsx +++ /dev/null @@ -1,5 +0,0 @@ -"use client" - -export default function Page() { - return null -} \ No newline at end of file diff --git a/src/app/(dashboard)/@tab/namespace/[namespaceId]/licenses/default.tsx b/src/app/(dashboard)/@tab/namespace/[namespaceId]/licenses/default.tsx deleted file mode 100644 index 3881c19f..00000000 --- a/src/app/(dashboard)/@tab/namespace/[namespaceId]/licenses/default.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import {NamespaceTabView} from "@edition/namespace/views/NamespaceTabView"; - -export default function Page() { - return -} \ No newline at end of file diff --git a/src/app/(dashboard)/@tab/namespace/[namespaceId]/members/default.tsx b/src/app/(dashboard)/@tab/namespace/[namespaceId]/members/default.tsx deleted file mode 100644 index 3881c19f..00000000 --- a/src/app/(dashboard)/@tab/namespace/[namespaceId]/members/default.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import {NamespaceTabView} from "@edition/namespace/views/NamespaceTabView"; - -export default function Page() { - return -} \ No newline at end of file diff --git a/src/app/(dashboard)/@tab/namespace/[namespaceId]/page.tsx b/src/app/(dashboard)/@tab/namespace/[namespaceId]/page.tsx deleted file mode 100644 index 3881c19f..00000000 --- a/src/app/(dashboard)/@tab/namespace/[namespaceId]/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import {NamespaceTabView} from "@edition/namespace/views/NamespaceTabView"; - -export default function Page() { - return -} \ No newline at end of file diff --git a/src/app/(dashboard)/@tab/namespace/[namespaceId]/projects/default.tsx b/src/app/(dashboard)/@tab/namespace/[namespaceId]/projects/default.tsx deleted file mode 100644 index 3881c19f..00000000 --- a/src/app/(dashboard)/@tab/namespace/[namespaceId]/projects/default.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import {NamespaceTabView} from "@edition/namespace/views/NamespaceTabView"; - -export default function Page() { - return -} \ No newline at end of file diff --git a/src/app/(dashboard)/@tab/namespace/[namespaceId]/roles/default.tsx b/src/app/(dashboard)/@tab/namespace/[namespaceId]/roles/default.tsx deleted file mode 100644 index 3881c19f..00000000 --- a/src/app/(dashboard)/@tab/namespace/[namespaceId]/roles/default.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import {NamespaceTabView} from "@edition/namespace/views/NamespaceTabView"; - -export default function Page() { - return -} \ No newline at end of file diff --git a/src/app/(dashboard)/@tab/namespace/[namespaceId]/runtimes/default.tsx b/src/app/(dashboard)/@tab/namespace/[namespaceId]/runtimes/default.tsx deleted file mode 100644 index 3881c19f..00000000 --- a/src/app/(dashboard)/@tab/namespace/[namespaceId]/runtimes/default.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import {NamespaceTabView} from "@edition/namespace/views/NamespaceTabView"; - -export default function Page() { - return -} \ No newline at end of file diff --git a/src/app/(dashboard)/@tab/namespace/[namespaceId]/settings/default.tsx b/src/app/(dashboard)/@tab/namespace/[namespaceId]/settings/default.tsx deleted file mode 100644 index 3881c19f..00000000 --- a/src/app/(dashboard)/@tab/namespace/[namespaceId]/settings/default.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import {NamespaceTabView} from "@edition/namespace/views/NamespaceTabView"; - -export default function Page() { - return -} \ No newline at end of file diff --git a/src/app/(dashboard)/@tab/organizations/default.tsx b/src/app/(dashboard)/@tab/organizations/default.tsx deleted file mode 100644 index 5251309f..00000000 --- a/src/app/(dashboard)/@tab/organizations/default.tsx +++ /dev/null @@ -1,10 +0,0 @@ -"use client" - -import React from "react"; -import {ApplicationTabView} from "@edition/application/views/ApplicationTabView"; - -const Page: React.FC = () => { - return -} - -export default Page \ No newline at end of file diff --git a/src/app/(dashboard)/@tab/runtimes/default.tsx b/src/app/(dashboard)/@tab/runtimes/default.tsx deleted file mode 100644 index 5251309f..00000000 --- a/src/app/(dashboard)/@tab/runtimes/default.tsx +++ /dev/null @@ -1,10 +0,0 @@ -"use client" - -import React from "react"; -import {ApplicationTabView} from "@edition/application/views/ApplicationTabView"; - -const Page: React.FC = () => { - return -} - -export default Page \ No newline at end of file diff --git a/src/app/(dashboard)/@tab/settings/page.tsx b/src/app/(dashboard)/@tab/settings/page.tsx deleted file mode 100644 index 5251309f..00000000 --- a/src/app/(dashboard)/@tab/settings/page.tsx +++ /dev/null @@ -1,10 +0,0 @@ -"use client" - -import React from "react"; -import {ApplicationTabView} from "@edition/application/views/ApplicationTabView"; - -const Page: React.FC = () => { - return -} - -export default Page \ No newline at end of file diff --git a/src/app/(dashboard)/@tab/users/default.tsx b/src/app/(dashboard)/@tab/users/default.tsx deleted file mode 100644 index 1be75b18..00000000 --- a/src/app/(dashboard)/@tab/users/default.tsx +++ /dev/null @@ -1,10 +0,0 @@ -"use client" - -import React from "react"; -import {ApplicationTabView} from "@edition/application/views/ApplicationTabView"; - -const Default: React.FC = () => { - return -} - -export default Default \ No newline at end of file diff --git a/src/app/(dashboard)/layout.tsx b/src/app/(dashboard)/layout.tsx index a671d72d..714ecb0d 100644 --- a/src/app/(dashboard)/layout.tsx +++ b/src/app/(dashboard)/layout.tsx @@ -82,7 +82,7 @@ const ApplicationLayout: React.FC = ({children, bar, tab {tab} }> - {bar}}> + {bar}}> <>{children} diff --git a/src/app/(dashboard)/namespace/[namespaceId]/page.tsx b/src/app/(dashboard)/namespace/[namespaceId]/page.tsx index dbfc7343..7e9c8ab8 100644 --- a/src/app/(dashboard)/namespace/[namespaceId]/page.tsx +++ b/src/app/(dashboard)/namespace/[namespaceId]/page.tsx @@ -2,6 +2,4 @@ import {NamespaceOverviewPage} from "@edition/namespace/pages/NamespaceOverviewPage"; -export default function Page() { - return -} \ No newline at end of file +export default NamespaceOverviewPage \ No newline at end of file diff --git a/src/app/(dashboard)/namespace/[namespaceId]/projects/page.tsx b/src/app/(dashboard)/namespace/[namespaceId]/projects/page.tsx deleted file mode 100644 index b6743871..00000000 --- a/src/app/(dashboard)/namespace/[namespaceId]/projects/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import {NamespaceProjectsPage} from "@edition/namespace/pages/NamespaceProjectsPage"; - -export default function Page() { - return -} \ No newline at end of file diff --git a/src/app/(dashboard)/organizations/page.tsx b/src/app/(dashboard)/organizations/page.tsx deleted file mode 100644 index e49f829e..00000000 --- a/src/app/(dashboard)/organizations/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -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 7b4dd440..c749a281 100644 --- a/src/app/(dashboard)/page.tsx +++ b/src/app/(dashboard)/page.tsx @@ -1,8 +1,5 @@ "use client" -import React from "react"; import {ApplicationPage} from "@edition/application/pages/ApplicationPage"; -export default function Page() { - return -} +export default ApplicationPage diff --git a/src/app/(dashboard)/organizations/create/page.tsx b/src/app/(dashboard)/workspaces/create/page.tsx similarity index 100% rename from src/app/(dashboard)/organizations/create/page.tsx rename to src/app/(dashboard)/workspaces/create/page.tsx diff --git a/src/app/(dashboard)/workspaces/page.tsx b/src/app/(dashboard)/workspaces/page.tsx new file mode 100644 index 00000000..9b5c4961 --- /dev/null +++ b/src/app/(dashboard)/workspaces/page.tsx @@ -0,0 +1,5 @@ +"use client" + +import {NamespacesPage} from "@edition/namespace/pages/NamespacesPage"; + +export default NamespacesPage diff --git a/src/app/(flow)/@bar/default.tsx b/src/app/(flow)/@bar/default.tsx index 11a620f6..2722f04c 100644 --- a/src/app/(flow)/@bar/default.tsx +++ b/src/app/(flow)/@bar/default.tsx @@ -1,10 +1,5 @@ "use client"; -import React from "react"; import {ApplicationBarView} from "@edition/application/views/ApplicationBarView"; -const Page = () => { - return -} - -export default Page \ No newline at end of file +export default ApplicationBarView \ No newline at end of file diff --git a/src/app/(flow)/@tab/default.tsx b/src/app/(flow)/@tab/default.tsx index ac3dd7f7..0e93b0db 100644 --- a/src/app/(flow)/@tab/default.tsx +++ b/src/app/(flow)/@tab/default.tsx @@ -1,5 +1,5 @@ "use client" -export default function Page() { - return null -} \ No newline at end of file +import {ApplicationTabView} from "@edition/application/views/ApplicationTabView"; + +export default ApplicationTabView diff --git a/src/app/(flow)/@tab/namespace/[namespaceId]/project/[projectId]/flow/[flowId]/page.tsx b/src/app/(flow)/@tab/namespace/[namespaceId]/project/[projectId]/flow/[flowId]/page.tsx deleted file mode 100644 index 5b941806..00000000 --- a/src/app/(flow)/@tab/namespace/[namespaceId]/project/[projectId]/flow/[flowId]/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -"use client" - -import {ProjectTabView} from "@edition/project/views/ProjectTabView"; - -export default ProjectTabView \ No newline at end of file diff --git a/src/app/(flow)/@tab/namespace/[namespaceId]/project/[projectId]/flow/page.tsx b/src/app/(flow)/@tab/namespace/[namespaceId]/project/[projectId]/flow/page.tsx deleted file mode 100644 index 5b941806..00000000 --- a/src/app/(flow)/@tab/namespace/[namespaceId]/project/[projectId]/flow/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -"use client" - -import {ProjectTabView} from "@edition/project/views/ProjectTabView"; - -export default ProjectTabView \ No newline at end of file diff --git a/src/app/(flow)/@tab/namespace/[namespaceId]/project/[projectId]/module/[moduleId]/page.tsx b/src/app/(flow)/@tab/namespace/[namespaceId]/project/[projectId]/module/[moduleId]/page.tsx deleted file mode 100644 index ee84a50d..00000000 --- a/src/app/(flow)/@tab/namespace/[namespaceId]/project/[projectId]/module/[moduleId]/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -"use client" - -import {ProjectTabView} from "@ce-internal/project/views/ProjectTabView"; - -export default ProjectTabView \ No newline at end of file diff --git a/src/app/(flow)/@tab/namespace/[namespaceId]/project/[projectId]/module/page.tsx b/src/app/(flow)/@tab/namespace/[namespaceId]/project/[projectId]/module/page.tsx deleted file mode 100644 index ee84a50d..00000000 --- a/src/app/(flow)/@tab/namespace/[namespaceId]/project/[projectId]/module/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -"use client" - -import {ProjectTabView} from "@ce-internal/project/views/ProjectTabView"; - -export default ProjectTabView \ No newline at end of file diff --git a/src/app/(flow)/@tab/namespace/[namespaceId]/project/[projectId]/page.tsx b/src/app/(flow)/@tab/namespace/[namespaceId]/project/[projectId]/page.tsx deleted file mode 100644 index 5b941806..00000000 --- a/src/app/(flow)/@tab/namespace/[namespaceId]/project/[projectId]/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -"use client" - -import {ProjectTabView} from "@edition/project/views/ProjectTabView"; - -export default ProjectTabView \ No newline at end of file diff --git a/src/app/(flow)/@tab/namespace/[namespaceId]/project/[projectId]/runtime/page.tsx b/src/app/(flow)/@tab/namespace/[namespaceId]/project/[projectId]/runtime/page.tsx deleted file mode 100644 index 5b941806..00000000 --- a/src/app/(flow)/@tab/namespace/[namespaceId]/project/[projectId]/runtime/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -"use client" - -import {ProjectTabView} from "@edition/project/views/ProjectTabView"; - -export default ProjectTabView \ No newline at end of file diff --git a/src/app/(flow)/@tab/namespace/[namespaceId]/project/[projectId]/settings/page.tsx b/src/app/(flow)/@tab/namespace/[namespaceId]/project/[projectId]/settings/page.tsx deleted file mode 100644 index 5b941806..00000000 --- a/src/app/(flow)/@tab/namespace/[namespaceId]/project/[projectId]/settings/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -"use client" - -import {ProjectTabView} from "@edition/project/views/ProjectTabView"; - -export default ProjectTabView \ No newline at end of file diff --git a/src/packages/ce/src/application/pages/ApplicationPage.tsx b/src/packages/ce/src/application/pages/ApplicationPage.tsx index 6ff6f60f..14af16d7 100644 --- a/src/packages/ce/src/application/pages/ApplicationPage.tsx +++ b/src/packages/ce/src/application/pages/ApplicationPage.tsx @@ -1,9 +1,10 @@ import {PersonalProjectsView} from "@edition/project/views/PersonalProjectsView"; -import {OrganizationsView} from "@edition/organization/views/OrganizationsView"; import { AuroraBackground, Avatar, Badge, + Button, + ButtonGroup, Card, Col, Flex, @@ -18,68 +19,286 @@ import { useStore } from "@code0-tech/pictor"; import {UserService} from "@edition/user/services/User.service"; +import {NamespaceService} from "@edition/namespace/services/Namespace.service"; +import {OrganizationService} from "@edition/organization/services/Organization.service"; +import {OrganizationView} from "@edition/organization/services/Organization.view"; +import {ProjectService} from "@edition/project/services/Project.service"; +import {Namespace, Organization} from "@code0-tech/sagittarius-graphql-types"; import React from "react"; -import {OrganizationsTopView} from "@edition/organization/views/OrganizationsTopView"; +import {NamespacesTopView} from "@edition/namespace/views/NamespacesTopView"; import {useUserSession} from "@edition/user/hooks/User.session.hook"; +import {IconArrowUpRight, IconMail, IconSparkles, IconTrendingUp, IconUser} from "@tabler/icons-react"; +import {Layout} from "@code0-tech/pictor/dist/components/layout/Layout"; +import Link from "next/link"; + +const withinDays = (time: string | null | undefined, days: number): boolean => + !!time && Date.now() - new Date(time).getTime() < days * 24 * 60 * 60 * 1000 export const ApplicationPage = () => { const userStore = useStore(UserService) const userService = useService(UserService) + const namespaceService = useService(NamespaceService) + const namespaceStore = useStore(NamespaceService) + const organizationService = useService(OrganizationService) + const organizationStore = useStore(OrganizationService) + const projectService = useService(ProjectService) + const projectStore = useStore(ProjectService) const userSession = useUserSession() const currentUser = React.useMemo(() => userService.getById(userSession?.user?.id), [userStore, userSession]) + const namespaceIndex = currentUser?.namespace?.id?.match(/Namespace\/(\d+)$/)?.[1] + + const memberships = React.useMemo( + () => currentUser?.namespaceMemberships?.nodes ?? [], + [currentUser?.namespaceMemberships?.nodes?.length] + ) + + const namespaces = React.useMemo( + () => memberships + .map(membership => namespaceService.getById(membership?.namespace?.id)) + .filter((namespace): namespace is Namespace => !!namespace), + [memberships.length, namespaceStore] + ) + + const organizationNamespaces = React.useMemo( + () => namespaces.filter(namespace => namespace.parent?.__typename === "Organization"), + [namespaces] + ) + + const organizations = React.useMemo( + () => organizationNamespaces + .map(namespace => organizationService.getById((namespace.parent as Organization).id)) + .filter((organization): organization is OrganizationView => !!organization), + [organizationNamespaces, organizationStore] + ) + + const projects = React.useMemo( + () => namespaces.flatMap(namespace => projectService.values({namespaceId: namespace.id})), + [namespaces, projectStore] + ) + + const ownedOrganizationsCount = organizations.filter(organization => organization.userAbilities?.deleteOrganization).length + const memberOrganizationsCount = organizationNamespaces.length - ownedOrganizationsCount + const newOrganizationsLastMonth = organizations.filter(organization => withinDays(organization.createdAt, 30)).length - return
- - - - -
- - - 👋 - -
-
- - Hi, @{currentUser?.username ?? "user"}, - - let's write some flows -
+ const totalProjectsCount = namespaces.reduce((sum, namespace) => sum + (namespace.projects?.count ?? 0), 0) + const personalProjectsCount = namespaces.find(namespace => namespace.id === currentUser?.namespace?.id)?.projects?.count ?? 0 + const organizationProjectsCount = totalProjectsCount - personalProjectsCount + const newProjectsLastWeek = projects.filter(project => withinDays(project.createdAt, 7)).length + + const totalFlowsCount = projects.reduce((sum, project) => sum + (project.flows?.count ?? 0), 0) + const newFlowsLastMonth = projects + .flatMap(project => project.flows?.nodes ?? []) + .filter(flow => withinDays(flow?.createdAt, 30)).length + + const leftContent = + + +
+ + + 👋 + +
+ + {currentUser?.firstname} {currentUser?.lastname} + + + + + @{currentUser?.username} + + + + {currentUser?.email} + + + + BASIC + - - - - -
- -
- -
- - - - - - -
- -
- - - -
- - -
+ + + + + + + + + return +
+ + + + Overview + + + + Track the current state of your organizations, projects and flows at a single glance
+ and keep an eye on how your activity has developed across every area over the recent + weeks +
+ + + + + + + Organizations + + {newOrganizationsLastMonth > 0 && + + +{newOrganizationsLastMonth} last month + } + + + + + + + {organizationNamespaces.length} + + + You are the owner of {ownedOrganizationsCount} organizations
and a + member of {memberOrganizationsCount} +
+
+
+
+ + + + + + Projects + + {newProjectsLastWeek > 0 && + + +{newProjectsLastWeek} last week + } + + + + + + + {totalProjectsCount} + + + {personalProjectsCount} in your personal workspace
and + {" "}{organizationProjectsCount} in your organizations +
+
+
+
+ + + + + + Flows + + {newFlowsLastMonth > 0 && + + +{newFlowsLastMonth} last month + } + + + + + + + {totalFlowsCount} + + + Spread across {totalProjectsCount} projects
and + {" "}{namespaces.length} workspaces +
+
+
+
+ +
+ + + Your work + + + + A quick summary of the personal projects you own and the organizations you belong to
+ so you can jump straight back into your recent work or start something completely + new +
+ + + + + + + Personal projects + + + + + + + + + + + + + + + + + + + + + + Top workspaces + + + + + + + + + + + + + + + + + +
+ + + +
+
+
} \ No newline at end of file diff --git a/src/packages/ce/src/application/views/ApplicationBarView.tsx b/src/packages/ce/src/application/views/ApplicationBarView.tsx index fc6f0a17..45c7547f 100644 --- a/src/packages/ce/src/application/views/ApplicationBarView.tsx +++ b/src/packages/ce/src/application/views/ApplicationBarView.tsx @@ -2,22 +2,39 @@ import {Button, Flex, MenuItem, MenuSeparator, Text, useService, useStore} from "@code0-tech/pictor"; import {UserService} from "@edition/user/services/User.service"; -import {useParams, useRouter} from "next/navigation"; +import {useParams, usePathname, useRouter} from "next/navigation"; import React from "react"; import Link from "next/link"; -import {IconBuilding, IconFolders, IconInbox, IconLogout, IconSearch, IconUser} from "@tabler/icons-react"; +import { + IconArrowLeft, + IconArrowUpRight, + IconBuilding, + IconFolders, + IconInbox, + IconLogout, + IconSearch, + IconUser +} from "@tabler/icons-react"; import {ApplicationBreadcrumbView} from "@edition/application/views/ApplicationBreadcrumbView"; import UserMenuComponent from "@edition/user/components/UserMenuComponent"; import {useUserSession} from "@edition/user/hooks/User.session.hook"; import {Island} from "@code0-tech/pictor/dist/components/island/Island"; import {ButtonGroup} from "@code0-tech/pictor/dist/components/button-group/ButtonGroup"; +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 ApplicationBarView: React.FC = () => { const currentSession = useUserSession() const userService = useService(UserService) const userStore = useStore(UserService) + const namespaceService = useService(NamespaceService) + const namespaceStore = useStore(NamespaceService) + const organizationService = useService(OrganizationService) + const organizationStore = useStore(OrganizationService) const router = useRouter() + const pathname = usePathname() const [loading, startTransition] = React.useTransition() const params = useParams() @@ -28,6 +45,94 @@ export const ApplicationBarView: React.FC = () => { const userIndex = currentUser?.id?.match(/User\/(\d+)$/)?.[1] const currentStep = projectIndex ? "project" : namespaceIndex ? "namespace" : "home"; + const namespaceId: Namespace['id'] = `gid://sagittarius/Namespace/${namespaceIndex as any as number}` + 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]) + + const homeTabs = React.useMemo(() => { + return [ + , + , + ...(currentUser?.admin ? [ + , + , + + ] : []) + ] + }, [currentUser, pathname]) + + const namespaceTabs = React.useMemo(() => { + const baseLink = `/namespace/${namespaceIndex}` + const showSettings = (parentOrganization && (parentOrganization.userAbilities?.deleteOrganization + || parentOrganization?.userAbilities?.updateOrganization)) + || namespace?.userAbilities?.createLicense + //TODO add license check for enterprise features + + return [ + , + , + , + , + ...(showSettings ? [ + + ] : []) + ] + }, [namespace, parentOrganization, namespaceIndex, pathname]) + + const projectTabs = React.useMemo(() => { + const baseLink = `/namespace/${namespaceIndex}/project/${projectIndex}` + + return [ + , + , + , + + ] + }, [namespaceIndex, projectIndex, pathname]) + const userMenu = React.useMemo(() => { if (!currentUser || !currentSession) { @@ -52,7 +157,7 @@ export const ApplicationBarView: React.FC = () => { - + Organizations @@ -70,31 +175,23 @@ export const ApplicationBarView: React.FC = () => { }, [currentUser, currentSession, userNamespaceIndex]) return - + {currentStep === "project" ? + + : currentStep === "namespace" ? + + :
} - - - {namespaceIndex ? ( - - ) : (null as any)} - {namespaceIndex && projectIndex ? ( - - ) : (null as any)} + + {currentStep === "project" ? projectTabs : currentStep === "namespace" ? namespaceTabs : homeTabs} - @@ -107,4 +204,4 @@ export const ApplicationBarView: React.FC = () => { {userMenu} -} \ No newline at end of file +} diff --git a/src/packages/ce/src/application/views/ApplicationTabView.tsx b/src/packages/ce/src/application/views/ApplicationTabView.tsx index a607f704..e0397fc9 100644 --- a/src/packages/ce/src/application/views/ApplicationTabView.tsx +++ b/src/packages/ce/src/application/views/ApplicationTabView.tsx @@ -1,72 +1,21 @@ "use client" import React from "react"; -import {Tab, TabList, TabTrigger} from "@code0-tech/pictor/dist/components/tab/Tab"; -import {Button, useService, useStore} 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/services/User.service"; -import {RuntimeService} from "@edition/runtime/services/Runtime.service"; -import {OrganizationService} from "@edition/organization/services/Organization.service"; -import {useUserSession} from "@edition/user/hooks/User.session.hook"; +import {Button} from "@code0-tech/pictor"; +import {IconBuilding, IconHome} from "@tabler/icons-react"; +import {useRouter} from "next/navigation"; export const ApplicationTabView: React.FC = () => { - const pathname = usePathname() const router = useRouter() - const userService = useService(UserService) - const runtimeService = useService(RuntimeService) - const runtimeStore = useStore(RuntimeService) - const organizationService = useService(OrganizationService) - const organizationStore = useStore(OrganizationService) - const userStore = useStore(UserService) - const currentSession = useUserSession() - const currentUser = React.useMemo(() => userService.getById(currentSession?.user?.id), [userStore, currentSession]) - const defaultValue = pathname.includes("organizations") ? "organizations" - : pathname.includes("users") ? "users" - : pathname.includes("settings") ? "settings" - : pathname.includes("runtimes") ? "runtimes" - : "overview" - const adminLinks = React.useMemo(() => { - return currentUser && currentUser.admin ? ( - <> - - - - - - - - - - - - ) : null - }, [currentUser, runtimeStore, organizationStore]) - - return - - - - - - - - {adminLinks} - - -} \ No newline at end of file + return <> + + + +} diff --git a/src/packages/ce/src/module/pages/ModulesPage.tsx b/src/packages/ce/src/module/pages/ModulesPage.tsx index 8ac6b205..78ddb000 100644 --- a/src/packages/ce/src/module/pages/ModulesPage.tsx +++ b/src/packages/ce/src/module/pages/ModulesPage.tsx @@ -8,10 +8,11 @@ import { Button, Card, Col, - DataTableFilterInput, Flex, hashToColor, Row, + ScrollArea, ScrollAreaScrollbar, ScrollAreaThumb, + ScrollAreaViewport, Spacing, Text, useService, @@ -60,100 +61,113 @@ export const ModulesPage: React.FC = () => { borderTopLeftRadius: "1rem", borderTopRightRadius: "1rem" }}> - - - Installed plugins on {project?.name} - - - - - - { - modules.map(module => { + + + + + Installed plugins on {project?.name} + + + + + + { + modules.map(module => { - const DisplayIcon = icon(module.icon as IconString) + const DisplayIcon = icon(module.icon as IconString) - return - - - - - - - - - - - {module.names?.[0].content} - - - {module.flowTypes?.count ? `${module.flowTypes.count} flow types` : null} - {module.flowTypes?.count && module.functionDefinitions?.count ? " and " : null} - {module.functionDefinitions?.count ? `${module.functionDefinitions.count} functions` : null} - + return + + + + + + + + + + + {module.names?.[0].content} + + + {module.flowTypes?.count ? `${module.flowTypes.count} flow types` : null} + {module.flowTypes?.count && module.functionDefinitions?.count ? " and " : null} + {module.functionDefinitions?.count ? `${module.functionDefinitions.count} functions` : null} + + + + + + installed + + + {module.descriptions?.[0]?.content}
+ This plugin was developed by + + @{module.author} + +
+
+
+ + + + - - - installed - -
- - {module.descriptions?.[0]?.content}
- This plugin was developed by - - @{module.author} - -
-
-
- - - - - - + +
+ + }) + } + + + Are you missing a plugin? + + + Create a issue on our github repository or join our discord to request a plugin + + + + + - }) - } - - - Are you missing a plugin? - - - Create a issue on our github repository or join our discord to request a plugin - - - - - - - -
+
+ + + + +
diff --git a/src/packages/ce/src/namespace/components/NamespaceDataTableComponent.tsx b/src/packages/ce/src/namespace/components/NamespaceDataTableComponent.tsx new file mode 100644 index 00000000..f5637caa --- /dev/null +++ b/src/packages/ce/src/namespace/components/NamespaceDataTableComponent.tsx @@ -0,0 +1,81 @@ +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} from "@code0-tech/sagittarius-graphql-types"; +import {NamespaceDataTableRowComponent} from "@edition/namespace/components/NamespaceDataTableRowComponent"; +import {NamespaceService} from "@edition/namespace/services/Namespace.service"; +import {OrganizationService} from "@edition/organization/services/Organization.service"; +import {MemberService} from "@edition/member/services/Member.service"; +import {UserService} from "@edition/user/services/User.service"; +import {useUserSession} from "@edition/user/hooks/User.session.hook"; +import {getNamespaceName} from "@edition/namespace/util/Namespace.name.util"; + +export type NamespaceRow = Namespace & { name?: string } + +export interface NamespaceDataTableComponentProps { + sort?: DataTableSortProps + filter?: DataTableFilterProps + preFilter?: (namespace: Namespace, index: number) => boolean + onSelect?: (item: Namespace | undefined) => void + minimized?: boolean +} + +export const NamespaceDataTableComponent: React.FC = (props) => { + + const {sort, filter, preFilter = () => true, onSelect, minimized} = props + + const namespaceService = useService(NamespaceService) + const namespaceStore = useStore(NamespaceService) + const organizationService = useService(OrganizationService) + const organizationStore = useStore(OrganizationService) + 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, userService, currentSession?.user?.id] + ) + + const memberships = React.useMemo( + () => currentUser?.namespaceMemberships?.nodes ?? [], + [currentUser?.namespaceMemberships?.nodes?.length] + ) + + const data = React.useMemo( + () => memberships.map(membership => { + const namespace = namespaceService.getById(membership?.namespace?.id) + if (!namespace) return undefined + + const members = memberService.values({namespaceId: namespace.id}) + const row: NamespaceRow = { + ...namespace, + name: getNamespaceName(namespace, organizationService, userService), + members: { + ...namespace.members, + nodes: members, + } + } + return row + }).filter((namespace): namespace is NamespaceRow => !!namespace).filter(preFilter), + [memberships.length, namespaceStore, memberStore, organizationStore, userStore, preFilter] + ) + + return + + No workspace found. Create one to get started. + + } + onSelect={(item) => item && onSelect?.(item)} + data={data}> + {(namespace, index) => { + return + }} + + +} diff --git a/src/packages/ce/src/namespace/components/NamespaceDataTableFilterInputComponent.tsx b/src/packages/ce/src/namespace/components/NamespaceDataTableFilterInputComponent.tsx new file mode 100644 index 00000000..b0f48bf9 --- /dev/null +++ b/src/packages/ce/src/namespace/components/NamespaceDataTableFilterInputComponent.tsx @@ -0,0 +1,140 @@ +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 {NamespaceService} from "@edition/namespace/services/Namespace.service"; +import {OrganizationService} from "@edition/organization/services/Organization.service"; +import {MemberService} from "@edition/member/services/Member.service"; +import {UserService} from "@edition/user/services/User.service"; +import {useUserSession} from "@edition/user/hooks/User.session.hook"; +import {getNamespaceName} from "@edition/namespace/util/Namespace.name.util"; + +export interface NamespaceDataTableFilterInputComponentProps { + onChange: (filter: DataTableFilterProps) => void +} + +export const NamespaceDataTableFilterInputComponent: React.FC = (props) => { + + const {onChange} = props + + const namespaceService = useService(NamespaceService) + const namespaceStore = useStore(NamespaceService) + const organizationService = useService(OrganizationService) + const organizationStore = useStore(OrganizationService) + 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 memberships = React.useMemo( + () => currentUser?.namespaceMemberships?.nodes ?? [], + [currentUser] + ) + + const namespaces = React.useMemo( + () => memberships.map( + membership => namespaceService.getById(membership?.namespace?.id) + ).filter(namespace => !!namespace), + [namespaceStore, memberships] + ) + + const members = React.useMemo( + () => { + // A user can be a member of several namespaces; keep one entry per username + // since the suggestions filter on the username. + const seen = new Set() + return namespaces.map( + namespace => memberService.values({namespaceId: namespace?.id}) + ).flat().filter(member => { + const username = member?.user?.username + if (!username || seen.has(username)) return false + seen.add(username) + return true + }) + }, + [namespaces, memberStore] + ) + + // The editor inside DataTableFilterInput builds its suggestion extension once on + // mount, so the suggestion callbacks would otherwise close over the empty arrays + // of the first render. Refs let them read the latest data at invocation time. + const namespacesRef = React.useRef(namespaces) + const membersRef = React.useRef(members) + + React.useEffect(() => { + namespacesRef.current = namespaces + membersRef.current = members + }, [namespaces, members]) + + return { + + const split = currentValue.split(",").map(s => s.trim()).filter(Boolean) + + return + {namespacesRef.current.map(namespace => { + + const name = getNamespaceName(namespace, organizationService, userService) + const isChecked = split.includes(name!) + + return { + e.preventDefault(); + e.stopPropagation(); + const updated = isChecked + ? split.filter(n => n !== name) + : [...split, name]; + applySuggestion(updated.join(","), true); + }}> + {isChecked ? : } + {name} + + })} + + } + }, { + token: "Member", + key: "members.nodes.user.username", + operators: ["isOneOf"], + suggestion: (context, operator, currentValue, applySuggestion) => { + + const split = currentValue.split(",").map(s => s.trim()).filter(Boolean) + + return + {membersRef.current.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} + + })} + + } + } + ]}/> +} diff --git a/src/packages/ce/src/namespace/components/NamespaceDataTableRowComponent.tsx b/src/packages/ce/src/namespace/components/NamespaceDataTableRowComponent.tsx new file mode 100644 index 00000000..5b816af7 --- /dev/null +++ b/src/packages/ce/src/namespace/components/NamespaceDataTableRowComponent.tsx @@ -0,0 +1,115 @@ +import React from "react"; +import {Namespace} from "@code0-tech/sagittarius-graphql-types"; +import {Avatar, Button, DataTableColumn, Flex, hashToColor, Text, useService, useStore} from "@code0-tech/pictor"; +import {IconLogout} from "@tabler/icons-react"; +import {NamespaceService} from "@edition/namespace/services/Namespace.service"; +import {OrganizationService} from "@edition/organization/services/Organization.service"; +import {MemberService} from "@edition/member/services/Member.service"; +import {UserService} from "@edition/user/services/User.service"; +import {formatDistanceToNow} from "date-fns"; +import {useUserSession} from "@edition/user/hooks/User.session.hook"; +import {getNamespaceName} from "@edition/namespace/util/Namespace.name.util"; + +export interface NamespaceDataTableRowComponentProps { + namespaceId: Namespace['id'] + onLeave?: (namespace: Namespace) => void + minimized?: boolean +} + +export const NamespaceDataTableRowComponent: React.FC = (props) => { + + const {namespaceId, onLeave, minimized} = props + + const namespaceService = useService(NamespaceService) + const namespaceStore = useStore(NamespaceService) + const organizationService = useService(OrganizationService) + const organizationStore = useStore(OrganizationService) + 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 namespace = React.useMemo( + () => namespaceService.getById(namespaceId), + [namespaceStore, namespaceId] + ) + + const name = React.useMemo( + () => getNamespaceName(namespace, organizationService, userService), + [namespace, organizationStore, userStore] + ) + + const namespaceMember = React.useMemo( + () => namespace && currentUser ? memberService.getByNamespaceIdAndUserId(namespace.id, currentUser.id) : null, + [memberStore, namespace, currentUser] + ) + + return <> + + + + + + + {name} + + + + {!minimized && ( + + Updated {formatDistanceToNow(namespace?.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 ? ( + + + + ) : null} + + )} + + +} diff --git a/src/packages/ce/src/namespace/pages/NamespaceOverviewPage.tsx b/src/packages/ce/src/namespace/pages/NamespaceOverviewPage.tsx index e2cf02a7..c50a3bd4 100644 --- a/src/packages/ce/src/namespace/pages/NamespaceOverviewPage.tsx +++ b/src/packages/ce/src/namespace/pages/NamespaceOverviewPage.tsx @@ -21,7 +21,7 @@ export const NamespaceOverviewPage: React.FC = () => { const namespaceIndexCurrentUser = currentUser?.namespace?.id?.match(/Namespace\/(\d+)$/)?.[1] const namespaceId = params.namespaceId as any as string - return : }>
{ + + + const userStore = useStore(UserService) + const userService = useService(UserService) + + const userSession = useUserSession() + const currentUser = React.useMemo(() => userService.getById(userSession?.user?.id), [userStore, userSession]) + + const leftContent = + + +
+ + + 👋 + +
+ + {currentUser?.firstname} {currentUser?.lastname} + + + + + @{currentUser?.username} + + + + {currentUser?.email} + + + + BASIC + + + + + + +
+
+
+ + return +
+ + + + + + + + +
+
+ +} diff --git a/src/packages/ce/src/namespace/util/Namespace.name.util.ts b/src/packages/ce/src/namespace/util/Namespace.name.util.ts new file mode 100644 index 00000000..16bcc663 --- /dev/null +++ b/src/packages/ce/src/namespace/util/Namespace.name.util.ts @@ -0,0 +1,21 @@ +import {Namespace} from "@code0-tech/sagittarius-graphql-types"; +import {OrganizationService} from "@edition/organization/services/Organization.service"; +import {UserService} from "@edition/user/services/User.service"; + +/** + * A namespace has no own name. Its display name is derived from its parent: + * the organization name or the username of the user it belongs to. + */ +export const getNamespaceName = ( + namespace: Namespace | undefined | null, + organizationService: OrganizationService, + userService: UserService +): string | undefined => { + if (!namespace?.parent) return undefined + if (namespace.parent.__typename === "Organization") + return organizationService.getById(namespace.parent.id)?.name ?? undefined + if (namespace.parent.__typename === "User") + return userService.getById(namespace.parent.id)?.username ? + `@${userService.getById(namespace.parent.id)?.username}'s workspace` : undefined + return undefined +} diff --git a/src/packages/ce/src/namespace/views/NamespaceOverviewOrganizationLeftView.tsx b/src/packages/ce/src/namespace/views/NamespaceOverviewOrganizationLeftView.tsx index 3aa6c1e3..2a643c17 100644 --- a/src/packages/ce/src/namespace/views/NamespaceOverviewOrganizationLeftView.tsx +++ b/src/packages/ce/src/namespace/views/NamespaceOverviewOrganizationLeftView.tsx @@ -63,9 +63,11 @@ export const NamespaceOverviewOrganizationLeftView: React.FC = () => { {parentOrganization?.name} - + + + - diff --git a/src/packages/ce/src/namespace/views/NamespaceTabView.tsx b/src/packages/ce/src/namespace/views/NamespaceTabView.tsx deleted file mode 100644 index 81af8d27..00000000 --- a/src/packages/ce/src/namespace/views/NamespaceTabView.tsx +++ /dev/null @@ -1,79 +0,0 @@ -"use client" - -import React from "react"; -import {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"; - -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 ( - (parentOrganization && (parentOrganization.userAbilities?.deleteOrganization - || parentOrganization?.userAbilities?.updateOrganization)) - || namespace?.userAbilities?.createLicense - //TODO add license check for enterprise features - ) ? ( - - - - ) : null - }, [namespace, parentOrganization]) - - return - - - - - - - - - - - - - - - - - {settings} - - -} \ No newline at end of file diff --git a/src/packages/ce/src/namespace/views/NamespacesTopView.tsx b/src/packages/ce/src/namespace/views/NamespacesTopView.tsx new file mode 100644 index 00000000..e6b84f14 --- /dev/null +++ b/src/packages/ce/src/namespace/views/NamespacesTopView.tsx @@ -0,0 +1,20 @@ +"use client" + +import React from "react"; +import {useRouter} from "next/navigation"; +import {NamespaceDataTableComponent} from "@edition/namespace/components/NamespaceDataTableComponent"; + +export const NamespacesTopView = () => { + + const router = useRouter() + + return <> + index < 5} onSelect={(namespace) => { + const number = namespace?.id?.match(/Namespace\/(\d+)$/)?.[1] + router.push(`/namespace/${number}`) + }}/> + + +} diff --git a/src/packages/ce/src/namespace/views/NamespacesView.tsx b/src/packages/ce/src/namespace/views/NamespacesView.tsx new file mode 100644 index 00000000..c39b3329 --- /dev/null +++ b/src/packages/ce/src/namespace/views/NamespacesView.tsx @@ -0,0 +1,124 @@ +"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 {NamespaceDataTableComponent} from "@edition/namespace/components/NamespaceDataTableComponent"; +import { + NamespaceDataTableFilterInputComponent +} from "@edition/namespace/components/NamespaceDataTableFilterInputComponent"; +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 NamespacesView = () => { + + const router = useRouter() + + const [filter, setFilter] = React.useState({}) + const [sort, setSort] = React.useState({}) + + return <> + +
+ + Workspaces + + + + Workspaces bring your organizations, projects and flows together in one shared place
+ so you and your team can create, organize and manage everything from a single central + view +
+
+ + + + + + + + + + + { + 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)}/> +
+ + { + const number = namespace?.id?.match(/Namespace\/(\d+)$/)?.[1] + router.push(`/namespace/${number}`) + }}/> + + +} diff --git a/src/packages/ce/src/organization/pages/OrganizationCreatePage.tsx b/src/packages/ce/src/organization/pages/OrganizationCreatePage.tsx index c5971720..b3fc2acd 100644 --- a/src/packages/ce/src/organization/pages/OrganizationCreatePage.tsx +++ b/src/packages/ce/src/organization/pages/OrganizationCreatePage.tsx @@ -29,7 +29,7 @@ export const OrganizationCreatePage = () => { name: values.name as unknown as string }).then(payload => { if ((payload?.errors?.length ?? 0) <= 0) { - router.push("/organizations") + router.push("/workspaces") } }) }) @@ -55,7 +55,7 @@ export const OrganizationCreatePage = () => { {...inputs.getInputProps("name")}/> - + diff --git a/src/packages/ce/src/organization/views/OrganizationsTopView.tsx b/src/packages/ce/src/organization/views/OrganizationsTopView.tsx index 60a36763..bab3740c 100644 --- a/src/packages/ce/src/organization/views/OrganizationsTopView.tsx +++ b/src/packages/ce/src/organization/views/OrganizationsTopView.tsx @@ -1,6 +1,5 @@ "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"; @@ -10,16 +9,6 @@ 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) => { diff --git a/src/packages/ce/src/organization/views/OrganizationsView.tsx b/src/packages/ce/src/organization/views/OrganizationsView.tsx index 03e6c5d1..3f7cb0d1 100644 --- a/src/packages/ce/src/organization/views/OrganizationsView.tsx +++ b/src/packages/ce/src/organization/views/OrganizationsView.tsx @@ -40,7 +40,7 @@ export const OrganizationsView = () => { - + diff --git a/src/packages/ce/src/project/services/fragments/Project.fragment.graphql b/src/packages/ce/src/project/services/fragments/Project.fragment.graphql index 287c91f2..f28e8a0b 100644 --- a/src/packages/ce/src/project/services/fragments/Project.fragment.graphql +++ b/src/packages/ce/src/project/services/fragments/Project.fragment.graphql @@ -17,6 +17,7 @@ fragment Project on NamespaceProject { nodes { __typename id + createdAt } pageInfo { __typename diff --git a/src/packages/ce/src/project/views/PersonalProjectsView.tsx b/src/packages/ce/src/project/views/PersonalProjectsView.tsx index b4b719f8..013a4c7f 100644 --- a/src/packages/ce/src/project/views/PersonalProjectsView.tsx +++ b/src/packages/ce/src/project/views/PersonalProjectsView.tsx @@ -46,91 +46,6 @@ export const PersonalProjectsView: React.FC = () => { }, [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. - - - - - - - - - - - - - { - 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)}/> -
- {projectsList} diff --git a/src/packages/ce/src/project/views/ProjectTabView.tsx b/src/packages/ce/src/project/views/ProjectTabView.tsx deleted file mode 100644 index 30416b3a..00000000 --- a/src/packages/ce/src/project/views/ProjectTabView.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import React from "react"; -import {useParams, usePathname, useRouter} from "next/navigation"; -import {Tab, TabList, TabTrigger} from "@code0-tech/pictor/dist/components/tab/Tab"; -import {Button, Text, Tooltip, TooltipContent, TooltipPortal, TooltipTrigger} from "@code0-tech/pictor"; -import {IconBox, IconHome, IconServer, IconSettings} from "@tabler/icons-react"; - -export const ProjectTabView: React.FC = () => { - const pathname = usePathname() - const router = useRouter() - const params = useParams() - const defaultValue = pathname.includes("flow") ? "flow" - : pathname.includes("module") ? "module" - : pathname.includes("runtime") ? "runtime" : - "settings" - - const namespaceId = params.namespaceId as any - const projectId = params.projectId as any - const flowId = params.flowId as any - - return - - - - - - - - - - Flows - - - - - - - - - - - - Assigned runtimes - - - - - - - - - - - - Plugins - - - - - - - - - - - - Settings - - - - - -} \ 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 index 65c76a19..71f73b67 100644 --- a/src/packages/ce/src/project/views/ProjectsView.tsx +++ b/src/packages/ce/src/project/views/ProjectsView.tsx @@ -43,22 +43,23 @@ export const ProjectsView: React.FC = () => { return <> - - - + +
+ Projects - - Manage projects that you belong to. You can create new projects and switch between them. + + + Manage projects that you belong to.
You can create new projects and switch between them.
- +
- + - + diff --git a/src/packages/ce/src/user/components/UserEditDialogComponent.tsx b/src/packages/ce/src/user/components/UserEditDialogComponent.tsx index 75bebbe3..f14600d1 100644 --- a/src/packages/ce/src/user/components/UserEditDialogComponent.tsx +++ b/src/packages/ce/src/user/components/UserEditDialogComponent.tsx @@ -2,8 +2,8 @@ import React from "react"; import { + Badge, Button, - ButtonGroup, Card, Col, Dialog, @@ -31,7 +31,7 @@ import {Tab, TabContent, TabList, TabTrigger} from "@code0-tech/pictor/dist/comp import {User, UsersUpdateInput} from "@code0-tech/sagittarius-graphql-types"; import {UserService} from "@edition/user/services/User.service"; import {addIslandSuccessNotification} from "@code0-tech/pictor/dist/components/island/Island.hook"; -import {IconAt, IconLock, IconMail, IconUser, IconX} from "@tabler/icons-react"; +import {IconAt, IconLock, IconMail, IconUser} from "@tabler/icons-react"; import {Layout} from "@code0-tech/pictor/dist/components/layout/Layout"; import {motion} from "framer-motion"; @@ -151,23 +151,22 @@ export const UserEditDialogComponent: React.FC = ( }} initial={{ width: "200px", - opacity: 0.25, }} whileInView={{ width: "200px", - opacity: 0.25, }} whileHover={{ width: "250px", - opacity: 1, }} style={{ padding: "0.7rem", - marginTop: "0.55rem", - height: "100%" + paddingTop: "1rem", + height: "100%", + display: "flex", + flexDirection: "column" }} > - + Settings of @{user?.username ?? ""} @@ -193,23 +192,24 @@ export const UserEditDialogComponent: React.FC = ( + + + }> General - - - - - - + @@ -254,18 +254,10 @@ export const UserEditDialogComponent: React.FC = ( Permissions - - - - - - - + @@ -285,17 +277,10 @@ export const UserEditDialogComponent: React.FC = ( Security - - - - - - + diff --git a/src/packages/ce/src/user/components/UserMenuComponent.tsx b/src/packages/ce/src/user/components/UserMenuComponent.tsx index 0b4c1885..d69c9782 100644 --- a/src/packages/ce/src/user/components/UserMenuComponent.tsx +++ b/src/packages/ce/src/user/components/UserMenuComponent.tsx @@ -3,7 +3,8 @@ import React from "react" import {Scalars} from "@code0-tech/sagittarius-graphql-types"; import { - Avatar, Button, + Avatar, + Button, Flex, Menu, MenuContent, @@ -29,7 +30,7 @@ const UserMenuComponent: React.FC = props => { return ( -