diff --git a/package-lock.json b/package-lock.json index 06a36113..46c93cc2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.0", "dependencies": { "@apollo/client": "^4.0.9", - "@code0-tech/pictor": "^0.0.0-mvp.21", + "@code0-tech/pictor": "^0.0.0-mvp.26", "graphql": "^16.12.0", "graphql-tag": "^2.12.6", "next": "16.0.7", @@ -168,7 +168,8 @@ "version": "0.4.16", "resolved": "https://registry.npmjs.org/@ariakit/core/-/core-0.4.16.tgz", "integrity": "sha512-nPJ0Be8d5ZvRApYGqdLMuYUjP7ktkPmTPOXyZFw+0Illk8LKgF3Q74ctVGuoQurJNDsANXcewzlyXK4vyIAGTA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@ariakit/react": { "version": "0.4.19", @@ -193,6 +194,7 @@ "resolved": "https://registry.npmjs.org/@ariakit/react-core/-/react-core-0.4.19.tgz", "integrity": "sha512-Aj+fu4pMyPXtlBghI+E7KSWItqJkbAqEhut3DlsFAjK9fQdHE+e1tQJG1PtnoEdD9BExkJWQ6R4M5a9HkEhqPA==", "license": "MIT", + "peer": true, "dependencies": { "@ariakit/core": "0.4.16", "@floating-ui/dom": "^1.0.0", @@ -234,7 +236,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -445,12 +446,12 @@ } }, "node_modules/@code0-tech/pictor": { - "version": "0.0.0-mvp.21", - "resolved": "https://registry.npmjs.org/@code0-tech/pictor/-/pictor-0.0.0-mvp.21.tgz", - "integrity": "sha512-mqRNVl6GAiOj4PH83jbm0JZnu8wPMTqlFTut5+GaSZqRv06xionpGIhW1xWRBMmrpQgBO0Fdp+/bMEWM6aSBtQ==", + "version": "0.0.0-mvp.26", + "resolved": "https://registry.npmjs.org/@code0-tech/pictor/-/pictor-0.0.0-mvp.26.tgz", + "integrity": "sha512-9omk4Dk4pYn8qezI/PjNQ8F2hK5ZFLD+1/cGmxV2vhuWKWD+3mR1vi3/hBwedoR2LEdrsme/R39MPGbONPKsfA==", "peerDependencies": { "@ariakit/react": "^0.4.5", - "@code0-tech/sagittarius-graphql-types": "^0.0.0-755f9382e6589ede0182dace55452703bcd6e85b", + "@code0-tech/sagittarius-graphql-types": "^0.0.0-c63274fdd34593b8aa24f9f977659fd3d6270150", "@radix-ui/react-checkbox": "^1.3.2", "@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-dropdown-menu": "^2.1.15", @@ -474,16 +475,17 @@ } }, "node_modules/@code0-tech/sagittarius-graphql-types": { - "version": "0.0.0-beb91167a8ea12eb6d4a6e3933483bacf845f2f6", - "resolved": "https://registry.npmjs.org/@code0-tech/sagittarius-graphql-types/-/sagittarius-graphql-types-0.0.0-beb91167a8ea12eb6d4a6e3933483bacf845f2f6.tgz", - "integrity": "sha512-pYthkqPpFc6GQqn35mvh7y0ozc8zWKHi2Eq0bmvLGhJa535jwh3btE6wEi0eKPDC6BPOA4k34cetGMDU3cDQEQ==", + "version": "0.0.0-c63274fdd34593b8aa24f9f977659fd3d6270150", + "resolved": "https://registry.npmjs.org/@code0-tech/sagittarius-graphql-types/-/sagittarius-graphql-types-0.0.0-c63274fdd34593b8aa24f9f977659fd3d6270150.tgz", + "integrity": "sha512-6J0DC2OdNnZRcS7jJkwCIEv4PoQmpDMdR4FAdVKfrE08X4PZAD+csVf0zTVjmX82KFzVL0M46HB/6twpKCsRUg==", "peer": true }, "node_modules/@daybrush/utils": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/@daybrush/utils/-/utils-1.13.0.tgz", "integrity": "sha512-ALK12C6SQNNHw1enXK+UO8bdyQ+jaWNQ1Af7Z3FNxeAwjYhQT7do+TRE4RASAJ3ObaS2+TJ7TXR3oz2Gzbw0PQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@emnapi/core": { "version": "1.7.0", @@ -667,6 +669,7 @@ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", "license": "MIT", + "peer": true, "dependencies": { "@floating-ui/utils": "^0.2.10" } @@ -676,6 +679,7 @@ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", "license": "MIT", + "peer": true, "dependencies": { "@floating-ui/core": "^1.7.3", "@floating-ui/utils": "^0.2.10" @@ -686,6 +690,7 @@ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz", "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==", "license": "MIT", + "peer": true, "dependencies": { "@floating-ui/dom": "^1.7.4" }, @@ -698,7 +703,8 @@ "version": "0.2.10", "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@graphql-typed-document-node/core": { "version": "3.2.0", @@ -1757,19 +1763,22 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@radix-ui/primitive": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@radix-ui/react-arrow": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", "license": "MIT", + "peer": true, "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, @@ -1824,6 +1833,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", "license": "MIT", + "peer": true, "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", @@ -1850,6 +1860,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -1865,6 +1876,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -1917,6 +1929,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -1932,6 +1945,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", "license": "MIT", + "peer": true, "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", @@ -1989,6 +2003,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -2004,6 +2019,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", "license": "MIT", + "peer": true, "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", @@ -2029,6 +2045,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", "license": "MIT", + "peer": true, "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, @@ -2047,6 +2064,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz", "integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==", "license": "MIT", + "peer": true, "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", @@ -2122,6 +2140,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", "license": "MIT", + "peer": true, "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", @@ -2154,6 +2173,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", "license": "MIT", + "peer": true, "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" @@ -2178,6 +2198,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", "license": "MIT", + "peer": true, "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" @@ -2202,6 +2223,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", "license": "MIT", + "peer": true, "dependencies": { "@radix-ui/react-slot": "1.2.3" }, @@ -2258,6 +2280,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", "license": "MIT", + "peer": true, "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", @@ -2321,6 +2344,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", "license": "MIT", + "peer": true, "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, @@ -2370,6 +2394,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.10.tgz", "integrity": "sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==", "license": "MIT", + "peer": true, "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-primitive": "2.1.3", @@ -2460,6 +2485,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -2475,6 +2501,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", "license": "MIT", + "peer": true, "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" @@ -2494,6 +2521,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", "license": "MIT", + "peer": true, "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, @@ -2512,6 +2540,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", "license": "MIT", + "peer": true, "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, @@ -2530,6 +2559,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.0.tgz", "integrity": "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==", "license": "MIT", + "peer": true, "dependencies": { "use-sync-external-store": "^1.5.0" }, @@ -2548,6 +2578,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -2563,6 +2594,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -2578,6 +2610,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", "license": "MIT", + "peer": true, "dependencies": { "@radix-ui/rect": "1.1.1" }, @@ -2596,6 +2629,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", "license": "MIT", + "peer": true, "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, @@ -2614,6 +2648,7 @@ "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", "license": "MIT", + "peer": true, "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, @@ -2636,7 +2671,8 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@rtsao/scc": { "version": "1.1.0", @@ -2659,6 +2695,7 @@ "resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-3.35.0.tgz", "integrity": "sha512-yYXe+gJ56xlZFiXwV9zVoe3FWCGuZ/D7/G4ZIlDtGxSx5CGQK110wrnT29gUj52kEZoxqF7oURTk97GQxELOFQ==", "license": "MIT", + "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/codecalm" @@ -2696,13 +2733,15 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/d3-drag": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/d3-selection": "*" } @@ -2712,6 +2751,7 @@ "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", "license": "MIT", + "peer": true, "dependencies": { "@types/d3-color": "*" } @@ -2720,13 +2760,15 @@ "version": "3.0.11", "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/d3-transition": { "version": "3.0.9", "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", "license": "MIT", + "peer": true, "dependencies": { "@types/d3-selection": "*" } @@ -2736,6 +2778,7 @@ "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", "license": "MIT", + "peer": true, "dependencies": { "@types/d3-interpolate": "*", "@types/d3-selection": "*" @@ -2778,7 +2821,6 @@ "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -2789,7 +2831,6 @@ "integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==", "devOptional": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -2840,7 +2881,6 @@ "integrity": "sha512-6m1I5RmHBGTnUGS113G04DMu3CpSdxCAU/UvtjNWL4Nuf3MW9tQhiJqRlHzChIkhy6kZSAQmc+I1bcGjE3yNKg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.3", "@typescript-eslint/types": "8.46.3", @@ -3434,6 +3474,7 @@ "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.72.tgz", "integrity": "sha512-WBI5Aau0fXTXwxHPzceLNS6QdXggSWnGjDtj/gG669crApN8+SCmEtkBth1m7r6pStNo/5fI9McEi7Dk0ymCLA==", "license": "MIT", + "peer": true, "dependencies": { "@types/d3-drag": "^3.0.7", "@types/d3-interpolate": "^3.0.4", @@ -3452,7 +3493,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3515,6 +3555,7 @@ "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.0.0" }, @@ -3751,7 +3792,6 @@ "integrity": "sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/types": "^7.26.0" } @@ -3817,7 +3857,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.19", "caniuse-lite": "^1.0.30001751", @@ -3948,7 +3987,8 @@ "version": "5.0.5", "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz", "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/client-only": { "version": "0.0.1", @@ -4034,6 +4074,7 @@ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", "license": "ISC", + "peer": true, "engines": { "node": ">=12" } @@ -4043,6 +4084,7 @@ "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", "license": "ISC", + "peer": true, "engines": { "node": ">=12" } @@ -4052,6 +4094,7 @@ "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", "license": "ISC", + "peer": true, "dependencies": { "d3-dispatch": "1 - 3", "d3-selection": "3" @@ -4065,6 +4108,7 @@ "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", "license": "BSD-3-Clause", + "peer": true, "engines": { "node": ">=12" } @@ -4074,6 +4118,7 @@ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", "license": "ISC", + "peer": true, "dependencies": { "d3-color": "1 - 3" }, @@ -4096,6 +4141,7 @@ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", "license": "ISC", + "peer": true, "engines": { "node": ">=12" } @@ -4105,6 +4151,7 @@ "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", "license": "ISC", + "peer": true, "dependencies": { "d3-color": "1 - 3", "d3-dispatch": "1 - 3", @@ -4124,6 +4171,7 @@ "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", "license": "ISC", + "peer": true, "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", @@ -4271,7 +4319,8 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/doctrine": { "version": "2.1.0", @@ -4521,7 +4570,6 @@ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -4707,7 +4755,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -5173,6 +5220,7 @@ "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -5290,7 +5338,6 @@ "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.12.0.tgz", "integrity": "sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==", "license": "MIT", - "peer": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -6652,7 +6699,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -6662,7 +6708,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -6682,6 +6727,7 @@ "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz", "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==", "license": "MIT", + "peer": true, "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", @@ -6707,6 +6753,7 @@ "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", "license": "MIT", + "peer": true, "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" @@ -6740,6 +6787,7 @@ "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", "license": "MIT", + "peer": true, "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" @@ -6910,7 +6958,6 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -6975,7 +7022,6 @@ "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.2.tgz", "integrity": "sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==", "license": "MIT", - "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -7478,7 +7524,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -7641,7 +7686,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7781,6 +7825,7 @@ "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.0.0" }, @@ -7802,6 +7847,7 @@ "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", "license": "MIT", + "peer": true, "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" @@ -7824,6 +7870,7 @@ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", "license": "MIT", + "peer": true, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } @@ -7969,7 +8016,6 @@ "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -7992,6 +8038,7 @@ "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", "license": "MIT", + "peer": true, "dependencies": { "use-sync-external-store": "^1.2.2" }, diff --git a/package.json b/package.json index d7ccd7e5..3fbe8352 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ }, "dependencies": { "@apollo/client": "^4.0.9", - "@code0-tech/pictor": "^0.0.0-mvp.21", + "@code0-tech/pictor": "^0.0.0-mvp.26", "graphql": "^16.12.0", "graphql-tag": "^2.12.6", "next": "16.0.7", diff --git a/public/CodeZero_Logo.png b/public/CodeZero_Logo.png new file mode 100644 index 00000000..01f6a104 Binary files /dev/null and b/public/CodeZero_Logo.png differ diff --git a/public/CodeZero_Logo_MVP.png b/public/CodeZero_Logo_MVP.png new file mode 100644 index 00000000..ecbde2f5 Binary files /dev/null and b/public/CodeZero_Logo_MVP.png differ diff --git a/src/app/(auth)/layout.tsx b/src/app/(auth)/layout.tsx index e3b88f87..7e64ff5b 100644 --- a/src/app/(auth)/layout.tsx +++ b/src/app/(auth)/layout.tsx @@ -32,16 +32,28 @@ export default function AuthLayout({children}: Readonly<{ children: React.ReactN
- All rights reserved © Code0 UG (haftungsbeschränkt) - {"CodeZero + + + Every great idea starts at zero. {" "} + + Start with CodeZero. + + + + All rights reserved © Code0 UG (haftungsbeschränkt) + {"CodeZero + +
diff --git a/src/app/(dashboard)/@bar/default.tsx b/src/app/(dashboard)/@bar/default.tsx index 76798987..75fab784 100644 --- a/src/app/(dashboard)/@bar/default.tsx +++ b/src/app/(dashboard)/@bar/default.tsx @@ -14,7 +14,7 @@ import { useUserSession } from "@code0-tech/pictor"; import DUserMenu from "@code0-tech/pictor/dist/components/d-user/DUserMenu"; -import {IconBuilding, IconInbox, IconLogout, IconSearch, IconSettings} from "@tabler/icons-react"; +import {IconBuilding, IconFolders, IconInbox, IconLogout, IconSearch} from "@tabler/icons-react"; import React from "react"; import Image from "next/image"; import {UserService} from "@edition/user/User.service"; @@ -46,12 +46,19 @@ const Page = () => { }) } + const namespaceIndex = currentSession.user?.namespace?.id?.match(/Namespace\/(\d+)$/)?.[1] + return Organizations + + + Personal Workspace + + Logout @@ -63,8 +70,8 @@ const Page = () => { - {"CodeZero + {"CodeZero Application diff --git a/src/app/(dashboard)/@tab/namespace/[namespaceId]/members/default.tsx b/src/app/(dashboard)/@tab/namespace/[namespaceId]/members/default.tsx new file mode 100644 index 00000000..48890ba8 --- /dev/null +++ b/src/app/(dashboard)/@tab/namespace/[namespaceId]/members/default.tsx @@ -0,0 +1,5 @@ +import {NamespaceTabView} from "@edition/dashboard/namespace/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 new file mode 100644 index 00000000..48890ba8 --- /dev/null +++ b/src/app/(dashboard)/@tab/namespace/[namespaceId]/page.tsx @@ -0,0 +1,5 @@ +import {NamespaceTabView} from "@edition/dashboard/namespace/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 new file mode 100644 index 00000000..48890ba8 --- /dev/null +++ b/src/app/(dashboard)/@tab/namespace/[namespaceId]/projects/default.tsx @@ -0,0 +1,5 @@ +import {NamespaceTabView} from "@edition/dashboard/namespace/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 new file mode 100644 index 00000000..48890ba8 --- /dev/null +++ b/src/app/(dashboard)/@tab/namespace/[namespaceId]/roles/default.tsx @@ -0,0 +1,5 @@ +import {NamespaceTabView} from "@edition/dashboard/namespace/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 new file mode 100644 index 00000000..48890ba8 --- /dev/null +++ b/src/app/(dashboard)/@tab/namespace/[namespaceId]/runtimes/default.tsx @@ -0,0 +1,5 @@ +import {NamespaceTabView} from "@edition/dashboard/namespace/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 new file mode 100644 index 00000000..48890ba8 --- /dev/null +++ b/src/app/(dashboard)/@tab/namespace/[namespaceId]/settings/default.tsx @@ -0,0 +1,5 @@ +import {NamespaceTabView} from "@edition/dashboard/namespace/NamespaceTabView"; + +export default function Page() { + return +} \ No newline at end of file diff --git a/src/app/(dashboard)/layout.tsx b/src/app/(dashboard)/layout.tsx index 9bd88c5d..07e3d497 100644 --- a/src/app/(dashboard)/layout.tsx +++ b/src/app/(dashboard)/layout.tsx @@ -11,7 +11,8 @@ import { DOrganizationView, DRuntimeView, DUserView, ReactiveArrayStore, - useUserSession + useUserSession, + DNamespaceRoleView } from "@code0-tech/pictor"; import {UserService} from "@edition/user/User.service"; import {GraphqlClient} from "@core/util/graphql-client"; @@ -22,6 +23,8 @@ import {NamespaceService} from "@edition/namespace/Namespace.service"; import {usePersistentReactiveArrayService} from "@/hooks/usePersistentReactiveArrayService"; import {RuntimeService} from "@edition/runtime/Runtime.service"; import {ProjectService} from "@edition/project/Project.service"; +import {RoleService} from "@edition/role/Role.service"; +import {Spacing} from "@code0-tech/pictor/dist/components/spacing/Spacing"; interface ApplicationLayoutProps { children: React.ReactNode @@ -43,10 +46,11 @@ const ApplicationLayout: React.FC = ({children, bar, tab const namespace = usePersistentReactiveArrayService(`dashboard::namespaces::${currentSession?.id}`, (store: ReactiveArrayStore) => new NamespaceService(graphqlClient, store)) const runtime = usePersistentReactiveArrayService(`dashboard::global_runtimes::${currentSession?.id}`, (store: ReactiveArrayStore) => new RuntimeService(graphqlClient, store)) const project = usePersistentReactiveArrayService(`dashboard::projects::${currentSession?.id}`, (store: ReactiveArrayStore) => new ProjectService(graphqlClient, store)) + const role = usePersistentReactiveArrayService(`dashboard::roles::${currentSession?.id}`, (store: ReactiveArrayStore) => new RoleService(graphqlClient, store)) if (currentSession === null) router.push("/login") - return + return
diff --git a/src/app/(dashboard)/namespace/[namespaceId]/layout.tsx b/src/app/(dashboard)/namespace/[namespaceId]/layout.tsx index bc9d3e85..aeee3200 100644 --- a/src/app/(dashboard)/namespace/[namespaceId]/layout.tsx +++ b/src/app/(dashboard)/namespace/[namespaceId]/layout.tsx @@ -1,3 +1,5 @@ +"use client" + import React from "react"; import Link from "next/link"; diff --git a/src/app/(dashboard)/namespace/[namespaceId]/members/add/page.tsx b/src/app/(dashboard)/namespace/[namespaceId]/members/add/page.tsx new file mode 100644 index 00000000..fafad442 --- /dev/null +++ b/src/app/(dashboard)/namespace/[namespaceId]/members/add/page.tsx @@ -0,0 +1,7 @@ +"use client" + +import {MemberAddPage} from "@edition/dashboard/member/MemberAddPage"; + +export default function Page() { + return +} \ No newline at end of file diff --git a/src/app/(dashboard)/namespace/[namespaceId]/members/page.tsx b/src/app/(dashboard)/namespace/[namespaceId]/members/page.tsx new file mode 100644 index 00000000..7e92b720 --- /dev/null +++ b/src/app/(dashboard)/namespace/[namespaceId]/members/page.tsx @@ -0,0 +1,7 @@ +"use client" + +import {MembersPage} from "@edition/dashboard/member/MembersPage"; + +export default function Page() { + return +} \ No newline at end of file diff --git a/src/app/(dashboard)/namespace/[namespaceId]/page.tsx b/src/app/(dashboard)/namespace/[namespaceId]/page.tsx index 886ae7db..a9a68513 100644 --- a/src/app/(dashboard)/namespace/[namespaceId]/page.tsx +++ b/src/app/(dashboard)/namespace/[namespaceId]/page.tsx @@ -1,3 +1,7 @@ -export default function NamespacePage() { - return <>Namespace page (view all projects) -} +"use client" + +import {NamespaceOverviewPage} from "@edition/dashboard/namespace/NamespaceOverviewPage"; + +export default function Page() { + return +} \ No newline at end of file diff --git a/src/app/(dashboard)/namespace/[namespaceId]/projects/create/page.tsx b/src/app/(dashboard)/namespace/[namespaceId]/projects/create/page.tsx new file mode 100644 index 00000000..8211b5a4 --- /dev/null +++ b/src/app/(dashboard)/namespace/[namespaceId]/projects/create/page.tsx @@ -0,0 +1,5 @@ +import {NamespaceProjectsCreatePage} from "@edition/dashboard/namespace/NamespaceProjectsCreatePage"; + +export default function Page() { + return +} \ 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 new file mode 100644 index 00000000..98e317e3 --- /dev/null +++ b/src/app/(dashboard)/namespace/[namespaceId]/projects/page.tsx @@ -0,0 +1,5 @@ +import {NamespaceProjectsPage} from "@edition/dashboard/namespace/NamespaceProjectsPage"; + +export default function Page() { + return +} \ No newline at end of file diff --git a/src/app/(dashboard)/namespace/[namespaceId]/roles/[roleId]/settings/page.tsx b/src/app/(dashboard)/namespace/[namespaceId]/roles/[roleId]/settings/page.tsx new file mode 100644 index 00000000..4eed9bc3 --- /dev/null +++ b/src/app/(dashboard)/namespace/[namespaceId]/roles/[roleId]/settings/page.tsx @@ -0,0 +1,7 @@ +"use client" + +import {RoleSettingsPage} from "@edition/dashboard/role/RoleSettingsPage"; + +export default function Page() { + return +} \ No newline at end of file diff --git a/src/app/(dashboard)/namespace/[namespaceId]/roles/create/page.tsx b/src/app/(dashboard)/namespace/[namespaceId]/roles/create/page.tsx new file mode 100644 index 00000000..a4998c3c --- /dev/null +++ b/src/app/(dashboard)/namespace/[namespaceId]/roles/create/page.tsx @@ -0,0 +1,7 @@ +"use client" + +import {RoleCreatePage} from "@edition/dashboard/role/RoleCreatePage"; + +export default function Page() { + return +} \ No newline at end of file diff --git a/src/app/(dashboard)/namespace/[namespaceId]/roles/page.tsx b/src/app/(dashboard)/namespace/[namespaceId]/roles/page.tsx new file mode 100644 index 00000000..1268e43b --- /dev/null +++ b/src/app/(dashboard)/namespace/[namespaceId]/roles/page.tsx @@ -0,0 +1,5 @@ +import {RolesPage} from "@edition/dashboard/role/RolesPage"; + +export default function Page() { + 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 new file mode 100644 index 00000000..94cdc737 --- /dev/null +++ b/src/app/(dashboard)/namespace/[namespaceId]/runtimes/[runtimeId]/settings/page.tsx @@ -0,0 +1,5 @@ +import {RuntimeSettingsPage} from "@edition/dashboard/runtime/RuntimeSettingsPage"; + +export default function Page() { + return +} \ No newline at end of file diff --git a/src/app/(dashboard)/namespace/[namespaceId]/runtimes/create/page.tsx b/src/app/(dashboard)/namespace/[namespaceId]/runtimes/create/page.tsx new file mode 100644 index 00000000..9063cb15 --- /dev/null +++ b/src/app/(dashboard)/namespace/[namespaceId]/runtimes/create/page.tsx @@ -0,0 +1,5 @@ +import {RuntimeCreatePage} from "@edition/dashboard/runtime/RuntimeCreatePage"; + +export default function Page() { + return +} \ No newline at end of file diff --git a/src/app/(dashboard)/namespace/[namespaceId]/runtimes/page.tsx b/src/app/(dashboard)/namespace/[namespaceId]/runtimes/page.tsx new file mode 100644 index 00000000..f9add133 --- /dev/null +++ b/src/app/(dashboard)/namespace/[namespaceId]/runtimes/page.tsx @@ -0,0 +1,5 @@ +import {RuntimesPage} from "@edition/dashboard/runtime/RuntimesPage"; + +export default function Page() { + return +} \ No newline at end of file diff --git a/src/app/(dashboard)/namespace/[namespaceId]/settings/page.tsx b/src/app/(dashboard)/namespace/[namespaceId]/settings/page.tsx new file mode 100644 index 00000000..201fd719 --- /dev/null +++ b/src/app/(dashboard)/namespace/[namespaceId]/settings/page.tsx @@ -0,0 +1,7 @@ +"use client" + +import {OrganizationSettingsPage} from "@edition/dashboard/organization/OrganizationSettingsPage"; + +export default function Page() { + return +} \ No newline at end of file diff --git a/src/app/(dashboard)/organizations/create/page.tsx b/src/app/(dashboard)/organizations/create/page.tsx index 4387ab1b..70fbc55c 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/application/OrganizationCreatePage"; +import {OrganizationCreatePage} from "@edition/dashboard/organization/OrganizationCreatePage"; export default function Page() { return diff --git a/src/app/(dashboard)/organizations/page.tsx b/src/app/(dashboard)/organizations/page.tsx index 946fd4e8..d0aed06b 100644 --- a/src/app/(dashboard)/organizations/page.tsx +++ b/src/app/(dashboard)/organizations/page.tsx @@ -1,4 +1,4 @@ -import {OrganizationsPage} from "@edition/dashboard/application/OrganizationsPage"; +import {OrganizationsPage} from "@edition/dashboard/organization/OrganizationsPage"; export default function Page() { return diff --git a/src/app/(dashboard)/page.tsx b/src/app/(dashboard)/page.tsx index 47ed3a02..27103554 100644 --- a/src/app/(dashboard)/page.tsx +++ b/src/app/(dashboard)/page.tsx @@ -3,8 +3,6 @@ import React from "react"; import {ApplicationPage} from "@edition/dashboard/application/ApplicationPage"; -const Page = () => { +export default function Page() { return } - -export default React.memo(Page); diff --git a/src/app/(dashboard)/runtimes/[runtimeId]/settings/page.tsx b/src/app/(dashboard)/runtimes/[runtimeId]/settings/page.tsx index 230b2453..94cdc737 100644 --- a/src/app/(dashboard)/runtimes/[runtimeId]/settings/page.tsx +++ b/src/app/(dashboard)/runtimes/[runtimeId]/settings/page.tsx @@ -1,5 +1,5 @@ -import {RuntimeSettingPage} from "@edition/dashboard/application/RuntimeSettingPage"; +import {RuntimeSettingsPage} from "@edition/dashboard/runtime/RuntimeSettingsPage"; export default function Page() { - return + return } \ No newline at end of file diff --git a/src/app/(dashboard)/runtimes/create/page.tsx b/src/app/(dashboard)/runtimes/create/page.tsx index e40af6be..9063cb15 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/application/RuntimeCreatePage"; +import {RuntimeCreatePage} from "@edition/dashboard/runtime/RuntimeCreatePage"; export default function Page() { return diff --git a/src/app/(dashboard)/runtimes/page.tsx b/src/app/(dashboard)/runtimes/page.tsx index 8602067b..48bde090 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/application/RuntimesPage"; +import {RuntimesPage} from "@edition/dashboard/runtime/RuntimesPage"; export default function Page() { return diff --git a/src/app/(dashboard)/users/page.tsx b/src/app/(dashboard)/users/page.tsx index e7313452..14e72354 100644 --- a/src/app/(dashboard)/users/page.tsx +++ b/src/app/(dashboard)/users/page.tsx @@ -1,4 +1,4 @@ -import {UsersPage} from "@edition/dashboard/application/UsersPage"; +import {UsersPage} from "@edition/dashboard/user/UsersPage"; export default function Page() { return diff --git a/src/app/global.scss b/src/app/global.scss index f7dc1ad7..90ae320d 100644 --- a/src/app/global.scss +++ b/src/app/global.scss @@ -13,7 +13,7 @@ html, body { a { text-decoration: none; - text-decoration-color: white !important; + text-decoration-color: transparent !important; &:hover { text-decoration: underline; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 58c0e859..e926e7a5 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -19,6 +19,7 @@ import {Toaster} from "sonner"; import {Error} from "@code0-tech/sagittarius-graphql-types"; import {toast} from "@code0-tech/pictor"; import {Inter} from 'next/font/google' +import {GraphQLFormattedError} from "graphql/error"; /** * Load the Inter font with Latin subset and swap display strategy @@ -66,8 +67,8 @@ const ErrorCodeDescription: Record = { /** * Handles error toasts based on error type */ -const toastHandler = (error: Error) => { - if (error.__typename === "ErrorCode") { +const toastHandler = (error: Error | GraphQLFormattedError) => { + if (error && "errorCode" in error) { toast({ title: ErrorCodeDescription[(error.errorCode as string)], color: "error", @@ -94,7 +95,7 @@ export default function RootLayout({children}: Readonly<{ children: React.ReactN */ const errorLink = new ErrorLink(({error, result, operation, forward}) => { if (error instanceof CombinedGraphQLErrors) { - error.errors.forEach((error: Error) => { + error.errors.forEach((error: GraphQLFormattedError) => { toastHandler(error) }) return diff --git a/src/packages/ce/src/auth/UserEmailVerificationPage.tsx b/src/packages/ce/src/auth/UserEmailVerificationPage.tsx index 1c53b1bb..4c1199a6 100644 --- a/src/packages/ce/src/auth/UserEmailVerificationPage.tsx +++ b/src/packages/ce/src/auth/UserEmailVerificationPage.tsx @@ -39,7 +39,7 @@ export const UserEmailVerificationPage: React.FC = () => { }) return <> - {"CodeZero diff --git a/src/packages/ce/src/auth/UserForgotPasswordPage.tsx b/src/packages/ce/src/auth/UserForgotPasswordPage.tsx index 00ddddbc..739e8af8 100644 --- a/src/packages/ce/src/auth/UserForgotPasswordPage.tsx +++ b/src/packages/ce/src/auth/UserForgotPasswordPage.tsx @@ -39,7 +39,7 @@ export const UserForgotPasswordPage: React.FC = () => { }) return <> - {"CodeZero diff --git a/src/packages/ce/src/auth/UserLoginPage.tsx b/src/packages/ce/src/auth/UserLoginPage.tsx index 43babcd7..e116c0e1 100644 --- a/src/packages/ce/src/auth/UserLoginPage.tsx +++ b/src/packages/ce/src/auth/UserLoginPage.tsx @@ -59,7 +59,8 @@ export const UserLoginPage: React.FC = () => { }) return <> - {"CodeZero + {"CodeZero @@ -87,10 +88,10 @@ export const UserLoginPage: React.FC = () => { Forgot password? - + Don't have an account yet - + Sign up diff --git a/src/packages/ce/src/auth/UserRegistrationPage.tsx b/src/packages/ce/src/auth/UserRegistrationPage.tsx index fc623323..9d4d3d3d 100644 --- a/src/packages/ce/src/auth/UserRegistrationPage.tsx +++ b/src/packages/ce/src/auth/UserRegistrationPage.tsx @@ -69,7 +69,7 @@ export const UserRegistrationPage: React.FC = () => { }) return <> - {"CodeZero diff --git a/src/packages/ce/src/auth/UserResetPasswordPage.tsx b/src/packages/ce/src/auth/UserResetPasswordPage.tsx index 7578413e..c1d9c75a 100644 --- a/src/packages/ce/src/auth/UserResetPasswordPage.tsx +++ b/src/packages/ce/src/auth/UserResetPasswordPage.tsx @@ -51,7 +51,7 @@ export const UserResetPasswordPage: React.FC = () => { }) return <> - {"CodeZero diff --git a/src/packages/ce/src/dashboard/application/ApplicationPage.tsx b/src/packages/ce/src/dashboard/application/ApplicationPage.tsx index 3fd16a1c..304d6e78 100644 --- a/src/packages/ce/src/dashboard/application/ApplicationPage.tsx +++ b/src/packages/ce/src/dashboard/application/ApplicationPage.tsx @@ -1,5 +1,5 @@ -import {PersonalProjectsView} from "@edition/dashboard/application/PersonalProjectsView"; -import {OrganizationsView} from "@edition/dashboard/application/OrganizationsView"; +import {PersonalProjectsView} from "@edition/dashboard/personal-project/PersonalProjectsView"; +import {OrganizationsView} from "@edition/dashboard/organization/OrganizationsView"; import {Spacing} from "@code0-tech/pictor"; export const ApplicationPage = () => { diff --git a/src/packages/ce/src/dashboard/application/ApplicationSettingsPage.tsx b/src/packages/ce/src/dashboard/application/ApplicationSettingsPage.tsx index f1253038..c301b0c7 100644 --- a/src/packages/ce/src/dashboard/application/ApplicationSettingsPage.tsx +++ b/src/packages/ce/src/dashboard/application/ApplicationSettingsPage.tsx @@ -38,13 +38,13 @@ export const ApplicationSettingsPage: React.FC = () => { Application - + - + - + + - - - - + + - - - - + + + ) : null @@ -64,22 +59,18 @@ export const ApplicationTabView: React.FC = () => { return - - - - + + - - - - + + {adminLinks} diff --git a/src/packages/ce/src/dashboard/application/RuntimesPage.tsx b/src/packages/ce/src/dashboard/application/RuntimesPage.tsx deleted file mode 100644 index dbe19682..00000000 --- a/src/packages/ce/src/dashboard/application/RuntimesPage.tsx +++ /dev/null @@ -1,50 +0,0 @@ -"use client" - -import React from "react"; -import { - Button, - DRuntimeList, - Flex, - Spacing, - Text, - TextInput, - useService, - useStore, - useUserSession -} from "@code0-tech/pictor"; -import {IconSearch} from "@tabler/icons-react"; -import Link from "next/link"; -import {notFound, useRouter} from "next/navigation"; -import {UserService} from "@edition/user/User.service"; - -export const RuntimesPage: React.FC = () => { - - const router = useRouter() - 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 <> - - - Global runtimes - - - } placeholder={"Find a global runtime..."}/> - - - - - - - { - const number = runtimeId?.match(/Runtime\/(\d+)$/)?.[1] - router.push(`/runtimes/${number}/settings`) - }}/> - -} \ 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 new file mode 100644 index 00000000..d7809127 --- /dev/null +++ b/src/packages/ce/src/dashboard/member/MemberAddPage.tsx @@ -0,0 +1,96 @@ +"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*/} + + + + + + + + + + +} \ 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 new file mode 100644 index 00000000..a201120d --- /dev/null +++ b/src/packages/ce/src/dashboard/member/MembersPage.tsx @@ -0,0 +1,8 @@ +"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 new file mode 100644 index 00000000..807a386e --- /dev/null +++ b/src/packages/ce/src/dashboard/member/MembersView.tsx @@ -0,0 +1,53 @@ +"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 + + + + + + + + + {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 new file mode 100644 index 00000000..dd812519 --- /dev/null +++ b/src/packages/ce/src/dashboard/namespace/NamespaceOverviewLeftView.tsx @@ -0,0 +1,61 @@ +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} + + + + + +
+ + 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 new file mode 100644 index 00000000..d09fceea --- /dev/null +++ b/src/packages/ce/src/dashboard/namespace/NamespaceOverviewPage.tsx @@ -0,0 +1,26 @@ +"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/NamespaceOverviewRightView.tsx b/src/packages/ce/src/dashboard/namespace/NamespaceOverviewRightView.tsx new file mode 100644 index 00000000..5698f9df --- /dev/null +++ b/src/packages/ce/src/dashboard/namespace/NamespaceOverviewRightView.tsx @@ -0,0 +1,84 @@ +"use client" + +import React from "react"; +import { + AuroraBackground, + Avatar, + Badge, + Button, + DRuntimeList, + Flex, + Spacing, + Text, + Tooltip, + TooltipArrow, + TooltipContent, + TooltipPortal, + TooltipTrigger, + useService, + useStore +} from "@code0-tech/pictor"; +import {MemberService} from "@edition/member/Member.service"; +import {useParams} from "next/navigation"; +import {NamespaceService} from "@edition/namespace/Namespace.service"; +import {UserService} from "@edition/user/User.service"; +import Link from "next/link"; + +export const NamespaceOverviewRightView: React.FC = () => { + + const params = useParams() + const memberService = useService(MemberService) + const memberStore = useStore(MemberService) + const namespaceService = useService(NamespaceService) + const namespaceStore = useStore(NamespaceService) + const userService = useService(UserService) + const userStore = useStore(UserService) + + const namespaceId = params.namespaceId as any as number + 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 + + + This is your personal space where you can manage your projects, roles, runtimes and invite outside + users. + + Members + + + {members.map(member => { + const user = userService.getById(member.user?.id!!) + return + + + + + + + + {user?.username} + + + {user?.email} + + + + + + + })} + + + + + + Runtimes + + + + +} \ No newline at end of file diff --git a/src/packages/ce/src/dashboard/application/PersonalProjectsCreatePage.tsx b/src/packages/ce/src/dashboard/namespace/NamespaceProjectsCreatePage.tsx similarity index 74% rename from src/packages/ce/src/dashboard/application/PersonalProjectsCreatePage.tsx rename to src/packages/ce/src/dashboard/namespace/NamespaceProjectsCreatePage.tsx index e605e881..0135fedc 100644 --- a/src/packages/ce/src/dashboard/application/PersonalProjectsCreatePage.tsx +++ b/src/packages/ce/src/dashboard/namespace/NamespaceProjectsCreatePage.tsx @@ -1,34 +1,23 @@ "use client" import React from "react"; -import { - Button, - Col, - Flex, - Spacing, - Text, - TextInput, - toast, - useForm, - useService, - useStore, - useUserSession -} from "@code0-tech/pictor"; +import {Button, Col, Flex, Spacing, Text, TextInput, toast, useForm, useService} from "@code0-tech/pictor"; import Link from "next/link"; -import {useRouter} from "next/navigation"; +import {useParams, useRouter} from "next/navigation"; import {ProjectService} from "@edition/project/Project.service"; -import {UserService} from "@edition/user/User.service"; +import {Namespace} from "@code0-tech/sagittarius-graphql-types"; -export const PersonalProjectsCreatePage: React.FC = () => { +export const NamespaceProjectsCreatePage: React.FC = () => { - const currentSession = useUserSession() - const userStore = useStore(UserService) - const userService = useService(UserService) + const params = useParams() const projectService = useService(ProjectService) const [, startTransition] = React.useTransition() const router = useRouter() - const currentUser = React.useMemo(() => userService.getById(currentSession?.user?.id), [currentSession, userStore]) + const namespaceIndex = params?.namespaceId as string + const namespaceId: Namespace['id'] = `gid://sagittarius/Namespace/${namespaceIndex as unknown as number}` + + //TODO: user abilities for project creation within namespace const [inputs, validate] = useForm({ initialValues: { @@ -47,7 +36,7 @@ export const PersonalProjectsCreatePage: React.FC = () => { }, onSubmit: (values) => { startTransition(() => { - if (!currentUser?.namespace?.id) { + if (!namespaceId) { toast({ title: "The current user does not have a personal namespace.", color: "error", @@ -58,11 +47,10 @@ export const PersonalProjectsCreatePage: React.FC = () => { projectService.projectCreate({ name: values.name as unknown as string, description: values.description as unknown as string, - namespaceId: currentUser.namespace.id + namespaceId: namespaceId }).then(payload => { if ((payload?.errors?.length ?? 0) <= 0) { - router.push("/") - //router.push(`/namespace/${currentUser.namespace?.id}`) + router.push(`/namespace/${namespaceIndex}/projects`) } }) }) @@ -72,7 +60,7 @@ export const PersonalProjectsCreatePage: React.FC = () => { return - Create new personal project + Create new project @@ -94,11 +82,11 @@ export const PersonalProjectsCreatePage: React.FC = () => { diff --git a/src/packages/ce/src/dashboard/namespace/NamespaceProjectsPage.tsx b/src/packages/ce/src/dashboard/namespace/NamespaceProjectsPage.tsx new file mode 100644 index 00000000..c66e04c2 --- /dev/null +++ b/src/packages/ce/src/dashboard/namespace/NamespaceProjectsPage.tsx @@ -0,0 +1,8 @@ +"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 new file mode 100644 index 00000000..5a72aa44 --- /dev/null +++ b/src/packages/ce/src/dashboard/namespace/NamespaceProjectsView.tsx @@ -0,0 +1,36 @@ +"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 + + + + + + + + + {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 new file mode 100644 index 00000000..8c21c806 --- /dev/null +++ b/src/packages/ce/src/dashboard/namespace/NamespaceTabView.tsx @@ -0,0 +1,103 @@ +"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 + ) ? ( + + + + ) : null + }, [namespace, parentOrganization]) + + return + + + + + + + + + + + + + + + + + + {settings} + + + +} \ No newline at end of file diff --git a/src/packages/ce/src/dashboard/application/OrganizationCreatePage.tsx b/src/packages/ce/src/dashboard/organization/OrganizationCreatePage.tsx similarity index 100% rename from src/packages/ce/src/dashboard/application/OrganizationCreatePage.tsx rename to src/packages/ce/src/dashboard/organization/OrganizationCreatePage.tsx diff --git a/src/packages/ce/src/dashboard/organization/OrganizationDeleteView.tsx b/src/packages/ce/src/dashboard/organization/OrganizationDeleteView.tsx new file mode 100644 index 00000000..b261e1d6 --- /dev/null +++ b/src/packages/ce/src/dashboard/organization/OrganizationDeleteView.tsx @@ -0,0 +1,64 @@ +"use client" + +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 {Namespace} from "@code0-tech/sagittarius-graphql-types"; + +export const OrganizationDeleteView: React.FC = () => { + + const params = useParams() + const router = useRouter() + 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]) + + const [, startTransition] = React.useTransition() + + + const deleteOrganization = React.useCallback(() => { + startTransition(() => { + organizationService.organizationDelete({ + organizationId: parentOrganization?.id!! + }).then(payload => { + if ((payload?.errors?.length ?? 0) <= 0) { + toast({ + title: "The organization was successfully deleted.", + color: "success", + dismissible: true, + }) + } + router.push("/") + }) + }) + }, [parentOrganization]) + + return <> + + Delete organization forever + + + + + + Delete organization + + This will delete the organization and cannot be undone. + + + + + + +} \ No newline at end of file diff --git a/src/packages/ce/src/dashboard/organization/OrganizationGeneralSettingsView.tsx b/src/packages/ce/src/dashboard/organization/OrganizationGeneralSettingsView.tsx new file mode 100644 index 00000000..b7d7d5c6 --- /dev/null +++ b/src/packages/ce/src/dashboard/organization/OrganizationGeneralSettingsView.tsx @@ -0,0 +1,77 @@ +"use client" + +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 {Namespace} from "@code0-tech/sagittarius-graphql-types"; + +export const OrganizationGeneralSettingsView: React.FC = () => { + + 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]) + + const [, startTransition] = React.useTransition() + + const initialValues = React.useMemo(() => ({ + name: parentOrganization?.name, + }), [parentOrganization]) + + const [inputs, validate] = useForm({ + initialValues: initialValues, + validate: { + name: (value) => { + if (!value) return "Name is required" + return null + }, + }, + onSubmit: (values) => { + startTransition(() => { + organizationService.organizationUpdate({ + name: values.name!!, + organizationId: parentOrganization?.id!! + }).then(payload => { + if ((payload?.errors?.length ?? 0) <= 0) { + toast({ + title: "The organization was successfully updated.", + color: "success", + dismissible: true, + }) + } + }) + }) + } + }) + + return <> + + General adjustments + + + + + + Name + + + + + + + + + +} \ 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 new file mode 100644 index 00000000..92c36b27 --- /dev/null +++ b/src/packages/ce/src/dashboard/organization/OrganizationSettingsPage.tsx @@ -0,0 +1,117 @@ +"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])} + + + + + + + + + + + + + + + + + }> + <> + + + + + + + + + + + + + + + + +} \ No newline at end of file diff --git a/src/packages/ce/src/dashboard/organization/OrganizationUpgradeView.tsx b/src/packages/ce/src/dashboard/organization/OrganizationUpgradeView.tsx new file mode 100644 index 00000000..8f4f84f8 --- /dev/null +++ b/src/packages/ce/src/dashboard/organization/OrganizationUpgradeView.tsx @@ -0,0 +1,176 @@ +"use client" + +import React from "react"; +import { + AuroraBackground, + Badge, + Button, + Card, + Col, + Flex, + Row, + SegmentedControl, + SegmentedControlItem, + Spacing, + Text +} from "@code0-tech/pictor"; +import {IconArrowRight, IconBuilding, IconGitBranch, IconServer, IconUserCog} from "@tabler/icons-react"; + +export const OrganizationUpgradeView: React.FC = () => { + + const [value, setValue] = React.useState("annually") + + return <> + 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 + your organization thrive. + + + + { + if (value )setValue(value) + }} value={value}> + + Billed monthly + + + + Billed annually + + -27% + + + + + + + + + + + + Team self-hosted + + + 7,95€ + per member / month billed monthly + + + + + + + + + Manage projects inside organizations + + + + Advanced role management + + + + Unlimited flows + + + + 10.000 runtime minutes per month + + + + + + + + + + Team cloud + + + +
+ 3,95€ + + -27% + +
+ per member / month + billed annually +
+ + +
+ + + + + Manage projects inside organizations + + + + Advanced role management + + + + Unlimited flows + + + + 10.000 runtime minutes per month + + + + +
+ + + + + Team cloud (pay as you go) + + + 3,95€ + per member / month billed monthly + + + + + + + + + Manage projects inside organizations + + + + Advanced role management + + + + Unlimited flows + + + + 10.000 runtime minutes per month + + + + + +
+ +} \ No newline at end of file diff --git a/src/packages/ce/src/dashboard/application/OrganizationsPage.tsx b/src/packages/ce/src/dashboard/organization/OrganizationsPage.tsx similarity index 57% rename from src/packages/ce/src/dashboard/application/OrganizationsPage.tsx rename to src/packages/ce/src/dashboard/organization/OrganizationsPage.tsx index 9d988c78..cf663f27 100644 --- a/src/packages/ce/src/dashboard/application/OrganizationsPage.tsx +++ b/src/packages/ce/src/dashboard/organization/OrganizationsPage.tsx @@ -1,7 +1,7 @@ "use client" import React from "react"; -import {OrganizationsView} from "@edition/dashboard/application/OrganizationsView"; +import {OrganizationsView} from "@edition/dashboard/organization/OrganizationsView"; export const OrganizationsPage = () => { return diff --git a/src/packages/ce/src/dashboard/application/OrganizationsView.tsx b/src/packages/ce/src/dashboard/organization/OrganizationsView.tsx similarity index 68% rename from src/packages/ce/src/dashboard/application/OrganizationsView.tsx rename to src/packages/ce/src/dashboard/organization/OrganizationsView.tsx index 3f9552a1..c43533f5 100644 --- a/src/packages/ce/src/dashboard/application/OrganizationsView.tsx +++ b/src/packages/ce/src/dashboard/organization/OrganizationsView.tsx @@ -1,24 +1,32 @@ +"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 - } placeholder={"Find an 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/application/PersonalProjectsView.tsx b/src/packages/ce/src/dashboard/personal-project/PersonalProjectsView.tsx similarity index 85% rename from src/packages/ce/src/dashboard/application/PersonalProjectsView.tsx rename to src/packages/ce/src/dashboard/personal-project/PersonalProjectsView.tsx index 28573be9..9eeb08cd 100644 --- a/src/packages/ce/src/dashboard/application/PersonalProjectsView.tsx +++ b/src/packages/ce/src/dashboard/personal-project/PersonalProjectsView.tsx @@ -2,7 +2,8 @@ import React from "react"; import { Button, DNamespaceProjectList, - Flex, Spacing, + Flex, + Spacing, Text, TextInput, useService, @@ -20,6 +21,8 @@ export const PersonalProjectsView: React.FC = () => { 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 @@ -34,8 +37,7 @@ export const PersonalProjectsView: React.FC = () => { Personal projects
- } placeholder={"Find a personal project..."}/> - + diff --git a/src/packages/ce/src/dashboard/role/RoleCreatePage.tsx b/src/packages/ce/src/dashboard/role/RoleCreatePage.tsx new file mode 100644 index 00000000..0fa649b0 --- /dev/null +++ b/src/packages/ce/src/dashboard/role/RoleCreatePage.tsx @@ -0,0 +1,38 @@ +"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. + + + + + + + + + + + + +} \ 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 new file mode 100644 index 00000000..a3a4b96b --- /dev/null +++ b/src/packages/ce/src/dashboard/role/RoleSettingsPage.tsx @@ -0,0 +1,421 @@ +"use client" + +import React from "react"; +import type {Namespace, NamespaceRole, NamespaceRoleAbility, Scalars} from "@code0-tech/sagittarius-graphql-types"; +import {useParams} from "next/navigation"; +import { + Alert, + AuroraBackground, + Button, + Card, + CheckboxInput, + Col, + DLayout, + DNamespaceProjectView, + Flex, + Row, + Spacing, + Text, + toast, + useForm, + useService, + useStore +} from "@code0-tech/pictor"; +import {RoleService} from "@edition/role/Role.service"; +import {ProjectService} from "@edition/project/Project.service"; +import {Tab, TabContent, TabList, TabTrigger} from "@code0-tech/pictor/dist/components/tab/Tab"; +import CardSection from "@code0-tech/pictor/dist/components/card/CardSection"; +import {DNamespaceRolePermissions} from "@code0-tech/pictor/dist/components/d-role/DNamespaceRolePermissions"; +import DNamespaceProjectMenu from "@code0-tech/pictor/dist/components/d-project/DNamespaceProjectMenu"; +import {DNamespaceProjectContent} from "@code0-tech/pictor/dist/components/d-project/DNamespaceProjectContent"; +import {IconTrash} from "@tabler/icons-react"; + +type Permission = { + label: string + ability: NamespaceRoleAbility | string +} + +type PermissionGroup = { + title: string + permissions: Permission[] +} + +type PermissionObject = { + [key: NamespaceRoleAbility | string]: boolean +} + +type PermissionTemplate = { + name: string + abilities: PermissionObject +} + + +const permissions: PermissionGroup[] = [ + { + title: "Member permissions", + permissions: [ + {label: "Assign member roles", ability: "ASSIGN_MEMBER_ROLES"}, + {label: "Add member", ability: "INVITE_MEMBER"}, + {label: "Remove member", ability: "DELETE_MEMBER"}, + ] + }, + { + title: "Project permissions", + permissions: [ + {label: "Create project", ability: "CREATE_NAMESPACE_PROJECT"}, + {label: "Update project", ability: "UPDATE_NAMESPACE_PROJECT"}, + {label: "Delete project", ability: "DELETE_NAMESPACE_PROJECT"}, + {label: "Read project", ability: "READ_NAMESPACE_PROJECT"}, + ] + }, + { + title: "Role permissions", + permissions: [ + {label: "Create role", ability: "CREATE_NAMESPACE_ROLE"}, + {label: "Update role", ability: "UPDATE_NAMESPACE_ROLE"}, + {label: "Limit roles to projects", ability: "ASSIGN_ROLE_PROJECTS"}, + {label: "Assign role abilities", ability: "ASSIGN_ROLE_ABILITIES"}, + ] + }, + { + title: "Runtime permissions", + permissions: [ + {label: "Create runtime", ability: "CREATE_RUNTIME"}, + {label: "Update runtime", ability: "UPDATE_RUNTIME"}, + {label: "Delete runtime", ability: "DELETE_RUNTIME"}, + {label: "Rotate runtime token", ability: "ROTATE_RUNTIME_TOKEN"}, + {label: "Assign runtime to projects", ability: "ASSIGN_PROJECT_RUNTIMES"}, + ] + }, + { + title: "Flow permissions", + permissions: [ + {label: "Create flow", ability: "CREATE_FLOW"}, + {label: "Update flow", ability: "UPDATE_FLOW"}, + {label: "Delete flow", ability: "DELETE_FLOW"}, + ] + }, + { + title: "License permissions", + permissions: [ + {label: "Create license", ability: "CREATE_NAMESPACE_LICENSE"}, + {label: "Read license", ability: "READ_NAMESPACE_LICENSE"}, + {label: "Delete license", ability: "DELETE_NAMESPACE_LICENSE"}, + ] + }, + { + title: "Organization permissions", + permissions: [ + {label: "Update organization", ability: "UPDATE_ORGANIZATION"}, + {label: "Delete organization", ability: "DELETE_ORGANIZATION"}, + ] + }, + { + title: "Administrator permissions", + permissions: [ + {label: "Namespace administrator", ability: "NAMESPACE_ADMINISTRATOR"}, + ] + } +] + +const defaultPermissions = { + ...permissions.reduce((acc, group) => { + group.permissions.forEach(permission => { + acc[permission.ability] = false + }) + return acc + }, {} as Record) +} + +const permissionTemplates: PermissionTemplate[] = [ + { + name: "Flow developer role template", + abilities: { + ...defaultPermissions, + "CREATE_FLOW": true, + "UPDATE_FLOW": true, + "DELETE_FLOW": true, + "CREATE_NAMESPACE_PROJECT": true, + "UPDATE_NAMESPACE_PROJECT": true, + "ASSIGN_PROJECT_RUNTIMES": true + } + }, + { + name: "Team manager role template", + abilities: { + ...defaultPermissions, + "INVITE_MEMBER": true, + "DELETE_MEMBER": true, + "ASSIGN_MEMBER_ROLES": true, + "CREATE_NAMESPACE_ROLE": true, + "UPDATE_NAMESPACE_ROLE": true, + "ASSIGN_ROLE_PROJECTS": true, + "ASSIGN_ROLE_ABILITIES": true + + } + }, + { + name: "Runtime manager role template", + abilities: { + ...defaultPermissions, + "CREATE_RUNTIME": true, + "UPDATE_RUNTIME": true, + "DELETE_RUNTIME": true, + "ROTATE_RUNTIME_TOKEN": true, + "ASSIGN_PROJECT_RUNTIMES": true + } + }, + { + name: "Project manager role template", + abilities: { + ...defaultPermissions, + "CREATE_NAMESPACE_PROJECT": true, + "UPDATE_NAMESPACE_PROJECT": true, + "DELETE_NAMESPACE_PROJECT": true, + "READ_NAMESPACE_PROJECT": true, + "ASSIGN_PROJECT_RUNTIMES": true + } + } +] + +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 projectService = useService(ProjectService) + const projectStore = useStore(ProjectService) + const [, startTransition] = React.useTransition() + + 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]) + const roleAbilities = React.useMemo(() => { + return { + ...permissions.reduce((acc, group) => { + group.permissions.forEach(permission => { + acc[permission.ability] = role?.abilities?.includes(permission.ability as NamespaceRoleAbility) ?? false + }) + return acc + }, {} as Record) + } + }, [role]) + + const roleAssignedProjects = React.useMemo(() => role?.assignedProjects?.nodes?.map(p => p?.id!!) ?? [], [role]) + const [assignedProjectIds, setAssignedProjectIds] = React.useState(roleAssignedProjects) + const [initialValues, setInitialValues] = React.useState(roleAbilities) + + React.useEffect(() => { + setAssignedProjectIds(roleAssignedProjects) + }, [role]) + + React.useEffect(() => { + setInitialValues(roleAbilities) + }, [role]) + + const [inputs, validate] = useForm({ + initialValues: initialValues, + validate: {}, + onSubmit: (values) => { + startTransition(() => { + const updatedAbilities = Object.entries(values) + .filter(([_, enabled]) => enabled) + .map(([ability, _]) => ability as NamespaceRoleAbility) + roleService.roleAssignAbilities({ + roleId: roleId!!, + abilities: updatedAbilities + }).then(payload => { + if ((payload?.errors?.length ?? 0) <= 0) { + toast({ + title: "The permissions were successfully updated.", + color: "success", + dismissible: true, + }) + } + }) + }) + } + }) + + const assignProjects = () => { + startTransition(() => { + roleService.roleAssignProject({ + roleId: roleId, + projectIds: assignedProjectIds as Scalars['NamespaceProjectID']['output'][] + }).then(payload => { + if ((payload?.errors?.length ?? 0) <= 0) { + toast({ + title: "All projects were successfully assigned to the role.", + color: "success", + dismissible: true, + }) + } + }) + }) + } + + const addAssignedProject = (projectId: Scalars['NamespaceProjectID']['output']) => { + setAssignedProjectIds(prevState => { + return [...prevState, projectId] + }) + } + + const removeAssignedProject = (projectId: Scalars['NamespaceProjectID']['output']) => { + setAssignedProjectIds(prevState => { + return prevState.filter(id => id !== projectId) + }) + } + + const filterProjects = React.useCallback((project: DNamespaceProjectView) => { + return !assignedProjectIds.find(projectId => projectId == project.id!!) + }, [assignedProjectIds]) + + return <> + + + + {role?.name} + + + + + + + + + + + + + }> + <> + + + Select from role templates + + + {React.useMemo(() => { + return + {permissionTemplates.map(permissionTemplate => { + + const templateAbilities = Object.entries(permissionTemplate.abilities) + .filter(([_, enabled]) => enabled) + .map(([ability]) => ability as NamespaceRoleAbility); + + const roleAbilities = Object.entries(initialValues) + .filter(([_, enabled]) => enabled) + .map(([ability]) => ability as NamespaceRoleAbility); + + const isSelected = templateAbilities.length === roleAbilities.length + && templateAbilities.every(a => new Set(roleAbilities).has(a)); + + + return + + + {permissionTemplate.name} + + + enabled) + .map(([ability, _]) => ability as NamespaceRoleAbility)}/> + + + {isSelected ? : null} + + + })} + + }, [initialValues, role])} + + +
+ Current stored + permissions + + +
+ +
+ + + {permissions.map(permissionGroup => { + return + + {permissionGroup.title} + + {permissionGroup.permissions.map(permission => ( + <> + + + ))} + + + + + })} + +
+ + + Projects that members can + access + + + addAssignedProject(project.id!!)}> + + + + + + + {(assignedProjectIds.length ?? 0) <= 0 ? ( + + + This role has no project assignments. Members with this role will have access to all + projects in the organization namespace. + + + ) : ( + + {assignedProjectIds.map(projectId => { + return + + + + + + })} + + )} + + +
+
+ +} \ No newline at end of file diff --git a/src/packages/ce/src/dashboard/role/RolesPage.tsx b/src/packages/ce/src/dashboard/role/RolesPage.tsx new file mode 100644 index 00000000..0d93b4b7 --- /dev/null +++ b/src/packages/ce/src/dashboard/role/RolesPage.tsx @@ -0,0 +1,8 @@ +"use client" + +import React from "react"; +import {RolesView} from "@edition/dashboard/role/RolesView"; + +export const RolesPage: React.FC = () => { + return +} \ 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 new file mode 100644 index 00000000..d157f5f8 --- /dev/null +++ b/src/packages/ce/src/dashboard/role/RolesView.tsx @@ -0,0 +1,41 @@ +"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 + + + + + + + + + {rolesList} + + + +} \ No newline at end of file diff --git a/src/packages/ce/src/dashboard/application/RuntimeCreatePage.tsx b/src/packages/ce/src/dashboard/runtime/RuntimeCreatePage.tsx similarity index 77% rename from src/packages/ce/src/dashboard/application/RuntimeCreatePage.tsx rename to src/packages/ce/src/dashboard/runtime/RuntimeCreatePage.tsx index 16e7c58d..bed0d10c 100644 --- a/src/packages/ce/src/dashboard/application/RuntimeCreatePage.tsx +++ b/src/packages/ce/src/dashboard/runtime/RuntimeCreatePage.tsx @@ -15,26 +15,36 @@ import { useUserSession } from "@code0-tech/pictor"; import Link from "next/link"; -import {notFound, useRouter} from "next/navigation"; +import {notFound, useParams, useRouter} from "next/navigation"; import {RuntimeService} from "@edition/runtime/Runtime.service"; import {UserService} from "@edition/user/User.service"; +import {NamespaceService} from "@edition/namespace/Namespace.service"; export const RuntimeCreatePage: React.FC = () => { + const params = useParams() + const namespaceIndex = params.namespaceId as any as number + const runtimeService = useService(RuntimeService) const [, startTransition] = React.useTransition() const [token, setToken] = React.useState(undefined) const router = useRouter() const currentSession = useUserSession() - const userStore = useStore(UserService) const userService = useService(UserService) + const namespaceService = useService(NamespaceService) + const namespaceStore = useStore(NamespaceService) + const currentUser = React.useMemo(() => userService.getById(currentSession?.user?.id), [userStore, currentSession]) + const namespace = React.useMemo(() => namespaceService.getById(`gid://sagittarius/Namespace/${namespaceIndex}`), [namespaceStore, namespaceIndex]) - if (currentUser && !currentUser.admin) { + if (!namespaceIndex && currentUser && !currentUser.admin) { notFound() } + //TODO: user abilities for runtime creation within namespace + // if (namespace.) + const [inputs, validate] = useForm({ initialValues: { name: null, @@ -54,7 +64,8 @@ export const RuntimeCreatePage: React.FC = () => { startTransition(() => { runtimeService.runtimeCreate({ name: values.name as unknown as string, - description: values.description as unknown as string + description: values.description as unknown as string, + ...(namespaceIndex ? {namespaceId: `gid://sagittarius/Namespace/${namespaceIndex}`} : {}) }).then(payload => { if ((payload?.errors?.length ?? 0) <= 0) { if (payload?.runtime?.token) { @@ -65,7 +76,7 @@ export const RuntimeCreatePage: React.FC = () => { color: "error", dismissible: true, }) - router.push("/runtimes") + router.push(namespaceIndex ? `/namespace/${namespaceIndex}/runtimes` : "/runtimes") } } }) @@ -76,11 +87,11 @@ export const RuntimeCreatePage: React.FC = () => { return - {!token ? "Create new global runtime" : "Global runtime created successfully"} + {!token ? "Create new runtime" : "Runtime created successfully"} - Global runtimes are shared runtimes that can be used across multiple organizations. + Runtimes are shared runtimes that can be used across multiple organizations. {!token ? ( @@ -108,14 +119,14 @@ export const RuntimeCreatePage: React.FC = () => { )} - + {!token ? ( ) : null} diff --git a/src/packages/ce/src/dashboard/application/RuntimeSettingPage.tsx b/src/packages/ce/src/dashboard/runtime/RuntimeSettingsPage.tsx similarity index 86% rename from src/packages/ce/src/dashboard/application/RuntimeSettingPage.tsx rename to src/packages/ce/src/dashboard/runtime/RuntimeSettingsPage.tsx index 2096f962..bff41d76 100644 --- a/src/packages/ce/src/dashboard/application/RuntimeSettingPage.tsx +++ b/src/packages/ce/src/dashboard/runtime/RuntimeSettingsPage.tsx @@ -6,40 +6,34 @@ import { Card, DLayout, Flex, - InputLabel, Spacing, Text, TextInput, toast, useForm, useService, - useStore, useUserSession + useStore } from "@code0-tech/pictor"; import {RuntimeService} from "@edition/runtime/Runtime.service"; import {notFound, useParams, useRouter} from "next/navigation"; import {Tab, TabContent, TabList, TabTrigger} from "@code0-tech/pictor/dist/components/tab/Tab"; -import {IconGavel, IconSettings} from "@tabler/icons-react"; import CardSection from "@code0-tech/pictor/dist/components/card/CardSection"; -import {UserService} from "@edition/user/User.service"; -export const RuntimeSettingPage: React.FC = () => { +export const RuntimeSettingsPage: React.FC = () => { + + //TODO: add ability check for runtime settings access for every settings tab + + const params = useParams() + const namespaceId = params.namespaceId as any as number + const runtimeId = params.runtimeId as any as number const runtimeService = useService(RuntimeService) const runtimeStore = useStore(RuntimeService) - const params = useParams() const [, startTransition] = React.useTransition() const [token, setToken] = React.useState(undefined) const router = useRouter() - const currentSession = useUserSession() - const userStore = useStore(UserService) - const userService = useService(UserService) - const currentUser = React.useMemo(() => userService.getById(currentSession?.user?.id), [userStore, currentSession]) - - const runtime = React.useMemo(() => runtimeService.getById(`gid://sagittarius/Runtime/${params.runtimeId as any}`), [runtimeStore, params]) - if (currentUser && !currentUser.admin) { - notFound() - } + const runtime = React.useMemo(() => runtimeService.getById(`gid://sagittarius/Runtime/${runtimeId}`), [runtimeStore, params]) if (runtime?.userAbilities && (!runtime?.userAbilities?.updateRuntime || !runtime.userAbilities.deleteRuntime || !runtime.userAbilities.rotateRuntimeToken)) { notFound() @@ -93,7 +87,7 @@ export const RuntimeSettingPage: React.FC = () => { dismissible: true, }) } - router.push("/runtimes") + router.push(namespaceId ? `/namespace/${namespaceId}/runtimes` : "/runtimes") }) }) }, [runtime, router]) @@ -132,17 +126,19 @@ export const RuntimeSettingPage: React.FC = () => { - Runtime - + - - + + + @@ -150,7 +146,7 @@ export const RuntimeSettingPage: React.FC = () => { <> - General + General adjustments @@ -174,27 +170,9 @@ export const RuntimeSettingPage: React.FC = () => { - - Danger zone - -
- - - - - Delete runtime - - This will delete the runtime and cannot be undone. - - - - - - Access + How to connect
@@ -218,6 +196,27 @@ export const RuntimeSettingPage: React.FC = () => { + + + Delete runtime forever + + +
+ + + + + Delete runtime + + This will delete the runtime and cannot be undone. + + + + + + diff --git a/src/packages/ce/src/dashboard/runtime/RuntimesPage.tsx b/src/packages/ce/src/dashboard/runtime/RuntimesPage.tsx new file mode 100644 index 00000000..ce7dd952 --- /dev/null +++ b/src/packages/ce/src/dashboard/runtime/RuntimesPage.tsx @@ -0,0 +1,42 @@ +"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 + + + + + + + + + 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/usage/UsageView.tsx b/src/packages/ce/src/dashboard/usage/UsageView.tsx new file mode 100644 index 00000000..54321a9a --- /dev/null +++ b/src/packages/ce/src/dashboard/usage/UsageView.tsx @@ -0,0 +1,14 @@ +"use client" + +import React from "react"; +import {Button, Flex, Spacing, Text} from "@code0-tech/pictor"; + +export const UsageView: React.FC = () => { + return <> + Runtime usage + +
+ + + +} \ No newline at end of file diff --git a/src/packages/ce/src/dashboard/application/UsersPage.tsx b/src/packages/ce/src/dashboard/user/UsersPage.tsx similarity index 92% rename from src/packages/ce/src/dashboard/application/UsersPage.tsx rename to src/packages/ce/src/dashboard/user/UsersPage.tsx index dea4cbf0..d015ff55 100644 --- a/src/packages/ce/src/dashboard/application/UsersPage.tsx +++ b/src/packages/ce/src/dashboard/user/UsersPage.tsx @@ -29,7 +29,6 @@ export const UsersPage: React.FC = () => { Users - } placeholder={"Find a user..."}/> diff --git a/src/packages/ce/src/member/Member.service.ts b/src/packages/ce/src/member/Member.service.ts index 689151bc..08f6c42e 100644 --- a/src/packages/ce/src/member/Member.service.ts +++ b/src/packages/ce/src/member/Member.service.ts @@ -1,68 +1,129 @@ -import {DNamespaceMemberReactiveService, DNamespaceMemberView, ReactiveArrayStore} from "@code0-tech/pictor"; import { - Namespace, + DMemberDependencies, + DNamespaceMemberReactiveService, + DNamespaceMemberView, + ReactiveArrayStore +} from "@code0-tech/pictor" +import { + Mutation, + NamespaceMember, NamespacesMembersAssignRolesInput, NamespacesMembersAssignRolesPayload, NamespacesMembersDeleteInput, NamespacesMembersDeletePayload, NamespacesMembersInviteInput, NamespacesMembersInvitePayload, - Query, - User -} from "@code0-tech/sagittarius-graphql-types"; -import {GraphqlClient} from "@core/util/graphql-client"; + Query +} from "@code0-tech/sagittarius-graphql-types" +import {GraphqlClient} from "@core/util/graphql-client" 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" export class MemberService extends DNamespaceMemberReactiveService { private readonly client: GraphqlClient - private i = 0; + private i = 0 constructor(client: GraphqlClient, store: ReactiveArrayStore) { - super(store); + super(store) this.client = client } - getByNamespaceIdAndUserId(namespaceId: Namespace["id"], userId: User["id"]): DNamespaceMemberView | undefined { - if (super.getByNamespaceIdAndUserId(namespaceId, userId)) return super.getByNamespaceIdAndUserId(namespaceId, userId) + values(dependencies?: DMemberDependencies): DNamespaceMemberView[] { + const members = super.values() + if (!dependencies?.namespaceId) return members - this.client.query({ - query: membersQuery, - variables: { - namespaceId: namespaceId, - firstMember: 50, - afterMember: null, - firstRole: 50, - afterRole: null, - } - }).then(result => { - const data = result.data - if (!data) return + const namespaceId = dependencies.namespaceId + const filtered = members.filter(m => m.namespace?.id === namespaceId) - if (data.namespace && data.namespace.members && data.namespace.members.nodes) { - data.namespace.members.nodes.forEach((member) => { - if (member) this.set(this.i++, new DNamespaceMemberView(member)) + if (filtered.length <= 0) { + this.client.query({ + query: membersQuery, + variables: { + namespaceId: namespaceId, + firstMember: 50, + afterMember: null, + firstRole: 50, + afterRole: null, + } + }).then(res => { + const nodes = res.data?.namespace?.members?.nodes ?? [] + nodes.forEach(member => { + if (member && !this.hasById(member.id)) { + this.set(this.i++, new DNamespaceMemberView(member)) + } }) - } - }) + }) + } - return super.getByNamespaceIdAndUserId(namespaceId, userId) + return filtered } - values(): DNamespaceMemberView[] { - return super.values(); + hasById(id: NamespaceMember["id"]): boolean { + const member = super.values().find(o => o.id === id) + return member !== undefined } - memberAssignRoles(payload: NamespacesMembersAssignRolesInput): Promise { - return Promise.resolve(undefined); + async memberAssignRoles(payload: NamespacesMembersAssignRolesInput): Promise { + const result = await this.client.mutate({ + mutation: memberAssignRoleMutation, + variables: { + ...payload + } + }) + + //TODO: should be done by a new query + if (result.data && result.data.namespacesMembersAssignRoles) { + const currentMember = this.getById(payload.memberId) + const index = super.values().findIndex(m => m.id === payload.memberId) + + const newMember = new DNamespaceMemberView({ + ...currentMember?.json(), + roles: { + count: payload.roleIds.length, + nodes: payload.roleIds.map(roleId => ({ id: roleId })) + } + }) + + this.set(index, newMember) + } + + return result.data?.namespacesMembersAssignRoles ?? undefined } - memberDelete(payload: NamespacesMembersDeleteInput): Promise { - return Promise.resolve(undefined); + async memberDelete(payload: NamespacesMembersDeleteInput): Promise { + const result = await this.client.mutate({ + mutation: memberDeleteMutation, + variables: { + ...payload + } + }) + + if (result.data && result.data.namespacesMembersDelete && result.data.namespacesMembersDelete.namespaceMember) { + const member = result.data.namespacesMembersDelete.namespaceMember + const index = this.values().findIndex(m => m.id === member.id) + this.delete(index) + } + + return result.data?.namespacesMembersDelete ?? undefined } - memberInvite(payload: NamespacesMembersInviteInput): Promise { - return Promise.resolve(undefined); + async memberInvite(payload: NamespacesMembersInviteInput): Promise { + const result = await this.client.mutate({ + mutation: memberInviteMutation, + variables: { + ...payload + } + }) + + if (result.data && result.data.namespacesMembersInvite && result.data.namespacesMembersInvite.namespaceMember) { + const member = result.data.namespacesMembersInvite.namespaceMember + this.set(this.i++, new DNamespaceMemberView(member)) + } + + return result.data?.namespacesMembersInvite ?? undefined } } \ No newline at end of file diff --git a/src/packages/ce/src/member/fragments/Member.basic.fragment.graphql b/src/packages/ce/src/member/fragments/Member.basic.fragment.graphql new file mode 100644 index 00000000..e5643d88 --- /dev/null +++ b/src/packages/ce/src/member/fragments/Member.basic.fragment.graphql @@ -0,0 +1,20 @@ +#import "../../user/fragments/User.id.fragment.graphql" +#import "../../namespace/fragments/Namespace.id.fragment.graphql" +fragment BasicMember on NamespaceMember { + id + roles { + count + } + user { + ...UserId + } + namespace { + ...NamespaceId + } + updatedAt + createdAt + userAbilities { + assignMemberRoles + deleteMember + } +} \ No newline at end of file diff --git a/src/packages/ce/src/member/mutations/Member.assignRoles.mutation.graphql b/src/packages/ce/src/member/mutations/Member.assignRoles.mutation.graphql index e69de29b..ccd8f562 100644 --- a/src/packages/ce/src/member/mutations/Member.assignRoles.mutation.graphql +++ b/src/packages/ce/src/member/mutations/Member.assignRoles.mutation.graphql @@ -0,0 +1,15 @@ +mutation memberAssignRoles($memberId: NamespaceMemberID!, $roleIds: [NamespaceRoleID!]!) { + namespacesMembersAssignRoles(input: { + roleIds: $roleIds, + memberId: $memberId + }) { + errors { + ...on Error { + errorCode, + details { + __typename + } + } + } + } +} \ No newline at end of file diff --git a/src/packages/ce/src/member/mutations/Member.delete.mutation.graphql b/src/packages/ce/src/member/mutations/Member.delete.mutation.graphql index e69de29b..d9ef49fd 100644 --- a/src/packages/ce/src/member/mutations/Member.delete.mutation.graphql +++ b/src/packages/ce/src/member/mutations/Member.delete.mutation.graphql @@ -0,0 +1,18 @@ +#import "../fragments/Member.basic.fragment.graphql" +mutation deleteMember($namespaceMemberId: NamespaceMemberID!) { + namespacesMembersDelete(input: { + namespaceMemberId: $namespaceMemberId + }) { + errors { + ...on Error { + errorCode, + details { + __typename + } + } + } + namespaceMember { + ...BasicMember + } + } +} \ No newline at end of file diff --git a/src/packages/ce/src/member/mutations/Member.invite.mutation.graphql b/src/packages/ce/src/member/mutations/Member.invite.mutation.graphql index e69de29b..fa928e3d 100644 --- a/src/packages/ce/src/member/mutations/Member.invite.mutation.graphql +++ b/src/packages/ce/src/member/mutations/Member.invite.mutation.graphql @@ -0,0 +1,19 @@ +#import "../fragments/Member.basic.fragment.graphql" +mutation inviteMember($userId: UserID!, $namespaceId: NamespaceID!) { + namespacesMembersInvite(input: { + userId: $userId, + namespaceId: $namespaceId + }) { + errors { + ...on Error { + errorCode, + details { + __typename + } + } + } + namespaceMember { + ...BasicMember + } + } +} \ No newline at end of file diff --git a/src/packages/ce/src/namespace/fragments/Namespace.fragment.graphql b/src/packages/ce/src/namespace/fragments/Namespace.fragment.graphql index 8a947d38..3e54c883 100644 --- a/src/packages/ce/src/namespace/fragments/Namespace.fragment.graphql +++ b/src/packages/ce/src/namespace/fragments/Namespace.fragment.graphql @@ -1,3 +1,5 @@ +#import "../../organization/fragments/Organization.id.fragment.graphql" +#import "../../user/fragments/User.id.fragment.graphql" fragment Namespace on Namespace { __typename id @@ -23,6 +25,12 @@ fragment Namespace on Namespace { } parent { __typename + ...on Organization { + ...OrganizationId + } + ...on User{ + ...UserId + } } userAbilities { createNamespaceLicense diff --git a/src/packages/ce/src/organization/Organization.service.ts b/src/packages/ce/src/organization/Organization.service.ts index d5d1a242..50063f9e 100644 --- a/src/packages/ce/src/organization/Organization.service.ts +++ b/src/packages/ce/src/organization/Organization.service.ts @@ -5,15 +5,21 @@ import { OrganizationsCreateInput, OrganizationsCreatePayload, OrganizationsDeleteInput, - OrganizationsDeletePayload, Query + OrganizationsDeletePayload, + OrganizationsUpdateInput, + OrganizationsUpdatePayload, + Query } from "@code0-tech/sagittarius-graphql-types"; import {GraphqlClient} from "@core/util/graphql-client"; import createOrganizationMutation from "./mutations/Organization.create.mutation.graphql"; +import updateOrganizationMutation from "./mutations/Organization.update.mutation.graphql"; +import deleteOrganizationMutation from "./mutations/Organization.delete.mutation.graphql"; import organizationQuery from "./queries/Organization.query.graphql"; export class OrganizationService extends DOrganizationReactiveService { private readonly client: GraphqlClient + private i = 0; constructor(client: GraphqlClient, store: ReactiveArrayStore) { super(store); @@ -29,19 +35,14 @@ export class OrganizationService extends DOrganizationReactiveService { if (!data) return if (data.organizations && data.organizations.nodes) { - data.organizations.nodes.forEach((organization, index) => { - if (organization) this.set(index, new DOrganizationView(organization)) + data.organizations.nodes.forEach((organization) => { + if (organization && !this.hasById(organization.id)) this.set(this.i++, new DOrganizationView(organization)) }) } }) return super.values(); } - deleteById(id: Organization["id"]): void { - const index = this.values().findIndex(o => o.id === id) - this.delete(index) - } - hasById(id: Organization["id"]): boolean { const organization = super.values().find(o => o.id === id) return organization !== undefined @@ -66,7 +67,39 @@ export class OrganizationService extends DOrganizationReactiveService { } async organizationDelete(payload: OrganizationsDeleteInput): Promise { - return Promise.resolve(undefined) + const result = await this.client.mutate({ + mutation: deleteOrganizationMutation, + variables: { + ...payload + } + }) + + if (result.data && result.data.organizationsDelete && result.data.organizationsDelete.organization) { + const organization = result.data.organizationsDelete.organization + const index = this.values().findIndex(o => o.id === organization.id) + this.delete(index) + + } + + return result.data?.organizationsDelete ?? undefined + } + + async organizationUpdate(payload: OrganizationsUpdateInput): Promise { + const result = await this.client.mutate({ + mutation: updateOrganizationMutation, + variables: { + ...payload + } + }) + + 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)) + + } + + return result.data?.organizationsUpdate ?? undefined } } \ No newline at end of file diff --git a/src/packages/ce/src/organization/mutations/Organization.delete.mutation.graphql b/src/packages/ce/src/organization/mutations/Organization.delete.mutation.graphql index 2b040e5c..390dd784 100644 --- a/src/packages/ce/src/organization/mutations/Organization.delete.mutation.graphql +++ b/src/packages/ce/src/organization/mutations/Organization.delete.mutation.graphql @@ -1,3 +1,5 @@ +#import '../fragments/Organization.fragment.graphql' + mutation deleteOrganization ($organizationId: OrganizationID!) { organizationsDelete(input: { organizationId: $organizationId diff --git a/src/packages/ce/src/organization/mutations/Organization.update.mutation.graphql b/src/packages/ce/src/organization/mutations/Organization.update.mutation.graphql new file mode 100644 index 00000000..f2ed1954 --- /dev/null +++ b/src/packages/ce/src/organization/mutations/Organization.update.mutation.graphql @@ -0,0 +1,20 @@ +#import '../fragments/Organization.fragment.graphql' + +mutation updateOrganization ($organizationId: OrganizationID!, $name: String!) { + organizationsUpdate(input: { + name: $name, + organizationId: $organizationId + }) { + errors { + ...on Error { + errorCode, + details { + __typename + } + } + } + organization { + ...Organization + } + } +} \ 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 index 889ad6fc..ab94b329 100644 --- a/src/packages/ce/src/project/Project.service.ts +++ b/src/packages/ce/src/project/Project.service.ts @@ -30,26 +30,35 @@ export class ProjectService extends DNamespaceProjectReactiveService { } values(dependencies: DProjectDependencies): DNamespaceProjectView[] { - if (super.values().length > 0) return super.values() - this.client.query({ - query: projectsQuery, - variables: { - namespaceId: dependencies?.namespaceId, - firstProject: 0, - afterProject: null, - }, - }).then(result => { - const data = result.data - if (!data) return + const projects = super.values() + if (!dependencies?.namespaceId) return projects + + const namespaceId = dependencies.namespaceId + const filtered = projects.filter(p => p.namespace?.id === namespaceId) - if (data.namespace && data.namespace.projects && data.namespace.projects.nodes) { - data.namespace.projects.nodes.forEach((project) => { - if (project) this.set(this.i++, new DNamespaceProjectView(project)) + 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 super.values(dependencies); + return filtered } hasById(id: NamespaceProject["id"]): boolean { diff --git a/src/packages/ce/src/project/fragments/Project.id.fragment.graphql b/src/packages/ce/src/project/fragments/Project.id.fragment.graphql new file mode 100644 index 00000000..7fc6e577 --- /dev/null +++ b/src/packages/ce/src/project/fragments/Project.id.fragment.graphql @@ -0,0 +1,4 @@ +fragment ProjectId on NamespaceProject { + __typename + id +} \ No newline at end of file diff --git a/src/packages/ce/src/role/Role.service.ts b/src/packages/ce/src/role/Role.service.ts index e69de29b..46750ec5 100644 --- a/src/packages/ce/src/role/Role.service.ts +++ b/src/packages/ce/src/role/Role.service.ts @@ -0,0 +1,135 @@ +import { + DNamespaceRoleReactiveService, + DNamespaceRoleView, + DRoleDependencies, + ReactiveArrayStore +} from "@code0-tech/pictor" +import { + Mutation, + NamespaceRole, + NamespacesRolesAssignAbilitiesInput, + NamespacesRolesAssignAbilitiesPayload, + NamespacesRolesAssignProjectsInput, + NamespacesRolesAssignProjectsPayload, + NamespacesRolesCreateInput, + NamespacesRolesCreatePayload, + NamespacesRolesDeleteInput, + NamespacesRolesDeletePayload, + Query +} from "@code0-tech/sagittarius-graphql-types" +import {GraphqlClient} from "@core/util/graphql-client"; +import rolesQuery from "@edition/role/queries/Roles.query.graphql"; +import roleAssignAbilitiesMutation from "@edition/role/mutations/Role.assignAbilities.mutation.graphql"; +import roleAssignProjectsMutation from "@edition/role/mutations/Role.assignProjects.mutation.graphql"; + +export class RoleService extends DNamespaceRoleReactiveService { + + private readonly client: GraphqlClient + private i = 0; + + constructor(client: GraphqlClient, store: ReactiveArrayStore) { + super(store); + this.client = client + } + + values(dependencies: DRoleDependencies): DNamespaceRoleView[] { + const roles = super.values() + if (!dependencies?.namespaceId) return roles + + const namespaceId = dependencies.namespaceId + const filtered = roles.filter(r => r.namespace?.id === namespaceId) + + if (filtered.length === 0) { + this.client.query({ + query: rolesQuery, + variables: { + namespaceId, + firstRole: 50, + afterRole: null, + firstProject: 50, + afterProject: null, + }, + }).then(res => { + const nodes = res.data?.namespace?.roles?.nodes ?? [] + nodes.forEach(role => { + if ( + role && + role.namespace?.id === namespaceId && + !this.hasById(role.id) + ) { + this.set(this.i++, new DNamespaceRoleView(role)) + } + }) + }) + } + + return filtered + } + + hasById(id: NamespaceRole["id"]): boolean { + const role = super.values().find(r => r.id === id) + return role !== undefined + } + + async roleAssignAbilities(payload: NamespacesRolesAssignAbilitiesInput): Promise { + const result = await this.client.mutate({ + mutation: roleAssignAbilitiesMutation, + variables: { + ...payload + } + }) + + //TODO: should be done by a new query + if (result.data && result.data.namespacesRolesAssignAbilities) { + const currentRole = this.getById(payload.roleId) + const index = super.values().findIndex(m => m.id === payload.roleId) + + const newRole = new DNamespaceRoleView({ + ...currentRole?.json(), + abilities: payload.abilities + }) + + this.set(index, newRole) + } + + return result.data?.namespacesRolesAssignAbilities ?? undefined + } + + async roleAssignProject(payload: NamespacesRolesAssignProjectsInput): Promise { + const result = await this.client.mutate({ + mutation: roleAssignProjectsMutation, + variables: { + ...payload + } + }) + + //TODO: should be done by a new query + if (result.data && result.data.namespacesRolesAssignProjects) { + const currentRole = this.getById(payload.roleId) + const index = super.values().findIndex(m => m.id === payload.roleId) + + const newRole = new DNamespaceRoleView({ + ...currentRole?.json(), + assignedProjects: { + count: payload.projectIds.length, + nodes: payload.projectIds.map(id => ({ + id: id, + })), + } + }) + + this.set(index, newRole) + } + + return result.data?.namespacesRolesAssignProjects ?? undefined + } + + roleCreate(payload: NamespacesRolesCreateInput): Promise { + throw new Error("Method not implemented.") + } + + roleDelete(payload: NamespacesRolesDeleteInput): Promise { + throw new Error("Method not implemented.") + } + +} \ No newline at end of file diff --git a/src/packages/ce/src/role/fragments/Role.basic.fragment.graphql b/src/packages/ce/src/role/fragments/Role.basic.fragment.graphql new file mode 100644 index 00000000..eb91d664 --- /dev/null +++ b/src/packages/ce/src/role/fragments/Role.basic.fragment.graphql @@ -0,0 +1,22 @@ +#import "../../namespace/fragments/Namespace.id.fragment.graphql" +fragment RoleBasic on NamespaceRole { + __typename + id + name + createdAt + updatedAt + namespace { + ...NamespaceId + } + assignedProjects { + count + } + abilities + userAbilities { + __typename + assignRoleAbilities + assignRoleProjects + deleteNamespaceRole + updateNamespaceRole + } +} \ No newline at end of file diff --git a/src/packages/ce/src/role/fragments/Role.fragment.graphql b/src/packages/ce/src/role/fragments/Role.fragment.graphql new file mode 100644 index 00000000..150f9d3f --- /dev/null +++ b/src/packages/ce/src/role/fragments/Role.fragment.graphql @@ -0,0 +1,31 @@ +#import "../../namespace/fragments/Namespace.id.fragment.graphql" +#import "../../project/fragments/Project.id.fragment.graphql" +fragment Role on NamespaceRole { + __typename + id + name + createdAt + updatedAt + namespace { + ...NamespaceId + } + assignedProjects (first: $firstProject, after: $afterProject) { + count + nodes { + ...ProjectId + } + pageInfo { + endCursor + hasNextPage + } + + } + abilities + userAbilities { + __typename + assignRoleAbilities + assignRoleProjects + deleteNamespaceRole + updateNamespaceRole + } +} \ No newline at end of file diff --git a/src/packages/ce/src/role/fragments/Role.id.fragment.graphql b/src/packages/ce/src/role/fragments/Role.id.fragment.graphql new file mode 100644 index 00000000..8cab8db7 --- /dev/null +++ b/src/packages/ce/src/role/fragments/Role.id.fragment.graphql @@ -0,0 +1,4 @@ +fragment RoleId on NamespaceRole { + __typename + id +} \ No newline at end of file diff --git a/src/packages/ce/src/role/mutations/Role.assignAbilities.mutation.graphql b/src/packages/ce/src/role/mutations/Role.assignAbilities.mutation.graphql new file mode 100644 index 00000000..302e71f7 --- /dev/null +++ b/src/packages/ce/src/role/mutations/Role.assignAbilities.mutation.graphql @@ -0,0 +1,16 @@ +mutation roleAssignAbilities($roleId: NamespaceRoleID!, $abilities: [NamespaceRoleAbility!]!) { + namespacesRolesAssignAbilities(input: { + roleId: $roleId, + abilities: $abilities + }) { + errors { + ...on Error { + errorCode, + details { + __typename + } + } + } + + } +} \ No newline at end of file diff --git a/src/packages/ce/src/role/mutations/Role.assignProjects.mutation.graphql b/src/packages/ce/src/role/mutations/Role.assignProjects.mutation.graphql new file mode 100644 index 00000000..d3cf4b7d --- /dev/null +++ b/src/packages/ce/src/role/mutations/Role.assignProjects.mutation.graphql @@ -0,0 +1,16 @@ +mutation roleAssignProjects($roleId: NamespaceRoleID!, $projectIds: [NamespaceProjectID!]!) { + namespacesRolesAssignProjects(input: { + roleId: $roleId, + projectIds: $projectIds + }) { + errors { + ...on Error { + errorCode, + details { + __typename + } + } + } + + } +} \ No newline at end of file diff --git a/src/packages/ce/src/role/mutations/Role.create.mutation.graphql b/src/packages/ce/src/role/mutations/Role.create.mutation.graphql new file mode 100644 index 00000000..df98c3fa --- /dev/null +++ b/src/packages/ce/src/role/mutations/Role.create.mutation.graphql @@ -0,0 +1,18 @@ +mutation createRole($namespaceId: NamespaceID!, $name: String!) { + namespacesRolesCreate(input: { + namespaceId: $namespaceId, + name: $name, + }) { + errors { + ...on Error { + errorCode, + details { + __typename + } + } + } + namespaceRole { + ...RoleBasic + } + } +} \ No newline at end of file diff --git a/src/packages/ce/src/role/queries/Roles.query.graphql b/src/packages/ce/src/role/queries/Roles.query.graphql new file mode 100644 index 00000000..9fe0cc77 --- /dev/null +++ b/src/packages/ce/src/role/queries/Roles.query.graphql @@ -0,0 +1,24 @@ +#import "../fragments/Role.fragment.graphql" +query Roles ( + $namespaceId: NamespaceID!, + + $firstRole: Int!, + $afterRole: String, + + $firstProject: Int!, + $afterProject: String + +) { + namespace(id: $namespaceId) { + roles (first: $firstRole, after: $afterRole) { + count + nodes { + ...Role + } + pageInfo { + endCursor + hasNextPage + } + } + } +} \ No newline at end of file diff --git a/src/packages/ce/src/runtime/Runtime.service.ts b/src/packages/ce/src/runtime/Runtime.service.ts index 0b4b20d0..959e17f4 100644 --- a/src/packages/ce/src/runtime/Runtime.service.ts +++ b/src/packages/ce/src/runtime/Runtime.service.ts @@ -23,6 +23,7 @@ import rotateTokenRuntimeMutation from "./mutations/Runtime.rotateToken.mutation export class RuntimeService extends DRuntimeReactiveService { private readonly client: GraphqlClient + private i = 0 constructor(client: GraphqlClient, store: ReactiveArrayStore) { super(store); @@ -35,52 +36,55 @@ export class RuntimeService extends DRuntimeReactiveService { } values(dependencies?: DRuntimeDependencies): DRuntimeView[] { - if (super.values().length > 0) return super.values(); - - let i = 0; - - this.client.query({ - query: globalRuntimesQuery, - variables: { - firstRuntime: 50, - afterRuntime: null, - firstDataType: 0, - afterDataType: null, - firstFlowType: 0, - afterFlowType: null, - firstProject: 0, - afterProject: null, - }, - }).then(result => { - const data = result.data - if (!data) return - - if (data.globalRuntimes && data.globalRuntimes.nodes) { - data.globalRuntimes.nodes.forEach((runtime) => { - if (runtime) this.set(i++, new DRuntimeView(runtime)) + const runtimes = super.values() + + if (!dependencies?.namespaceId) { + if (runtimes.length === 0) { + this.client.query({ + query: globalRuntimesQuery, + variables: { + firstRuntime: 50, + afterRuntime: null + }, + }).then(res => { + const nodes = res.data?.globalRuntimes?.nodes ?? [] + nodes.forEach(runtime => { + if (runtime && !this.hasById(runtime.id)) { + this.set(this.i++, new DRuntimeView(runtime)) + } + }) }) } - }) - if (dependencies?.namespaceId) { + return runtimes + } + + const namespaceId = dependencies.namespaceId + const filtered = runtimes.filter(r => r.namespace?.id === namespaceId) + + if (filtered.length === 0) { this.client.query({ query: namespaceRuntimesQuery, variables: { - namespaceId: dependencies.namespaceId - } - }).then(result => { - const data = result.data - if (!data) return - - if (data.namespace && data.namespace.runtimes && data.namespace.runtimes.nodes) { - data.namespace.runtimes.nodes.forEach((runtime) => { - if (runtime) this.set(i++, new DRuntimeView(runtime)) - }) - } + namespaceId, + firstRuntime: 50, + afterRuntime: null + }, + }).then(res => { + const nodes = res.data?.namespace?.runtimes?.nodes ?? [] + nodes.forEach(runtime => { + if ( + runtime && + runtime.namespace?.id === namespaceId && + !this.hasById(runtime.id) + ) { + this.set(this.i++, new DRuntimeView(runtime)) + } + }) }) } - return super.values(); + return filtered } async runtimeCreate(payload: RuntimesCreateInput): Promise { diff --git a/src/packages/ce/src/user/User.service.ts b/src/packages/ce/src/user/User.service.ts index 23ebe6b7..51daf1d6 100644 --- a/src/packages/ce/src/user/User.service.ts +++ b/src/packages/ce/src/user/User.service.ts @@ -37,11 +37,14 @@ import registerMutation from "./mutations/User.register.mutation.graphql"; import emailVerificationMutation from "./mutations/User.emailVerification.mutation.graphql"; import passwordResetMutation from "./mutations/User.passwordReset.mutation.graphql" import passwordResetRequestMutation from "./mutations/User.passwordResetRequest.mutation.graphql" -import userQuery from "./queries/User.query.graphql"; +import usersQuery from "./queries/Users.query.graphql"; +import userByUsernameQuery from "./queries/User.byUsername.query.graphql"; +import userByIdQuery from "./queries/User.byId.query.graphql"; export class UserService extends DUserReactiveService { private readonly client: GraphqlClient + private i = 0; constructor(client: GraphqlClient, store: ReactiveArrayStore) { super(store); @@ -51,22 +54,60 @@ export class UserService extends DUserReactiveService { values(): DUserView[] { if (super.values().length > 0) return super.values(); this.client.query({ - query: userQuery + query: usersQuery }).then(result => { - let i = 0; const data = result.data if (!data) return - if (data && data.currentUser) this.set(i++, new DUserView(data.currentUser)) + if (data && data.currentUser && !this.hasById(data.currentUser.id)) this.set(this.i++, new DUserView(data.currentUser)) if (data.users && data.users.nodes) { data.users.nodes.forEach((user) => { - if (user && !(user.id === data.currentUser?.id)) this.set(i++, new DUserView(user)) + if (user && !(user.id === data.currentUser?.id) && !this.hasById(user.id)) this.set(this.i++, new DUserView(user)) }) } }) return super.values(); } + getById(id: User["id"]): DUserView | undefined { + const user = super.getById(id) + if (user) return user + + if (id) { + this.client.query({ + query: userByIdQuery, + variables: { + id: id + } + }).then(result => { + const data = result.data + if (!data) return + + if (data && data.user && !this.hasById(data.user.id)) this.set(this.i++, new DUserView(data.user)) + }) + } + + return super.getById(id) + } + + getByUsername(username: User["username"]): DUserView | undefined { + if (super.getByUsername(username)) return super.getByUsername(username) + + this.client.query({ + query: userByUsernameQuery, + variables: { + username: username ?? "" + } + }).then(result => { + const data = result.data + if (!data) return + + if (data && data.user && !this.hasById(data.user.id)) this.set(this.i++, new DUserView(data.user)) + }) + + return super.getByUsername(username) + } + deleteById(id: User["id"]): void { const index = this.values().findIndex(user => user.id === id) this.delete(index) diff --git a/src/packages/ce/src/user/queries/User.byId.query.graphql b/src/packages/ce/src/user/queries/User.byId.query.graphql new file mode 100644 index 00000000..ba9d12fd --- /dev/null +++ b/src/packages/ce/src/user/queries/User.byId.query.graphql @@ -0,0 +1,6 @@ +#import '../fragments/User.basic.fragment.graphql' +query User ($id: UserID!) { + user (id: $id) { + ...UserBasic + } +} \ No newline at end of file diff --git a/src/packages/ce/src/user/queries/User.byUsername.query.graphql b/src/packages/ce/src/user/queries/User.byUsername.query.graphql new file mode 100644 index 00000000..9e232098 --- /dev/null +++ b/src/packages/ce/src/user/queries/User.byUsername.query.graphql @@ -0,0 +1,6 @@ +#import '../fragments/User.basic.fragment.graphql' +query User ($username: String!) { + user (username: $username) { + ...UserBasic + } +} \ No newline at end of file diff --git a/src/packages/ce/src/user/queries/User.query.graphql b/src/packages/ce/src/user/queries/Users.query.graphql similarity index 70% rename from src/packages/ce/src/user/queries/User.query.graphql rename to src/packages/ce/src/user/queries/Users.query.graphql index f4e0d61a..6cac0624 100644 --- a/src/packages/ce/src/user/queries/User.query.graphql +++ b/src/packages/ce/src/user/queries/Users.query.graphql @@ -1,7 +1,7 @@ #import '../fragments/User.fragment.graphql' #import '../fragments/User.basic.fragment.graphql' -query User($firstUser: Int, $afterUser: String, $firstIdentity: Int, $afterIdentity: String, $firstSession: Int, $afterSession: String) { +query Users($firstUser: Int, $afterUser: String, $firstIdentity: Int, $afterIdentity: String, $firstSession: Int, $afterSession: String) { currentUser { ...User }