add: template editor
This commit is contained in:
292
package-lock.json
generated
292
package-lock.json
generated
@@ -9,10 +9,13 @@
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"assert": "^2.1.0",
|
||||
"axios": "^1.7.9",
|
||||
"bulma": "^1.0.2",
|
||||
"katex": "^0.16.11",
|
||||
"oidc-client-ts": "^3.1.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"process": "^0.11.10",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-markdown": "^9.0.1",
|
||||
@@ -445,25 +448,25 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helpers": {
|
||||
"version": "7.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz",
|
||||
"integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz",
|
||||
"integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.25.9",
|
||||
"@babel/types": "^7.26.0"
|
||||
"@babel/template": "^7.27.0",
|
||||
"@babel/types": "^7.27.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.26.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz",
|
||||
"integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz",
|
||||
"integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.26.0"
|
||||
"@babel/types": "^7.27.0"
|
||||
},
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
@@ -1788,9 +1791,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
|
||||
"integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz",
|
||||
"integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
},
|
||||
@@ -1799,14 +1802,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
|
||||
"integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz",
|
||||
"integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.25.9",
|
||||
"@babel/parser": "^7.25.9",
|
||||
"@babel/types": "^7.25.9"
|
||||
"@babel/code-frame": "^7.26.2",
|
||||
"@babel/parser": "^7.27.0",
|
||||
"@babel/types": "^7.27.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -1854,9 +1857,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz",
|
||||
"integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
|
||||
"integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.25.9",
|
||||
@@ -3649,6 +3652,18 @@
|
||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/assert": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz",
|
||||
"integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.2",
|
||||
"is-nan": "^1.3.2",
|
||||
"object-is": "^1.1.5",
|
||||
"object.assign": "^4.1.4",
|
||||
"util": "^0.12.5"
|
||||
}
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
@@ -3669,9 +3684,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.7.9",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz",
|
||||
"integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
|
||||
"version": "1.8.4",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz",
|
||||
"integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.0",
|
||||
@@ -4103,15 +4118,41 @@
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
|
||||
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
|
||||
"integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.0",
|
||||
"es-define-property": "^1.0.0",
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-intrinsic": "^1.2.4",
|
||||
"set-function-length": "^1.2.1"
|
||||
"set-function-length": "^1.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bound": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
||||
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"get-intrinsic": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -4807,6 +4848,22 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/define-properties": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
|
||||
"integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
|
||||
"dependencies": {
|
||||
"define-data-property": "^1.0.1",
|
||||
"has-property-descriptors": "^1.0.0",
|
||||
"object-keys": "^1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
@@ -5029,6 +5086,19 @@
|
||||
"webpack": "^4 || ^5"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
@@ -5112,12 +5182,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
|
||||
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
|
||||
"dependencies": {
|
||||
"get-intrinsic": "^1.2.4"
|
||||
},
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
@@ -5136,6 +5203,17 @@
|
||||
"integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
||||
@@ -5756,15 +5834,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
|
||||
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"es-define-property": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"es-object-atoms": "^1.1.1",
|
||||
"function-bind": "^1.1.2",
|
||||
"has-proto": "^1.0.1",
|
||||
"has-symbols": "^1.0.3",
|
||||
"hasown": "^2.0.0"
|
||||
"get-proto": "^1.0.1",
|
||||
"gopd": "^1.2.0",
|
||||
"has-symbols": "^1.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"math-intrinsics": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -5782,6 +5865,18 @@
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/get-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||
"dependencies": {
|
||||
"dunder-proto": "^1.0.1",
|
||||
"es-object-atoms": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/get-stream": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
|
||||
@@ -5842,12 +5937,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.1.0.tgz",
|
||||
"integrity": "sha512-FQoVQnqcdk4hVM4JN1eromaun4iuS34oStkdlLENLdpULsuQcTyXj8w7ayhuUfPwEYZ1ZOooOTT6fdA9Vmx/RA==",
|
||||
"dependencies": {
|
||||
"get-intrinsic": "^1.2.4"
|
||||
},
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
@@ -5893,20 +5985,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-proto": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.1.0.tgz",
|
||||
"integrity": "sha512-QLdzI9IIO1Jg7f9GT1gXpPpXArAn6cS31R1eEZqz08Gc+uQ8/XiqHWt17Fiw+2p6oTTIq5GXEpQkAlA88YRl/Q==",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
@@ -6918,6 +6996,21 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-nan": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz",
|
||||
"integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.0",
|
||||
"define-properties": "^1.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-network-error": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz",
|
||||
@@ -8244,9 +8337,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/katex": {
|
||||
"version": "0.16.11",
|
||||
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.11.tgz",
|
||||
"integrity": "sha512-RQrI8rlHY92OLf3rho/Ts8i/XvjgguEjOkO1BEXcU3N8BqPpSzBNwV/G0Ukr+P/l3ivvJUE/Fa/CwbS6HesGNQ==",
|
||||
"version": "0.16.21",
|
||||
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.21.tgz",
|
||||
"integrity": "sha512-XvqR7FgOHtWupfMiigNzmh+MgUVmDGU2kXZm899ZkPfcuoPuFxyHmXsgATDpFZDAXCI8tvinaVcDo8PIIJSo4A==",
|
||||
"funding": [
|
||||
"https://opencollective.com/katex",
|
||||
"https://github.com/sponsors/katex"
|
||||
@@ -8457,6 +8550,14 @@
|
||||
"remove-accents": "0.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-find-and-replace": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz",
|
||||
@@ -9643,6 +9744,48 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/object-is": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz",
|
||||
"integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.7",
|
||||
"define-properties": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/object-keys": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
|
||||
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/object.assign": {
|
||||
"version": "4.1.7",
|
||||
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
|
||||
"integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.8",
|
||||
"call-bound": "^1.0.3",
|
||||
"define-properties": "^1.2.1",
|
||||
"es-object-atoms": "^1.0.0",
|
||||
"has-symbols": "^1.1.0",
|
||||
"object-keys": "^1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/oblivious-set": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz",
|
||||
@@ -9873,6 +10016,11 @@
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/path-browserify": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
|
||||
"integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="
|
||||
},
|
||||
"node_modules/path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
@@ -10103,13 +10251,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prismjs": {
|
||||
"version": "1.29.0",
|
||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz",
|
||||
"integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==",
|
||||
"version": "1.30.0",
|
||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
|
||||
"integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/process": {
|
||||
"version": "0.11.10",
|
||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
|
||||
@@ -12,10 +12,13 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"assert": "^2.1.0",
|
||||
"axios": "^1.7.9",
|
||||
"bulma": "^1.0.2",
|
||||
"katex": "^0.16.11",
|
||||
"oidc-client-ts": "^3.1.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"process": "^0.11.10",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-markdown": "^9.0.1",
|
||||
|
||||
@@ -9,7 +9,7 @@ import Callback from "./components/KeycloakCallbacks/Callback";
|
||||
import Footer from "./components/Footer";
|
||||
import PopupCallback from "./components/KeycloakCallbacks/PopupCallback";
|
||||
import SilentCallback from "./components/KeycloakCallbacks/SilentCallback";
|
||||
import {useHomeMarkdown} from "./utils/markdown-queries";
|
||||
import MarkdownTemplateEditor from "./components/MarkdownTemplate/MarkdownTemplateEditor";
|
||||
|
||||
const App = () => {
|
||||
|
||||
@@ -32,7 +32,9 @@ const App = () => {
|
||||
<Route path="/markdown/create" element={<MarkdownEditor />} />
|
||||
<Route path="/markdown/edit/:id" element={<MarkdownEditor />} />
|
||||
<Route path="/popup_callback" element={<PopupCallback />} />
|
||||
<Route path="silent_callback" element={<SilentCallback />} />
|
||||
<Route path="/silent_callback" element={<SilentCallback />} />
|
||||
<Route path="/template/create" element={<MarkdownTemplateEditor />} />
|
||||
<Route path="/template/edit/:id" element={<MarkdownTemplateEditor />} />
|
||||
</Routes>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@@ -33,7 +33,6 @@ const ConfigProvider = ({ children }) => {
|
||||
return res.json();
|
||||
})
|
||||
.then((data) => {
|
||||
console.log(data);
|
||||
setConfig(data);
|
||||
setIsLoading(false);
|
||||
})
|
||||
|
||||
65
src/components/MarkdownTemplate/EnumsEditor.js
Normal file
65
src/components/MarkdownTemplate/EnumsEditor.js
Normal file
@@ -0,0 +1,65 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
const EnumsEditor = ({ enums, onChange }) => {
|
||||
const [_enums, setEnums] = useState(enums || []);
|
||||
|
||||
return (
|
||||
<div className="box">
|
||||
<ul>
|
||||
{_enums.map((item, index) => (
|
||||
<li key={index} className="field has-addons" style={{ marginBottom: "0.5rem" }}>
|
||||
<div className="control is-expanded">
|
||||
<input
|
||||
className="input is-small"
|
||||
type="text"
|
||||
value={item}
|
||||
onChange={(e) => {
|
||||
const updated = [..._enums];
|
||||
updated[index] = e.target.value;
|
||||
setEnums(updated);
|
||||
onChange(updated);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="control">
|
||||
<button
|
||||
className="button is-small is-danger"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
const updated = [..._enums];
|
||||
updated.splice(index, 1);
|
||||
setEnums(updated);
|
||||
onChange(updated);
|
||||
}}
|
||||
>
|
||||
<span className="icon is-small">
|
||||
<i className="fas fa-times" />
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className="field">
|
||||
<div className="control">
|
||||
<button
|
||||
className="button is-small is-primary"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
const updated = [..._enums, ""];
|
||||
setEnums(updated);
|
||||
onChange(updated);
|
||||
}}
|
||||
>
|
||||
<span className="icon is-small">
|
||||
<i className="fas fa-plus" />
|
||||
</span>
|
||||
<span>Add Enum</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EnumsEditor;
|
||||
16
src/components/MarkdownTemplate/LayoutEditor.js
Normal file
16
src/components/MarkdownTemplate/LayoutEditor.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import React, {useState} from 'react';
|
||||
const LayoutEditor = ({layout, onChange}) => {
|
||||
const [_layout, setLayout] = useState(layout || "");
|
||||
return (
|
||||
<textarea
|
||||
className="textarea"
|
||||
value={_layout}
|
||||
onChange={(e) => {
|
||||
setLayout(e.target.value);
|
||||
onChange(layout);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default LayoutEditor;
|
||||
90
src/components/MarkdownTemplate/MarkdownTemplateEditor.js
Normal file
90
src/components/MarkdownTemplate/MarkdownTemplateEditor.js
Normal file
@@ -0,0 +1,90 @@
|
||||
import React, { useContext, useState } from "react";
|
||||
import { AuthContext } from "../../AuthProvider";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { useMarkdownTemplate, useSaveMarkdownTemplate } from "../../utils/queries/template-queries";
|
||||
import LayoutEditor from "./LayoutEditor";
|
||||
import ParametersManager from "./ParametersManager";
|
||||
import "bulma/css/bulma.min.css";
|
||||
|
||||
const MarkdownTemplateEditor = () => {
|
||||
const { roles } = useContext(AuthContext);
|
||||
if (!roles.includes("admin") || roles.includes("creator"))
|
||||
return <div className="notification is-danger">Permission Denied</div>;
|
||||
|
||||
const navigate = useNavigate();
|
||||
const { id } = useParams();
|
||||
const { data: template, isFetching: templateIsFetching } = useMarkdownTemplate(id);
|
||||
const saveMarkdownTemplate = useSaveMarkdownTemplate();
|
||||
|
||||
const [title, setTitle] = useState(template?.id || "");
|
||||
const [parameters, setParameters] = useState(template?.parameters || []);
|
||||
const [layout, setLayout] = useState(template?.layout || "");
|
||||
|
||||
if (templateIsFetching) {
|
||||
return <p>Loading...</p>;
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
saveMarkdownTemplate.mutate(
|
||||
{ id, data: { title, parameters, layout } },
|
||||
{
|
||||
onSuccess: () => {
|
||||
navigate("/");
|
||||
},
|
||||
onError: () => {
|
||||
alert("Error saving template.");
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="section">
|
||||
<div className="container">
|
||||
<h2 className="title is-4">Markdown Template Editor</h2>
|
||||
<div className="field">
|
||||
<label className="label">Title:</label>
|
||||
<div className="control">
|
||||
<input
|
||||
className="input"
|
||||
type="text"
|
||||
placeholder="Enter template title"
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="columns is-variable is-8">
|
||||
<div className="column">
|
||||
<h3 className="title is-5">Layout</h3>
|
||||
<div className="box">
|
||||
<LayoutEditor
|
||||
layout={layout}
|
||||
parameters={parameters}
|
||||
onChange={(newLayout) => setLayout(newLayout)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="column">
|
||||
<h3 className="title is-5">Parameters</h3>
|
||||
<div className="box">
|
||||
<ParametersManager
|
||||
parameters={parameters}
|
||||
onChange={(newParameters) => setParameters(newParameters)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="field is-grouped">
|
||||
<div className="control">
|
||||
<button className="button is-primary" onClick={handleSave}>
|
||||
Save Template
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default MarkdownTemplateEditor;
|
||||
84
src/components/MarkdownTemplate/ParametersManager.js
Normal file
84
src/components/MarkdownTemplate/ParametersManager.js
Normal file
@@ -0,0 +1,84 @@
|
||||
import React, { useState } from "react";
|
||||
import TypeEditor from "./TypeEditor";
|
||||
|
||||
const ParametersManager = ({ parameters, onChange }) => {
|
||||
const [_parameters, setParameters] = useState(parameters || []);
|
||||
|
||||
const handleAdd = () => {
|
||||
const updated = [
|
||||
..._parameters,
|
||||
{
|
||||
name: "",
|
||||
type: { base_type: "string" }
|
||||
}
|
||||
];
|
||||
setParameters(updated);
|
||||
onChange(updated);
|
||||
};
|
||||
|
||||
const handleNameChange = (index, newName) => {
|
||||
const updated = [..._parameters];
|
||||
updated[index].name = newName;
|
||||
setParameters(updated);
|
||||
onChange(updated);
|
||||
};
|
||||
|
||||
const handleDelete = (index) => {
|
||||
const updated = [..._parameters];
|
||||
updated.splice(index, 1);
|
||||
setParameters(updated);
|
||||
onChange(updated);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="box">
|
||||
<div className="field">
|
||||
<div className="control">
|
||||
<button className="button is-primary" onClick={handleAdd}>
|
||||
Add Parameter
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{_parameters.map((param, index) => (
|
||||
<div key={index} className="box" style={{ marginBottom: "1rem" }}>
|
||||
<div className="field is-grouped is-grouped-multiline">
|
||||
<div className="control is-expanded">
|
||||
<label className="label">Name:</label>
|
||||
<input
|
||||
type="text"
|
||||
className="input"
|
||||
value={param.name}
|
||||
onChange={(e) => handleNameChange(index, e.target.value)}
|
||||
placeholder="Parameter name"
|
||||
/>
|
||||
</div>
|
||||
<div className="control">
|
||||
<button
|
||||
className="button is-danger"
|
||||
onClick={() => handleDelete(index)}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label className="label">Type:</label>
|
||||
<div className="control">
|
||||
<TypeEditor
|
||||
type={param.type}
|
||||
onChange={(newType) => {
|
||||
const updated = [..._parameters];
|
||||
updated[index].type = newType;
|
||||
setParameters(updated);
|
||||
onChange(updated);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ParametersManager;
|
||||
30
src/components/MarkdownTemplate/TemplateSelector.js
Normal file
30
src/components/MarkdownTemplate/TemplateSelector.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import React, {useState} from "react";
|
||||
import {useMarkdownTemplates} from "../../utils/queries/template-queries";
|
||||
|
||||
const TemplateSelector = ({template, onChange}) => {
|
||||
const [_template, setTemplate] = useState(template || {
|
||||
title: "",
|
||||
parameters: [],
|
||||
layout: ""
|
||||
});
|
||||
const {data:templates, isFetching: templatesAreFetching} = useMarkdownTemplates();
|
||||
if(templatesAreFetching) {
|
||||
return <p>Loading...</p>
|
||||
}
|
||||
return (
|
||||
<select
|
||||
value={_template.title}
|
||||
onChange={(e) => {
|
||||
setTemplate(e.target.value);
|
||||
onChange(e.target.value);
|
||||
}}>
|
||||
{
|
||||
templates.map((tmpl, index) => (
|
||||
<option key={index} value={tmpl} >{tmpl.title}</option>
|
||||
))
|
||||
}
|
||||
</select>
|
||||
);
|
||||
};
|
||||
|
||||
export default TemplateSelector;
|
||||
94
src/components/MarkdownTemplate/TypeEditor.js
Normal file
94
src/components/MarkdownTemplate/TypeEditor.js
Normal file
@@ -0,0 +1,94 @@
|
||||
import React from 'react';
|
||||
import EnumsEditor from './EnumsEditor';
|
||||
import TemplateSelector from './TemplateSelector';
|
||||
|
||||
const TypeEditor = ({ type, onChange }) => {
|
||||
const [_type, setType] = React.useState(type || {});
|
||||
const renderExtraFields = () => {
|
||||
switch (_type.base_type) {
|
||||
case 'enum':
|
||||
return <EnumsEditor
|
||||
enums={_type.definition.enums}
|
||||
onChange={(newEnums) => {
|
||||
const updated = {
|
||||
..._type,
|
||||
definition: {
|
||||
..._type.definition,
|
||||
enums: newEnums
|
||||
}
|
||||
};
|
||||
setType(updated);
|
||||
onChange(updated);
|
||||
}}
|
||||
/>;
|
||||
case 'list':
|
||||
return (
|
||||
<div>
|
||||
<TypeEditor
|
||||
extendType={_type.extend_type}
|
||||
onChange={(extendType) => {
|
||||
const updated = {..._type, extend_type: extendType};
|
||||
setType(updated);
|
||||
onChange(updated);
|
||||
}}
|
||||
/>
|
||||
<textarea
|
||||
className="textarea"
|
||||
value={_type.definition.iter_layout}
|
||||
onChange={(e) => {
|
||||
const updated = {
|
||||
..._type,
|
||||
definition: {
|
||||
..._type.definition,
|
||||
iter_layout: e.target.value
|
||||
}
|
||||
};
|
||||
setType(updated);
|
||||
onChange(updated);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
case 'template':
|
||||
return <TemplateSelector
|
||||
template={_type.definition.template}
|
||||
onChange={(newTemplate) => {
|
||||
const updated = {
|
||||
..._type,
|
||||
definition: {
|
||||
..._type.definition,
|
||||
template: newTemplate
|
||||
}
|
||||
};
|
||||
setType(updated);
|
||||
onChange(updated);
|
||||
}}
|
||||
/>;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label>type:</label>
|
||||
<select value={_type.base_type} onChange={(e) => {
|
||||
const updated = {
|
||||
base_type: e.target.value,
|
||||
definition: {}
|
||||
};
|
||||
setType(updated);
|
||||
onChange(updated);
|
||||
}}>
|
||||
<option value="string">string</option>
|
||||
<option value="markdown">markdown</option>
|
||||
<option value="enum">enum</option>
|
||||
<option value="list">list</option>
|
||||
<option value="template">template</option>
|
||||
</select>
|
||||
{renderExtraFields()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TypeEditor;
|
||||
@@ -4,14 +4,18 @@ import "katex/dist/katex.min.css";
|
||||
import "./MarkdownContent.css";
|
||||
import MarkdownView from "./MarkdownView";
|
||||
import PermissionGuard from "../PermissionGuard";
|
||||
import {useMarkdown} from "../../utils/markdown-queries";
|
||||
import {usePath} from "../../utils/path-queries";
|
||||
import {useMarkdown} from "../../utils/queries/markdown-queries";
|
||||
import {usePath} from "../../utils/queries/path-queries";
|
||||
import {useMarkdownSetting} from "../../utils/queries/setting-queries";
|
||||
import {useMarkdownTemplate} from "../../utils/queries/template-queries";
|
||||
|
||||
const MarkdownContent = () => {
|
||||
const { id } = useParams();
|
||||
const [indexTitle, setIndexTitle] = useState(null);
|
||||
const {data: markdown, isLoading, error} = useMarkdown(id);
|
||||
const {data: path, isFetching: isPathFetching} = usePath(markdown?.path_id);
|
||||
const {data: setting, isFetching: isSettingFetching} = useMarkdownSetting(markdown?.setting_id);
|
||||
const {data: template_setting, isFetching: isTemplateSettingFetching} = useMarkdownTemplate(setting?.template_setting_id);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
@@ -21,7 +25,9 @@ const MarkdownContent = () => {
|
||||
}, [markdown, path]);
|
||||
|
||||
|
||||
if (isLoading || isPathFetching) {
|
||||
const notReady = isLoading || isPathFetching || isSettingFetching || isTemplateSettingFetching;
|
||||
|
||||
if (notReady) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
@@ -39,8 +45,7 @@ const MarkdownContent = () => {
|
||||
</Link>
|
||||
</PermissionGuard>
|
||||
</div>
|
||||
|
||||
<MarkdownView content={markdown.content}/>
|
||||
<MarkdownView content={JSON.parse(markdown.content)} template={template_setting}/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,23 +5,29 @@ import "katex/dist/katex.min.css";
|
||||
import "./MarkdownEditor.css";
|
||||
import PathManager from "../PathManager";
|
||||
import MarkdownView from "./MarkdownView";
|
||||
import { useMarkdown, useSaveMarkdown } from "../../utils/markdown-queries";
|
||||
import { useMarkdown, useSaveMarkdown } from "../../utils/queries/markdown-queries";
|
||||
import {useMarkdownSetting} from "../../utils/queries/setting-queries";
|
||||
import {useMarkdownTemplate} from "../../utils/queries/template-queries";
|
||||
import TemplatedEditor from "./TemplatedEditor";
|
||||
|
||||
const MarkdownEditor = () => {
|
||||
const { roles } = useContext(AuthContext);
|
||||
const navigate = useNavigate();
|
||||
const { id } = useParams();
|
||||
const [title, setTitle] = useState("");
|
||||
const [content, setContent] = useState("");
|
||||
const [content, setContent] = useState({});
|
||||
const [shortcut, setShortcut] = useState("");
|
||||
const [pathId, setPathId] = useState(1);
|
||||
const {data: markdown, isLoading, error} = useMarkdown(id);
|
||||
const saveMarkdown = useSaveMarkdown();
|
||||
const {data: setting, isFetching: isSettingFetching} = useMarkdownSetting(markdown?.id);
|
||||
const {data: template, isFetching: isTemplateFetching} = useMarkdownTemplate(setting?.id);
|
||||
|
||||
const notReady = isLoading || isTemplateFetching || isSettingFetching;
|
||||
useEffect(() => {
|
||||
if(markdown){
|
||||
setTitle(markdown.title);
|
||||
setContent(markdown.content);
|
||||
setContent(JSON.parse(markdown.content));
|
||||
setShortcut(markdown.shortcut);
|
||||
setPathId(markdown.path_id);
|
||||
}
|
||||
@@ -29,7 +35,7 @@ const MarkdownEditor = () => {
|
||||
|
||||
const handleSave = () => {
|
||||
saveMarkdown.mutate(
|
||||
{id, data: {title, content, path_id: pathId, shortcut}},
|
||||
{id, data: {title, content: JSON.stringify(content), path_id: pathId, shortcut}},
|
||||
{
|
||||
onSuccess: () => {
|
||||
navigate("/");
|
||||
@@ -45,12 +51,12 @@ const MarkdownEditor = () => {
|
||||
if (!hasPermission)
|
||||
return <div className="notification is-danger">Permission Denied</div>;
|
||||
|
||||
if(isLoading)
|
||||
if(notReady)
|
||||
return <p>Loading...</p>;
|
||||
console.log(markdown?.id);
|
||||
|
||||
if(error)
|
||||
return <p>{error.message || "Failed to load markdown"}</p>;
|
||||
|
||||
return (
|
||||
<div className="container mt-5 markdown-editor-container">
|
||||
<h2 className="title is-4">{id ? "Edit Markdown" : "Create Markdown"}</h2>
|
||||
@@ -93,13 +99,14 @@ const MarkdownEditor = () => {
|
||||
<div className="field">
|
||||
<label className="label">Content</label>
|
||||
<div className="control">
|
||||
<textarea
|
||||
style={{ height: "70vh" }}
|
||||
className="textarea"
|
||||
placeholder="Enter Markdown content"
|
||||
value={content}
|
||||
onChange={(e) => setContent(e.target.value)}
|
||||
></textarea>
|
||||
<TemplatedEditor
|
||||
style={{height: "70vh"}}
|
||||
content={content}
|
||||
template={template}
|
||||
onContentChanged={(k, v) => setContent(
|
||||
prev => ({...prev, [k]: v})
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -120,7 +127,7 @@ const MarkdownEditor = () => {
|
||||
|
||||
<div className="column is-half">
|
||||
<h3 className="subtitle is-5">Preview</h3>
|
||||
<MarkdownView content={content} height='70vh'/>
|
||||
<MarkdownView content={content} template={template} height='70vh'/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,18 +8,68 @@ import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import { okaidia } from "react-syntax-highlighter/dist/esm/styles/prism";
|
||||
import "katex/dist/katex.min.css";
|
||||
import "./MarkdownView.css";
|
||||
import {useLinks} from "../../utils/markdown-queries";
|
||||
import {useLinks} from "../../utils/queries/markdown-queries";
|
||||
|
||||
const MarkdownView = ({ content, height="auto" }) => {
|
||||
const Translate = ({variable, value}) => {
|
||||
if (variable.type.base_type === "markdown" || variable.type.base_type === "string" || variable.type.base_type === "enum") {
|
||||
return value;
|
||||
}
|
||||
if(variable.type.base_type === "list"){
|
||||
if (!variable.type.extend_type)
|
||||
return [];
|
||||
|
||||
return value.map((item, index) => Translate({
|
||||
variable: {name: index, type: variable.type.extend_type},
|
||||
value: item,
|
||||
})).map((item) => variable.type.definition.iter_layout.replaceAll('<item/>', item));
|
||||
}
|
||||
if(variable.type.base_type === "template"){
|
||||
return ParseTemplate({
|
||||
template: variable.type.definition.template,
|
||||
variables: value
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const ParseTemplate = ({template, variables}) => {
|
||||
let res = template.layout;
|
||||
for (const parameter of template.parameters) {
|
||||
res = res.replaceAll(`<${parameter.name}/>`, Translate({
|
||||
variable: parameter,
|
||||
value: variables[parameter.name]
|
||||
}));
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
|
||||
const MarkdownView = ({ content, template, height="auto" }) => {
|
||||
const {data: links, isLoading} = useLinks();
|
||||
|
||||
if (isLoading)
|
||||
return <p>Loading...</p>
|
||||
const definitions = "\n<!-- Definitions -->\n" + links.join("\n");
|
||||
return (<p>Loading...</p>);
|
||||
const linkDefinitions = "\n<!-- Definitions -->\n" + links.join("\n");
|
||||
const _template = template || {
|
||||
parameters: [
|
||||
{
|
||||
name: "markdown",
|
||||
type: {
|
||||
base_type: "markdown",
|
||||
definition: {}
|
||||
}
|
||||
}
|
||||
],
|
||||
layout: "<markdown/>",
|
||||
title: "default"
|
||||
};
|
||||
return (
|
||||
<div className="markdown-preview" style={{height}}>
|
||||
<ReactMarkdown
|
||||
children={content + "\n" + definitions}
|
||||
children={ParseTemplate({
|
||||
template: _template,
|
||||
variables: content
|
||||
}) + "\n" + linkDefinitions}
|
||||
remarkPlugins={[remarkMath, remarkGfm]}
|
||||
rehypePlugins={[rehypeKatex, rehypeRaw]}
|
||||
components={{
|
||||
|
||||
172
src/components/Markdowns/TemplatedEditor.js
Normal file
172
src/components/Markdowns/TemplatedEditor.js
Normal file
@@ -0,0 +1,172 @@
|
||||
import React, {useState} from "react";
|
||||
import {useMarkdownTemplate} from "../../utils/queries/template-queries";
|
||||
|
||||
const TemplatedEditorComponent = ({ variable, value, namespace, onContentChanged }) => {
|
||||
if(variable.type.base_type === "string") {
|
||||
return (
|
||||
<div>
|
||||
<label>{namespace}{variable.name} - {variable.type.base_type}</label>
|
||||
<input
|
||||
type="text"
|
||||
className="input"
|
||||
value={value}
|
||||
onChange={(e) => onContentChanged(variable.name, e.target.value)}
|
||||
/>
|
||||
<hr/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (variable.type.base_type === 'markdown') {
|
||||
return(
|
||||
<div>
|
||||
<label>{namespace}{variable.name} - {variable.type.base_type}</label>
|
||||
<textarea
|
||||
className="textarea"
|
||||
value={value}
|
||||
onChange={(e) => onContentChanged(variable.name, e.target.value)}
|
||||
/>
|
||||
<hr/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if(variable.type.base_type === "enum"){
|
||||
return (
|
||||
<div>
|
||||
<label>{namespace}{variable.name} - {variable.type.base_type}</label>
|
||||
<select
|
||||
value={value}
|
||||
onChange={(e) => onContentChanged(variable.name, e.target.value)}
|
||||
>
|
||||
{variable.type.definition.enums.map((item) => (
|
||||
<option key={item} value={item}>{item}</option>
|
||||
))}
|
||||
</select>
|
||||
<hr/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if(variable.type.base_type === "list"){
|
||||
const [cache, setCache] = useState(value);
|
||||
const defaultValue = variable.type.definition.default || undefined;
|
||||
const addItem = () => {
|
||||
setCache(prev => {
|
||||
const newCache = [...prev, defaultValue];
|
||||
onContentChanged(variable.name, newCache);
|
||||
return newCache;
|
||||
});
|
||||
};
|
||||
const removeItem = (index) => {
|
||||
setCache(prev => {
|
||||
const newCache = prev.filter((_, i) => i!==index);
|
||||
onContentChanged(variable.name, newCache);
|
||||
return newCache;
|
||||
})
|
||||
};
|
||||
const _onContentChanged = (index, value) => {
|
||||
setCache(prev => {
|
||||
const newCache = [...prev];
|
||||
newCache[index] = value;
|
||||
onContentChanged(variable.name, newCache);
|
||||
return newCache;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label>{namespace}{variable.name} - {variable.type.base_type}[{variable.type.extend_type.base_type}]</label>
|
||||
{cache.map((item, index) =>
|
||||
(<div key={index}>
|
||||
<TemplatedEditorComponent
|
||||
variable={{name: index, type: variable.type.extend_type}}
|
||||
value={item}
|
||||
namespace={namespace}
|
||||
onContentChanged={_onContentChanged}
|
||||
/>
|
||||
<button
|
||||
className="button is-danger"
|
||||
type="button"
|
||||
onClick={() => removeItem(index)}
|
||||
>
|
||||
DELETE
|
||||
</button>
|
||||
</div>)
|
||||
)}
|
||||
<button className="button is-warning" type="button" onClick={addItem}>
|
||||
ADD
|
||||
</button>
|
||||
<hr/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if(variable.type.base_type === 'template'){
|
||||
const {data: _template, isFetching: _templateIsFetching} = useMarkdownTemplate(variable.type.definition.template_id);
|
||||
if(_templateIsFetching){
|
||||
return(<p>Loading...</p>);
|
||||
}
|
||||
const _parameters = _template.parameters;
|
||||
const _namespace = namespace + _template.title+ "::"
|
||||
const _onSubChanged = (subKey, subValue) => {
|
||||
const updated = {
|
||||
...value,
|
||||
[subKey]: subValue
|
||||
};
|
||||
onContentChanged(variable.name, updated);
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<label>{namespace}{variable.name} - {variable.type.base_type}</label>
|
||||
{
|
||||
_parameters.map((_variable, index) => (
|
||||
<div key={index}>
|
||||
<TemplatedEditorComponent
|
||||
variable={_variable}
|
||||
value={value[_variable.name]}
|
||||
namespace={_namespace}
|
||||
onContentChanged={_onSubChanged}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
<hr/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const TemplatedEditor = ({content, template, onContentChanged}) => {
|
||||
if(!template){
|
||||
template = {
|
||||
parameters: [
|
||||
{
|
||||
name: "markdown",
|
||||
type: {
|
||||
base_type: "markdown",
|
||||
definition: {}
|
||||
}
|
||||
}
|
||||
],
|
||||
layout: "<markdown/>",
|
||||
title: "default"
|
||||
};
|
||||
}
|
||||
return(
|
||||
<div>
|
||||
{
|
||||
template.parameters.map((variable, index) => (
|
||||
<div key={index}>
|
||||
<TemplatedEditorComponent
|
||||
variable={variable}
|
||||
value={content[variable.name]}
|
||||
namespace={template.title+"::"}
|
||||
onContentChanged={onContentChanged}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default TemplatedEditor;
|
||||
76
src/components/Modals/MarkdownSettingModal.js
Normal file
76
src/components/Modals/MarkdownSettingModal.js
Normal file
@@ -0,0 +1,76 @@
|
||||
import {useCreateMarkdownSetting, useMarkdownSetting} from "../../utils/queries/setting-queries";
|
||||
import {useSaveMarkdown} from "../../utils/queries/markdown-queries";
|
||||
import {useState} from "react";
|
||||
import MarkdownTemplateSettingPanel from "../Settings/MarkdownSettings/MarkdownTemplateSettingPanel";
|
||||
|
||||
const MarkdownSettingModal = ({isOpen, markdown, onClose}) => {
|
||||
const {data: markdownSetting, isFetching: markdownSettingIsFetching} = useMarkdownSetting(markdown?.setting_id || 0);
|
||||
const createMarkdownSetting = useCreateMarkdownSetting();
|
||||
const updateMarkdown = useSaveMarkdown();
|
||||
|
||||
const [activeTab, setActiveTab] = useState("template");
|
||||
|
||||
const handleCreateMarkdownSetting = () => {
|
||||
createMarkdownSetting.mutate({}, {
|
||||
onSuccess: (res) => {
|
||||
updateMarkdown.mutate({
|
||||
id: markdown.id,
|
||||
data: {
|
||||
setting_id: res.id
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if(markdownSettingIsFetching)
|
||||
return(<p>Loading...</p>);
|
||||
|
||||
return (
|
||||
<div className={`modal ${isOpen ? "is-active" : ""}`}>
|
||||
<div className="modal-background" onClick={onClose} />
|
||||
<div className="modal-card" style={{width: "60vw"}}>
|
||||
<header className="modal-card-head">
|
||||
<p className="modal-card-title">Markdown Settings</p>
|
||||
<button
|
||||
className="delete"
|
||||
type="button"
|
||||
aria-label="close"
|
||||
onClick={onClose}
|
||||
/>
|
||||
</header>
|
||||
{
|
||||
markdownSetting ? (
|
||||
<section className="modal-card-body">
|
||||
<button
|
||||
className="button is-primary"
|
||||
type="button"
|
||||
onClick={handleCreateMarkdownSetting}
|
||||
>
|
||||
Create Markdown Setting
|
||||
</button>
|
||||
</section>
|
||||
) : (
|
||||
<section className="modal-card-body">
|
||||
<div className="tabs">
|
||||
<ul>
|
||||
<li className={activeTab==="template" ? "is-active" : ""}>
|
||||
<a onClick={() => setActiveTab("template")}>Template</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{activeTab === "template" && (
|
||||
<MarkdownTemplateSettingPanel
|
||||
markdown={markdownSetting}
|
||||
onClose={onClose}
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {useUpdatePath} from "../../utils/path-queries";
|
||||
import {useCreatePathSetting, usePathSetting} from "../../utils/setting-queries";
|
||||
import {useUpdatePath} from "../../utils/queries/path-queries";
|
||||
import {useCreatePathSetting, usePathSetting} from "../../utils/queries/setting-queries";
|
||||
import WebhookSettingPanel from "../Settings/PathSettings/WebhookSettingPanel";
|
||||
import React, {useState} from "react";
|
||||
const PathSettingModal = ({ isOpen, path, onClose }) => {
|
||||
@@ -18,17 +18,22 @@ const PathSettingModal = ({ isOpen, path, onClose }) => {
|
||||
|
||||
|
||||
if(isPathSettingLoading)
|
||||
return <p>Loading...</p>
|
||||
return (<p>Loading...</p>);
|
||||
|
||||
return (
|
||||
<div className={`modal ${isOpen ? "is-active" : ""}`}>
|
||||
<div className="modal-background" onClick={onClose}></div>
|
||||
<div className="modal-background" onClick={onClose} />
|
||||
<div className="modal-card" style={{width: "60vw"}}>
|
||||
<header className="modal-card-head">
|
||||
<p className="modal-card-title">Path Settings</p>
|
||||
<button type="button" className="delete" aria-label="close" onClick={onClose} />
|
||||
<button
|
||||
type="button"
|
||||
className="delete"
|
||||
aria-label="close"
|
||||
onClick={onClose}
|
||||
/>
|
||||
</header>
|
||||
{!pathSetting && !isPathSettingLoading ? (
|
||||
{!pathSetting ? (
|
||||
<section className="modal-card-body">
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -72,9 +72,7 @@ const MainNavigation = () => {
|
||||
console.log(contentDisposition);
|
||||
if (contentDisposition) {
|
||||
const match = contentDisposition.match(/filename="?([^"]+)"?/);
|
||||
console.log(match);
|
||||
if (match && match[1]) {
|
||||
console.log(match[1]);
|
||||
filename = match[1];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ import React, {useState} from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import PermissionGuard from "../PermissionGuard";
|
||||
import "./PathNode.css";
|
||||
import {useDeletePath, useMovePath, useUpdatePath} from "../../utils/path-queries";
|
||||
import {useIndexMarkdown, useMoveMarkdown} from "../../utils/markdown-queries";
|
||||
import {useDeletePath, useMovePath, useUpdatePath} from "../../utils/queries/path-queries";
|
||||
import {useIndexMarkdown, useMoveMarkdown} from "../../utils/queries/markdown-queries";
|
||||
import MarkdownNode from "./MarkdownNode";
|
||||
import PathSettingModal from "../Modals/PathSettingModal";
|
||||
|
||||
@@ -11,7 +11,7 @@ const PathNode = ({ path, isRoot = false }) => {
|
||||
const [isPathSettingModalOpen, setIsPathSettingModalOpen] = useState(false);
|
||||
const [isExpanded, setIsExpanded] = useState(isRoot);
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [newName, setNewName] = useState(path.name);
|
||||
const [newName, setNewName] = useState(path.name || "");
|
||||
|
||||
const deletePath = useDeletePath();
|
||||
const updatePath = useUpdatePath();
|
||||
@@ -130,8 +130,6 @@ const PathNode = ({ path, isRoot = false }) => {
|
||||
<button
|
||||
className="button is-small is-success"
|
||||
onClick={() => {
|
||||
|
||||
console.log("path", path);
|
||||
setIsPathSettingModalOpen(true);
|
||||
}}
|
||||
type="button"
|
||||
@@ -217,6 +215,7 @@ const PathNode = ({ path, isRoot = false }) => {
|
||||
<MarkdownNode
|
||||
markdown={markdown}
|
||||
handleMoveMarkdown={handleMoveMarkdown}
|
||||
key={markdown.id}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import PermissionGuard from "../PermissionGuard";
|
||||
import PathNode from "./PathNode";
|
||||
import "./SideNavigation.css";
|
||||
import {useDeletePath, usePaths, useUpdatePath} from "../../utils/path-queries";
|
||||
import {useDeletePath, usePaths, useUpdatePath} from "../../utils/queries/path-queries";
|
||||
import React from 'react';
|
||||
import {useTree} from "../../utils/tree-queries";
|
||||
import {useTree} from "../../utils/queries/tree-queries";
|
||||
|
||||
const SideNavigation = () => {
|
||||
const {data: tree, isLoading, error} = useTree();
|
||||
@@ -71,6 +71,12 @@ const SideNavigation = () => {
|
||||
>
|
||||
Create New Markdown
|
||||
</a>
|
||||
<a
|
||||
href="/template/create"
|
||||
className="button is-primary is-small"
|
||||
>
|
||||
Create New Template
|
||||
</a>
|
||||
</PermissionGuard>
|
||||
{!filteredTree || filteredTree.length === 0 ?
|
||||
<p>No Result</p> :
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
|
||||
import React, {useEffect, useState, useRef, useContext} from "react";
|
||||
import {useCreatePath, usePath, usePaths} from "../utils/path-queries";
|
||||
import {useCreatePath, usePaths} from "../utils/queries/path-queries";
|
||||
import { useQueryClient } from "react-query";
|
||||
import "./PathManager.css";
|
||||
import {fetch_} from "../utils/request-utils";
|
||||
import {ConfigContext} from "../ConfigProvider";
|
||||
import PathSettingModal from "./Modals/PathSettingModal";
|
||||
|
||||
|
||||
const PathManager = ({ currentPathId = 1, onPathChange }) => {
|
||||
const { data: currentPath } = usePath(currentPathId);
|
||||
const [currentFullPath, setCurrentFullPath] = useState([{ name: "Root", id: 1 }]);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [dropdownActive, setDropdownActive] = useState(false);
|
||||
@@ -20,11 +17,7 @@ const PathManager = ({ currentPathId = 1, onPathChange }) => {
|
||||
const { data: subPaths, isLoading: isSubPathsLoading, error: subPathsError } = usePaths(currentPathId);
|
||||
const createPath = useCreatePath();
|
||||
const config = useContext(ConfigContext).config;
|
||||
const [isPathSettingModalOpen, setIsPathSettingModalModalOpen] = useState(false);
|
||||
|
||||
const handleSettingClick = () => {
|
||||
setIsPathSettingModalModalOpen(true);
|
||||
}
|
||||
|
||||
const buildFullPath = async (pathId) => {
|
||||
const path = [];
|
||||
@@ -117,31 +110,16 @@ const PathManager = ({ currentPathId = 1, onPathChange }) => {
|
||||
{currentFullPath.map((path, index) => (
|
||||
<span
|
||||
key={path.id}
|
||||
className="breadcrumb-item is-clickable"
|
||||
className="tag is-clickable is-link is-light"
|
||||
onClick={() => handlePathClick(path.id, index)}
|
||||
>
|
||||
{path.name}
|
||||
{index < currentFullPath.length - 1 && " / "}
|
||||
{path.name + "/"}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<div className="control">
|
||||
<span> </span>
|
||||
</div>
|
||||
<div className="control">
|
||||
<button
|
||||
className="button is-small is-primary"
|
||||
type="button"
|
||||
onClick={handleSettingClick}
|
||||
>
|
||||
Settings
|
||||
</button>
|
||||
<PathSettingModal
|
||||
isOpen={isPathSettingModalOpen}
|
||||
path={currentPath}
|
||||
onClose={() => setIsPathSettingModalModalOpen(false)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="path-manager-body">
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import {
|
||||
useCreateMarkdownTemplateSetting,
|
||||
useMarkdownTemplate,
|
||||
useMarkdownTemplates,
|
||||
useMarkdownTemplateSetting, useUpdateMarkdownTemplateSetting
|
||||
} from "../../../utils/queries/template-queries";
|
||||
import {useState} from "react";
|
||||
import {useUpdateMarkdownSetting} from "../../../utils/queries/setting-queries";
|
||||
|
||||
const MarkdownTemplateSettingPanel = ({markdownSetting, onClose}) => {
|
||||
const {data: setting, isFetching: settingIsFetching } = useMarkdownTemplateSetting(markdownSetting.template_setting_id || 0);
|
||||
const {data: templates, isFetching: templatesAreFetching}=useMarkdownTemplates();
|
||||
const {data: template, isFetching: templateIsFetching} = useMarkdownTemplate(setting?.template_id);
|
||||
const [selectedTemplate, setSelectedTemplate] = useState(template);
|
||||
|
||||
const createMarkdownTemplateSetting = useCreateMarkdownTemplateSetting();
|
||||
const updateMarkdownSetting = useUpdateMarkdownSetting();
|
||||
const updateMarkdownTemplateSetting = useUpdateMarkdownTemplateSetting();
|
||||
const handleCreateTemplateSetting = () => {
|
||||
createMarkdownTemplateSetting.mutate({}, {
|
||||
onSuccess: (data) => {
|
||||
updateMarkdownSetting.mutate({
|
||||
id: markdownSetting.id,
|
||||
data: {
|
||||
template_setting_id: data.id,
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleSaveMarkdownTemplateSetting = () => {
|
||||
updateMarkdownTemplateSetting.mutate({
|
||||
id: setting.id,
|
||||
data: {
|
||||
template_id: selectedTemplate.id,
|
||||
}
|
||||
}, {
|
||||
onSuccess: () => alert("Saved"),
|
||||
onError: () => alert("Failed to save"),
|
||||
});
|
||||
onClose();
|
||||
};
|
||||
|
||||
if (settingIsFetching || templatesAreFetching || templatesAreFetching || templateIsFetching) {
|
||||
return (<p>Loading...</p>);
|
||||
}
|
||||
return setting ? (
|
||||
<div className="box" style={{marginTop: "1rem"}}>
|
||||
<h4 className="title is-5">Template Setting</h4>
|
||||
<div className="field">
|
||||
<label className="label">Use Template</label>
|
||||
<div className="select is-fullwidth">
|
||||
<select
|
||||
value={selectedTemplate.title}
|
||||
onChange={(e) => setSelectedTemplate(e.target.value)}
|
||||
>
|
||||
<option value="">(default)</option>
|
||||
{templates.map((_template, index) => (
|
||||
<option key={index} value={_template}>{_template.title}</option>
|
||||
))}
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
className="button is-primary"
|
||||
type="button"
|
||||
onClick={handleSaveMarkdownTemplateSetting}
|
||||
>
|
||||
Save Template Setting
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
className="button is-primary"
|
||||
type="button"
|
||||
onClick={handleCreateTemplateSetting}
|
||||
>
|
||||
Create Template Setting
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default MarkdownTemplateSettingPanel;
|
||||
@@ -4,14 +4,14 @@ import {
|
||||
useCreateWebhookSetting,
|
||||
useUpdateWebhookSetting,
|
||||
useWebhooks,
|
||||
} from "../../../utils/webhook-queries";
|
||||
} from "../../../utils/queries/webhook-queries";
|
||||
|
||||
import {
|
||||
useCreateWebhook,
|
||||
useUpdateWebhook,
|
||||
useDeleteWebhook,
|
||||
} from "../../../utils/webhook-queries";
|
||||
import {useUpdatePathSetting} from "../../../utils/setting-queries";
|
||||
} from "../../../utils/queries/webhook-queries";
|
||||
import {useUpdatePathSetting} from "../../../utils/queries/setting-queries";
|
||||
|
||||
const WebhookSettingPanel = ({pathSetting, onClose}) => {
|
||||
|
||||
@@ -89,7 +89,12 @@ const WebhookSettingPanel = ({pathSetting, onClose}) => {
|
||||
const handleCreateWebhookSetting = () => {
|
||||
createWebhookSetting.mutate({}, {
|
||||
onSuccess: (data) => {
|
||||
updatePathSetting.mutate({id: pathSetting.id, data: {webhook_setting_id: data.id}});
|
||||
updatePathSetting.mutate({
|
||||
id: pathSetting.id,
|
||||
data: {
|
||||
webhook_setting_id: data.id
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -115,9 +120,6 @@ const WebhookSettingPanel = ({pathSetting, onClose}) => {
|
||||
}
|
||||
|
||||
const found = webhooks.find((wh) => wh.id === setting.webhook_id);
|
||||
console.log("found", found);
|
||||
console.log("webhooks", webhooks);
|
||||
console.log("setting.webhook_id", setting.webhook_id);
|
||||
setSelectedUrl(found ? found.hook_url : "");
|
||||
} else {
|
||||
setEnabled(false);
|
||||
@@ -191,8 +193,6 @@ const WebhookSettingPanel = ({pathSetting, onClose}) => {
|
||||
on_events: onEvents,
|
||||
|
||||
};
|
||||
console.log(webhooks);
|
||||
console.log(payload);
|
||||
if(!setting || !setting.id){
|
||||
createWebhookSetting.mutate(payload, {
|
||||
onSuccess: (res) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {useQuery, useMutation, useQueryClient} from 'react-query';
|
||||
import {fetch_} from "./request-utils";
|
||||
import {useConfig} from "../ConfigProvider";
|
||||
import {fetch_} from "../request-utils";
|
||||
import {useConfig} from "../../ConfigProvider";
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useQuery, useMutation, useQueryClient } from "react-query";
|
||||
import { fetch_ } from "./request-utils";
|
||||
import {useConfig} from "../ConfigProvider";
|
||||
import { fetch_ } from "../request-utils";
|
||||
import {useConfig} from "../../ConfigProvider";
|
||||
|
||||
|
||||
export const usePaths = (parent_id) => {
|
||||
@@ -1,5 +1,5 @@
|
||||
import {fetch_} from "./request-utils";
|
||||
import {useConfig} from "../ConfigProvider";
|
||||
import {fetch_} from "../request-utils";
|
||||
import {useConfig} from "../../ConfigProvider";
|
||||
import {useMutation, useQuery, useQueryClient} from "react-query";
|
||||
|
||||
export const usePathSettings = () => {
|
||||
@@ -99,7 +99,7 @@ export const useMarkdownSetting = (setting_id) => {
|
||||
const config = useConfig();
|
||||
return useQuery(
|
||||
["markdown_setting", setting_id],
|
||||
() => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/`), {
|
||||
() => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/${setting_id}`, {}), {
|
||||
enabled: !!setting_id,
|
||||
}
|
||||
);
|
||||
173
src/utils/queries/template-queries.js
Normal file
173
src/utils/queries/template-queries.js
Normal file
@@ -0,0 +1,173 @@
|
||||
import {useConfig} from "../../ConfigProvider";
|
||||
import {useMutation, useQuery, useQueryClient} from "react-query";
|
||||
import {fetch_} from "../request-utils";
|
||||
import {template} from "@babel/core";
|
||||
import {data} from "react-router-dom";
|
||||
|
||||
|
||||
export const useMarkdownTemplate = (template_id) => {
|
||||
const config = useConfig();
|
||||
return useQuery(
|
||||
["markdown_template", template_id],
|
||||
() => fetch_(`${config.BACKEND_HOST}/api/template/markdown/${template_id}`), {
|
||||
enabled: !!template_id,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export const useMarkdownTemplates = () => {
|
||||
const config = useConfig();
|
||||
const queryClient = useQueryClient();
|
||||
return useQuery(
|
||||
"markdown_templates",
|
||||
() => fetch_(`${config.BACKEND_HOST}/api/template/markdown/`), {
|
||||
onSuccess: (data) => {
|
||||
if(data){
|
||||
for(const template of data){
|
||||
queryClient.setQueryData(["markdown_template", template.id], data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export const useUpdateMarkdownTemplate = () => {
|
||||
const config = useConfig();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
({id, data}) => fetch_(`${config.BACKEND_HOST}/api/template/markdown/${id}`, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
{
|
||||
onSuccess: (data) => {
|
||||
queryClient.invalidateQueries(["markdown_template", data.id]);
|
||||
queryClient.invalidateQueries("markdown_templates");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export const useCreateMarkdownTemplate = () => {
|
||||
const config = useConfig();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
(data) => fetch_(`${config.BACKEND_HOST}/api/template/markdown/`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
}),{
|
||||
onSuccess: (data) => {
|
||||
queryClient.invalidateQueries(["markdown_template", data.id]);
|
||||
queryClient.invalidateQueries("markdown_templates");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export const useDeleteMarkdownTemplate = () => {
|
||||
const config = useConfig();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
(id) => fetch_(`${config.BACKEND_HOST}/api/template/markdown/${id}`, {
|
||||
method: "DELETE"
|
||||
}), {
|
||||
onSuccess: (res, variables) => {
|
||||
queryClient.invalidateQueries(["markdown_template", variables]);
|
||||
queryClient.invalidateQueries("markdown_templates");
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export const useSaveMarkdownTemplate = () => {
|
||||
const config = useConfig();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(({id, data}) => {
|
||||
const url = id
|
||||
? `${config.BACKEND_HOST}/api/template/markdown/${id}`
|
||||
: `${config.BACKEND_HOST}/api/template/markdown/`;
|
||||
const method = id? "PUT": "POST";
|
||||
return fetch_(url, {
|
||||
method,
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
},{
|
||||
onSuccess: (data) => {
|
||||
queryClient.invalidateQueries(["markdown_template", data.id]);
|
||||
queryClient.invalidateQueries("markdown_templates");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export const useMarkdownTemplateSettings = () => {
|
||||
const config = useConfig();
|
||||
const queryClient = useQueryClient();
|
||||
return useQuery(
|
||||
"markdown_template_settings",
|
||||
() => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/template/`), {
|
||||
onSuccess: (data) => {
|
||||
if(data){
|
||||
for(const setting of data){
|
||||
queryClient.invalidateQueries(["markdown_template_setting", settings.id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export const useMarkdownTemplateSetting = (setting_id) => {
|
||||
const config = useConfig();
|
||||
const queryClient = useQueryClient();
|
||||
return useQuery(
|
||||
["markdown_template_setting", setting_id],
|
||||
() => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/template/${settings.id}`), {
|
||||
enabled: !!setting_id,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export const useCreateMarkdownTemplateSetting = () => {
|
||||
const config = useConfig();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(({id, data}) => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/template/${id}`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
}), {
|
||||
onSuccess: (data) => {
|
||||
queryClient.invalidateQueries(["markdown_template_setting", data.id]);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateMarkdownTemplateSetting = () => {
|
||||
const config = useConfig();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
({id, data}) => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/template/${id}`, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(data),
|
||||
}),{
|
||||
onSuccess: (res) => {
|
||||
queryClient.invalidateQueries(["markdown_template_setting", res.id]);
|
||||
queryClient.invalidateQueries("markdown_template_settings");
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export const useDeleteMarkdownTemplateSetting = () => {
|
||||
const config = useConfig();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
({id}) => fetch_(`${config.BACKEND_HOST}/api/setting/markdown/template/${id}`, {
|
||||
method: "DELETE",
|
||||
}), {
|
||||
onSuccess: (res, variables) => {
|
||||
queryClient.invalidateQueries(["markdown_template_setting", variables.id]);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import {useQuery, useMutation, useQueryClient} from "react-query";
|
||||
import {fetch_} from "./request-utils";
|
||||
import {useConfig} from "../ConfigProvider";
|
||||
import {fetch_} from "../request-utils";
|
||||
import {useConfig} from "../../ConfigProvider";
|
||||
|
||||
|
||||
export const useTree = () => {
|
||||
@@ -1,5 +1,5 @@
|
||||
import {fetch_ } from "./request-utils"
|
||||
import {useConfig} from "../ConfigProvider";
|
||||
import {fetch_ } from "../request-utils"
|
||||
import {useConfig} from "../../ConfigProvider";
|
||||
import {useMutation, useQuery, useQueryClient} from "react-query";
|
||||
|
||||
export const useWebhooks = () =>{
|
||||
@@ -10,8 +10,11 @@ export const useWebhooks = () =>{
|
||||
() => fetch_(`${config.BACKEND_HOST}/api/webhook/`),
|
||||
{
|
||||
onSuccess: (data) => {
|
||||
if(data)
|
||||
queryClient.setQueryData("webhooks", data);
|
||||
if(data){
|
||||
for(const webhook of data){
|
||||
queryClient.setQueryData(["webhook", data.id], data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -45,7 +48,8 @@ export const useUpdateWebhook = () =>{
|
||||
body: JSON.stringify(data)
|
||||
}),
|
||||
{
|
||||
onSuccess: () => {
|
||||
onSuccess: (res) => {
|
||||
queryClient.invalidateQueries(["webhook", res.id]);
|
||||
queryClient.invalidateQueries("webhooks");
|
||||
}
|
||||
}
|
||||
@@ -60,7 +64,8 @@ export const useDeleteWebhook = () => {
|
||||
method: "DELETE",
|
||||
}),
|
||||
{
|
||||
onSuccess: () => {
|
||||
onSuccess: (res, variables) => {
|
||||
queryClient.invalidateQueries(["webhook", variables.id]);
|
||||
queryClient.invalidateQueries("webhooks");
|
||||
}
|
||||
}
|
||||
@@ -72,7 +77,7 @@ export const useWebhookSettings = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useQuery(
|
||||
"webhook_setting",
|
||||
() => fetch_(`${config.BACKEND_HOST}/api/webhook/setting/`),
|
||||
() => fetch_(`${config.BACKEND_HOST}/api/setting/webhook/`),
|
||||
{
|
||||
onSuccess: (data) => {
|
||||
if(data){
|
||||
@@ -89,7 +94,7 @@ export const useWebhookSetting = (setting_id) => {
|
||||
const config = useConfig();
|
||||
return useQuery(
|
||||
["webhook_setting", setting_id],
|
||||
() => fetch_(`${config.BACKEND_HOST}/api/webhook/setting/${setting_id}`),
|
||||
() => fetch_(`${config.BACKEND_HOST}/api/setting/webhook/${setting_id}`),
|
||||
{
|
||||
enabled: !!setting_id,
|
||||
});
|
||||
@@ -100,7 +105,7 @@ export const useCreateWebhookSetting = () => {
|
||||
const config = useConfig();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
(data) => fetch_(`${config.BACKEND_HOST}/api/webhook/setting/`, {
|
||||
(data) => fetch_(`${config.BACKEND_HOST}/api/setting/webhook/`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(data)
|
||||
}),{
|
||||
@@ -116,7 +121,7 @@ export const useUpdateWebhookSetting = () => {
|
||||
const config = useConfig();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
({id, data}) => fetch_(`${config.BACKEND_HOST}/api/webhook/setting/${id}`, {
|
||||
({id, data}) => fetch_(`${config.BACKEND_HOST}/api/setting/webhook/${id}`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(data)
|
||||
}),{
|
||||
@@ -133,7 +138,7 @@ export const useDeleteWebhookSetting = () => {
|
||||
const config = useConfig();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
(id) => fetch_(`${config.BACKEND_HOST}/api/webhook/setting/${id}`, {
|
||||
(id) => fetch_(`${config.BACKEND_HOST}/api/setting/webhook/${id}`, {
|
||||
method: "DELETE",
|
||||
}),
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
const path = require('path');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
|
||||
const webpack = require('webpack');
|
||||
module.exports = {
|
||||
entry: './src/index.js',
|
||||
output: {
|
||||
@@ -29,6 +29,10 @@ module.exports = {
|
||||
template: "./public/index.html",
|
||||
inject: true
|
||||
}),
|
||||
new webpack.ProvidePlugin({
|
||||
process: 'process/browser'
|
||||
})
|
||||
|
||||
],
|
||||
devServer: {
|
||||
static: path.join(__dirname, 'public'),
|
||||
@@ -36,5 +40,14 @@ module.exports = {
|
||||
open: true,
|
||||
hot: true,
|
||||
historyApiFallback: true,
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
fallback: {
|
||||
path: require.resolve('path-browserify'),
|
||||
fs: false,
|
||||
assert: require.resolve("assert/"),
|
||||
process: require.resolve("process/browser"),
|
||||
}
|
||||
},
|
||||
devtool: 'source-map',
|
||||
};
|
||||
Reference in New Issue
Block a user