first commit

This commit is contained in:
polaris 2024-06-29 01:22:22 -04:00
commit 8926934d2d
242 changed files with 494247 additions and 0 deletions

26
.gitignore vendored Normal file
View File

@ -0,0 +1,26 @@
# Node.js
node_modules/
# Logs
logs
*.log
npm-debug.log*
# Dependency directories
pids
logs
*.pid
*.seed
*.pid.lock
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Environment variables
.env*

View File

@ -0,0 +1,15 @@
{
"pages": {
"/page": [
"static/chunks/webpack.js",
"static/chunks/main-app.js",
"static/chunks/app/page.js"
],
"/layout": [
"static/chunks/webpack.js",
"static/chunks/main-app.js",
"static/css/app/layout.css",
"static/chunks/app/layout.js"
]
}
}

19
.next/build-manifest.json Normal file
View File

@ -0,0 +1,19 @@
{
"polyfillFiles": [
"static/chunks/polyfills.js"
],
"devFiles": [],
"ampDevFiles": [],
"lowPriorityFiles": [
"static/development/_buildManifest.js",
"static/development/_ssgManifest.js"
],
"rootMainFiles": [
"static/chunks/webpack.js",
"static/chunks/main-app.js"
],
"pages": {
"/_app": []
},
"ampFirstPages": []
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

1
.next/package.json Normal file
View File

@ -0,0 +1 @@
{"type": "commonjs"}

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,4 @@
{
"/page": "app/page.js",
"/favicon.ico/route": "app/favicon.ico/route.js"
}

File diff suppressed because one or more lines are too long

643
.next/server/app/page.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
self.__INTERCEPTION_ROUTE_REWRITE_MANIFEST="[]"

View File

@ -0,0 +1 @@
self.__BUILD_MANIFEST={"polyfillFiles":["static/chunks/polyfills.js"],"devFiles":[],"ampDevFiles":[],"lowPriorityFiles":["static/development/_buildManifest.js","static/development/_ssgManifest.js"],"rootMainFiles":["static/chunks/webpack.js","static/chunks/main-app.js"],"pages":{"/_app":[]},"ampFirstPages":[]}

View File

@ -0,0 +1,6 @@
{
"version": 3,
"middleware": {},
"functions": {},
"sortedMiddleware": []
}

View File

@ -0,0 +1 @@
self.__REACT_LOADABLE_MANIFEST="{}"

View File

@ -0,0 +1 @@
self.__NEXT_FONT_MANIFEST="{\"pages\":{},\"app\":{\"/home/polaris/Documents/daphnis/app/layout\":[\"static/media/e11418ac562b8ac1-s.p.woff2\"]},\"appUsingSizeAdjust\":true,\"pagesUsingSizeAdjust\":false}"

View File

@ -0,0 +1 @@
{"pages":{},"app":{"/home/polaris/Documents/daphnis/app/layout":["static/media/e11418ac562b8ac1-s.p.woff2"]},"appUsingSizeAdjust":true,"pagesUsingSizeAdjust":false}

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1 @@
self.__RSC_SERVER_MANIFEST="{\n \"node\": {\n \"53cc5bb38de2c5f5010807f77d18551505069f4c\": {\n \"workers\": {\n \"app/page\": \"(action-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Fpolaris%2FDocuments%2Fdaphnis%2Fauth%2Fcomponents%2Fsignin%2Faction.ts%22%2C%5B%22signIn%22%5D%5D%5D&__client_imported__=true!\"\n },\n \"layer\": {\n \"app/page\": \"action-browser\"\n }\n }\n },\n \"edge\": {},\n \"encryptionKey\": \"n80wJVX22coaSQA6as0aLUKetJyDnlKpxtJSHfa5eYY=\"\n}"

View File

@ -0,0 +1,14 @@
{
"node": {
"53cc5bb38de2c5f5010807f77d18551505069f4c": {
"workers": {
"app/page": "(action-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Fpolaris%2FDocuments%2Fdaphnis%2Fauth%2Fcomponents%2Fsignin%2Faction.ts%22%2C%5B%22signIn%22%5D%5D%5D&__client_imported__=true!"
},
"layer": {
"app/page": "action-browser"
}
}
},
"edge": {},
"encryptionKey": "n80wJVX22coaSQA6as0aLUKetJyDnlKpxtJSHfa5eYY="
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,75 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
exports.id = "vendor-chunks/@swc";
exports.ids = ["vendor-chunks/@swc"];
exports.modules = {
/***/ "(ssr)/./node_modules/@swc/helpers/esm/_class_private_field_loose_base.js":
/*!**************************************************************************!*\
!*** ./node_modules/@swc/helpers/esm/_class_private_field_loose_base.js ***!
\**************************************************************************/
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ _: () => (/* binding */ _class_private_field_loose_base),\n/* harmony export */ _class_private_field_loose_base: () => (/* binding */ _class_private_field_loose_base)\n/* harmony export */ });\nfunction _class_private_field_loose_base(receiver, privateKey) {\n if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {\n throw new TypeError(\"attempted to use private field on non-instance\");\n }\n\n return receiver;\n}\n\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHNzcikvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9fY2xhc3NfcHJpdmF0ZV9maWVsZF9sb29zZV9iYXNlLmpzIiwibWFwcGluZ3MiOiI7Ozs7O0FBQU87QUFDUDtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNnRCIsInNvdXJjZXMiOlsid2VicGFjazovL2RhcGhuaXMvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9fY2xhc3NfcHJpdmF0ZV9maWVsZF9sb29zZV9iYXNlLmpzPzQwNGQiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGZ1bmN0aW9uIF9jbGFzc19wcml2YXRlX2ZpZWxkX2xvb3NlX2Jhc2UocmVjZWl2ZXIsIHByaXZhdGVLZXkpIHtcbiAgICBpZiAoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChyZWNlaXZlciwgcHJpdmF0ZUtleSkpIHtcbiAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcihcImF0dGVtcHRlZCB0byB1c2UgcHJpdmF0ZSBmaWVsZCBvbiBub24taW5zdGFuY2VcIik7XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlY2VpdmVyO1xufVxuZXhwb3J0IHsgX2NsYXNzX3ByaXZhdGVfZmllbGRfbG9vc2VfYmFzZSBhcyBfIH07XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///(ssr)/./node_modules/@swc/helpers/esm/_class_private_field_loose_base.js\n");
/***/ }),
/***/ "(ssr)/./node_modules/@swc/helpers/esm/_class_private_field_loose_key.js":
/*!*************************************************************************!*\
!*** ./node_modules/@swc/helpers/esm/_class_private_field_loose_key.js ***!
\*************************************************************************/
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ _: () => (/* binding */ _class_private_field_loose_key),\n/* harmony export */ _class_private_field_loose_key: () => (/* binding */ _class_private_field_loose_key)\n/* harmony export */ });\nvar id = 0;\n\nfunction _class_private_field_loose_key(name) {\n return \"__private_\" + id++ + \"_\" + name;\n}\n\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHNzcikvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9fY2xhc3NfcHJpdmF0ZV9maWVsZF9sb29zZV9rZXkuanMiLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQTs7QUFFTztBQUNQO0FBQ0E7QUFDK0MiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9kYXBobmlzLy4vbm9kZV9tb2R1bGVzL0Bzd2MvaGVscGVycy9lc20vX2NsYXNzX3ByaXZhdGVfZmllbGRfbG9vc2Vfa2V5LmpzPzQ5MjgiXSwic291cmNlc0NvbnRlbnQiOlsidmFyIGlkID0gMDtcblxuZXhwb3J0IGZ1bmN0aW9uIF9jbGFzc19wcml2YXRlX2ZpZWxkX2xvb3NlX2tleShuYW1lKSB7XG4gICAgcmV0dXJuIFwiX19wcml2YXRlX1wiICsgaWQrKyArIFwiX1wiICsgbmFtZTtcbn1cbmV4cG9ydCB7IF9jbGFzc19wcml2YXRlX2ZpZWxkX2xvb3NlX2tleSBhcyBfIH07XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///(ssr)/./node_modules/@swc/helpers/esm/_class_private_field_loose_key.js\n");
/***/ }),
/***/ "(ssr)/./node_modules/@swc/helpers/esm/_interop_require_default.js":
/*!*******************************************************************!*\
!*** ./node_modules/@swc/helpers/esm/_interop_require_default.js ***!
\*******************************************************************/
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ _: () => (/* binding */ _interop_require_default),\n/* harmony export */ _interop_require_default: () => (/* binding */ _interop_require_default)\n/* harmony export */ });\nfunction _interop_require_default(obj) {\n return obj && obj.__esModule ? obj : { default: obj };\n}\n\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHNzcikvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9faW50ZXJvcF9yZXF1aXJlX2RlZmF1bHQuanMiLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBTztBQUNQLDJDQUEyQztBQUMzQztBQUN5QyIsInNvdXJjZXMiOlsid2VicGFjazovL2RhcGhuaXMvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9faW50ZXJvcF9yZXF1aXJlX2RlZmF1bHQuanM/YjMxOSJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZnVuY3Rpb24gX2ludGVyb3BfcmVxdWlyZV9kZWZhdWx0KG9iaikge1xuICAgIHJldHVybiBvYmogJiYgb2JqLl9fZXNNb2R1bGUgPyBvYmogOiB7IGRlZmF1bHQ6IG9iaiB9O1xufVxuZXhwb3J0IHsgX2ludGVyb3BfcmVxdWlyZV9kZWZhdWx0IGFzIF8gfTtcbiJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///(ssr)/./node_modules/@swc/helpers/esm/_interop_require_default.js\n");
/***/ }),
/***/ "(ssr)/./node_modules/@swc/helpers/esm/_interop_require_wildcard.js":
/*!********************************************************************!*\
!*** ./node_modules/@swc/helpers/esm/_interop_require_wildcard.js ***!
\********************************************************************/
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ _: () => (/* binding */ _interop_require_wildcard),\n/* harmony export */ _interop_require_wildcard: () => (/* binding */ _interop_require_wildcard)\n/* harmony export */ });\nfunction _getRequireWildcardCache(nodeInterop) {\n if (typeof WeakMap !== \"function\") return null;\n\n var cacheBabelInterop = new WeakMap();\n var cacheNodeInterop = new WeakMap();\n\n return (_getRequireWildcardCache = function(nodeInterop) {\n return nodeInterop ? cacheNodeInterop : cacheBabelInterop;\n })(nodeInterop);\n}\nfunction _interop_require_wildcard(obj, nodeInterop) {\n if (!nodeInterop && obj && obj.__esModule) return obj;\n if (obj === null || typeof obj !== \"object\" && typeof obj !== \"function\") return { default: obj };\n\n var cache = _getRequireWildcardCache(nodeInterop);\n\n if (cache && cache.has(obj)) return cache.get(obj);\n\n var newObj = { __proto__: null };\n var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;\n\n for (var key in obj) {\n if (key !== \"default\" && Object.prototype.hasOwnProperty.call(obj, key)) {\n var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;\n if (desc && (desc.get || desc.set)) Object.defineProperty(newObj, key, desc);\n else newObj[key] = obj[key];\n }\n }\n\n newObj.default = obj;\n\n if (cache) cache.set(obj, newObj);\n\n return newObj;\n}\n\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHNzcikvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9faW50ZXJvcF9yZXF1aXJlX3dpbGRjYXJkLmpzIiwibWFwcGluZ3MiOiI7Ozs7O0FBQUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDTztBQUNQO0FBQ0EsdUZBQXVGOztBQUV2Rjs7QUFFQTs7QUFFQSxtQkFBbUI7QUFDbkI7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7O0FBRUE7QUFDQTtBQUMwQyIsInNvdXJjZXMiOlsid2VicGFjazovL2RhcGhuaXMvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9faW50ZXJvcF9yZXF1aXJlX3dpbGRjYXJkLmpzPzRhOWQiXSwic291cmNlc0NvbnRlbnQiOlsiZnVuY3Rpb24gX2dldFJlcXVpcmVXaWxkY2FyZENhY2hlKG5vZGVJbnRlcm9wKSB7XG4gICAgaWYgKHR5cGVvZiBXZWFrTWFwICE9PSBcImZ1bmN0aW9uXCIpIHJldHVybiBudWxsO1xuXG4gICAgdmFyIGNhY2hlQmFiZWxJbnRlcm9wID0gbmV3IFdlYWtNYXAoKTtcbiAgICB2YXIgY2FjaGVOb2RlSW50ZXJvcCA9IG5ldyBXZWFrTWFwKCk7XG5cbiAgICByZXR1cm4gKF9nZXRSZXF1aXJlV2lsZGNhcmRDYWNoZSA9IGZ1bmN0aW9uKG5vZGVJbnRlcm9wKSB7XG4gICAgICAgIHJldHVybiBub2RlSW50ZXJvcCA/IGNhY2hlTm9kZUludGVyb3AgOiBjYWNoZUJhYmVsSW50ZXJvcDtcbiAgICB9KShub2RlSW50ZXJvcCk7XG59XG5leHBvcnQgZnVuY3Rpb24gX2ludGVyb3BfcmVxdWlyZV93aWxkY2FyZChvYmosIG5vZGVJbnRlcm9wKSB7XG4gICAgaWYgKCFub2RlSW50ZXJvcCAmJiBvYmogJiYgb2JqLl9fZXNNb2R1bGUpIHJldHVybiBvYmo7XG4gICAgaWYgKG9iaiA9PT0gbnVsbCB8fCB0eXBlb2Ygb2JqICE9PSBcIm9iamVjdFwiICYmIHR5cGVvZiBvYmogIT09IFwiZnVuY3Rpb25cIikgcmV0dXJuIHsgZGVmYXVsdDogb2JqIH07XG5cbiAgICB2YXIgY2FjaGUgPSBfZ2V0UmVxdWlyZVdpbGRjYXJkQ2FjaGUobm9kZUludGVyb3ApO1xuXG4gICAgaWYgKGNhY2hlICYmIGNhY2hlLmhhcyhvYmopKSByZXR1cm4gY2FjaGUuZ2V0KG9iaik7XG5cbiAgICB2YXIgbmV3T2JqID0geyBfX3Byb3RvX186IG51bGwgfTtcbiAgICB2YXIgaGFzUHJvcGVydHlEZXNjcmlwdG9yID0gT2JqZWN0LmRlZmluZVByb3BlcnR5ICYmIE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3I7XG5cbiAgICBmb3IgKHZhciBrZXkgaW4gb2JqKSB7XG4gICAgICAgIGlmIChrZXkgIT09IFwiZGVmYXVsdFwiICYmIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvYmosIGtleSkpIHtcbiAgICAgICAgICAgIHZhciBkZXNjID0gaGFzUHJvcGVydHlEZXNjcmlwdG9yID8gT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcihvYmosIGtleSkgOiBudWxsO1xuICAgICAgICAgICAgaWYgKGRlc2MgJiYgKGRlc2MuZ2V0IHx8IGRlc2Muc2V0KSkgT2JqZWN0LmRlZmluZVByb3BlcnR5KG5ld09iaiwga2V5LCBkZXNjKTtcbiAgICAgICAgICAgIGVsc2UgbmV3T2JqW2tleV0gPSBvYmpba2V5XTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIG5ld09iai5kZWZhdWx0ID0gb2JqO1xuXG4gICAgaWYgKGNhY2hlKSBjYWNoZS5zZXQob2JqLCBuZXdPYmopO1xuXG4gICAgcmV0dXJuIG5ld09iajtcbn1cbmV4cG9ydCB7IF9pbnRlcm9wX3JlcXVpcmVfd2lsZGNhcmQgYXMgXyB9O1xuIl0sIm5hbWVzIjpbXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///(ssr)/./node_modules/@swc/helpers/esm/_interop_require_wildcard.js\n");
/***/ }),
/***/ "(ssr)/./node_modules/@swc/helpers/esm/_tagged_template_literal_loose.js":
/*!*************************************************************************!*\
!*** ./node_modules/@swc/helpers/esm/_tagged_template_literal_loose.js ***!
\*************************************************************************/
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ _: () => (/* binding */ _tagged_template_literal_loose),\n/* harmony export */ _tagged_template_literal_loose: () => (/* binding */ _tagged_template_literal_loose)\n/* harmony export */ });\nfunction _tagged_template_literal_loose(strings, raw) {\n if (!raw) raw = strings.slice(0);\n\n strings.raw = raw;\n\n return strings;\n}\n\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHNzcikvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9fdGFnZ2VkX3RlbXBsYXRlX2xpdGVyYWxfbG9vc2UuanMiLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBTztBQUNQOztBQUVBOztBQUVBO0FBQ0E7QUFDK0MiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9kYXBobmlzLy4vbm9kZV9tb2R1bGVzL0Bzd2MvaGVscGVycy9lc20vX3RhZ2dlZF90ZW1wbGF0ZV9saXRlcmFsX2xvb3NlLmpzPzI5M2UiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGZ1bmN0aW9uIF90YWdnZWRfdGVtcGxhdGVfbGl0ZXJhbF9sb29zZShzdHJpbmdzLCByYXcpIHtcbiAgICBpZiAoIXJhdykgcmF3ID0gc3RyaW5ncy5zbGljZSgwKTtcblxuICAgIHN0cmluZ3MucmF3ID0gcmF3O1xuXG4gICAgcmV0dXJuIHN0cmluZ3M7XG59XG5leHBvcnQgeyBfdGFnZ2VkX3RlbXBsYXRlX2xpdGVyYWxfbG9vc2UgYXMgXyB9O1xuIl0sIm5hbWVzIjpbXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///(ssr)/./node_modules/@swc/helpers/esm/_tagged_template_literal_loose.js\n");
/***/ }),
/***/ "(rsc)/./node_modules/@swc/helpers/esm/_interop_require_default.js":
/*!*******************************************************************!*\
!*** ./node_modules/@swc/helpers/esm/_interop_require_default.js ***!
\*******************************************************************/
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ _: () => (/* binding */ _interop_require_default),\n/* harmony export */ _interop_require_default: () => (/* binding */ _interop_require_default)\n/* harmony export */ });\nfunction _interop_require_default(obj) {\n return obj && obj.__esModule ? obj : { default: obj };\n}\n\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHJzYykvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9faW50ZXJvcF9yZXF1aXJlX2RlZmF1bHQuanMiLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBTztBQUNQLDJDQUEyQztBQUMzQztBQUN5QyIsInNvdXJjZXMiOlsid2VicGFjazovL2RhcGhuaXMvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9faW50ZXJvcF9yZXF1aXJlX2RlZmF1bHQuanM/MzQ5ZiJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZnVuY3Rpb24gX2ludGVyb3BfcmVxdWlyZV9kZWZhdWx0KG9iaikge1xuICAgIHJldHVybiBvYmogJiYgb2JqLl9fZXNNb2R1bGUgPyBvYmogOiB7IGRlZmF1bHQ6IG9iaiB9O1xufVxuZXhwb3J0IHsgX2ludGVyb3BfcmVxdWlyZV9kZWZhdWx0IGFzIF8gfTtcbiJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///(rsc)/./node_modules/@swc/helpers/esm/_interop_require_default.js\n");
/***/ })
};
;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,25 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
exports.id = "vendor-chunks/clsx";
exports.ids = ["vendor-chunks/clsx"];
exports.modules = {
/***/ "(ssr)/./node_modules/clsx/dist/clsx.mjs":
/*!*****************************************!*\
!*** ./node_modules/clsx/dist/clsx.mjs ***!
\*****************************************/
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ clsx: () => (/* binding */ clsx),\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\nfunction r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (clsx);//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHNzcikvLi9ub2RlX21vZHVsZXMvY2xzeC9kaXN0L2Nsc3gubWpzIiwibWFwcGluZ3MiOiI7Ozs7O0FBQUEsY0FBYyxhQUFhLCtDQUErQyxnREFBZ0QsZUFBZSxRQUFRLElBQUksMENBQTBDLHlDQUF5QyxTQUFnQixnQkFBZ0Isd0NBQXdDLElBQUksbURBQW1ELFNBQVMsaUVBQWUsSUFBSSIsInNvdXJjZXMiOlsid2VicGFjazovL2RhcGhuaXMvLi9ub2RlX21vZHVsZXMvY2xzeC9kaXN0L2Nsc3gubWpzPzZkOTYiXSwic291cmNlc0NvbnRlbnQiOlsiZnVuY3Rpb24gcihlKXt2YXIgdCxmLG49XCJcIjtpZihcInN0cmluZ1wiPT10eXBlb2YgZXx8XCJudW1iZXJcIj09dHlwZW9mIGUpbis9ZTtlbHNlIGlmKFwib2JqZWN0XCI9PXR5cGVvZiBlKWlmKEFycmF5LmlzQXJyYXkoZSkpe3ZhciBvPWUubGVuZ3RoO2Zvcih0PTA7dDxvO3QrKyllW3RdJiYoZj1yKGVbdF0pKSYmKG4mJihuKz1cIiBcIiksbis9Zil9ZWxzZSBmb3IoZiBpbiBlKWVbZl0mJihuJiYobis9XCIgXCIpLG4rPWYpO3JldHVybiBufWV4cG9ydCBmdW5jdGlvbiBjbHN4KCl7Zm9yKHZhciBlLHQsZj0wLG49XCJcIixvPWFyZ3VtZW50cy5sZW5ndGg7ZjxvO2YrKykoZT1hcmd1bWVudHNbZl0pJiYodD1yKGUpKSYmKG4mJihuKz1cIiBcIiksbis9dCk7cmV0dXJuIG59ZXhwb3J0IGRlZmF1bHQgY2xzeDsiXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///(ssr)/./node_modules/clsx/dist/clsx.mjs\n");
/***/ })
};
;

View File

@ -0,0 +1,25 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
exports.id = "vendor-chunks/geist";
exports.ids = ["vendor-chunks/geist"];
exports.modules = {
/***/ "(rsc)/./node_modules/geist/dist/sans.js":
/*!*****************************************!*\
!*** ./node_modules/geist/dist/sans.js ***!
\*****************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ GeistSans: () => (/* reexport default from dynamic */ next_font_local_target_css_path_node_modules_geist_dist_sans_js_import_arguments_src_fonts_geist_sans_Geist_Variable_woff2_variable_font_geist_sans_weight_100_900_variableName_GeistSans___WEBPACK_IMPORTED_MODULE_0___default.a)\n/* harmony export */ });\n/* harmony import */ var next_font_local_target_css_path_node_modules_geist_dist_sans_js_import_arguments_src_fonts_geist_sans_Geist_Variable_woff2_variable_font_geist_sans_weight_100_900_variableName_GeistSans___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! next/font/local/target.css?{\"path\":\"node_modules/geist/dist/sans.js\",\"import\":\"\",\"arguments\":[{\"src\":\"./fonts/geist-sans/Geist-Variable.woff2\",\"variable\":\"--font-geist-sans\",\"weight\":\"100 900\"}],\"variableName\":\"GeistSans\"} */ \"(rsc)/./node_modules/next/font/local/target.css?{\\\"path\\\":\\\"node_modules/geist/dist/sans.js\\\",\\\"import\\\":\\\"\\\",\\\"arguments\\\":[{\\\"src\\\":\\\"./fonts/geist-sans/Geist-Variable.woff2\\\",\\\"variable\\\":\\\"--font-geist-sans\\\",\\\"weight\\\":\\\"100 900\\\"}],\\\"variableName\\\":\\\"GeistSans\\\"}\");\n/* harmony import */ var next_font_local_target_css_path_node_modules_geist_dist_sans_js_import_arguments_src_fonts_geist_sans_Geist_Variable_woff2_variable_font_geist_sans_weight_100_900_variableName_GeistSans___WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(next_font_local_target_css_path_node_modules_geist_dist_sans_js_import_arguments_src_fonts_geist_sans_Geist_Variable_woff2_variable_font_geist_sans_weight_100_900_variableName_GeistSans___WEBPACK_IMPORTED_MODULE_0__);\n\n\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHJzYykvLi9ub2RlX21vZHVsZXMvZ2Vpc3QvZGlzdC9zYW5zLmpzIiwibWFwcGluZ3MiOiI7Ozs7OztBQUVhQTtBQUFBQSIsInNvdXJjZXMiOlsid2VicGFjazovL2RhcGhuaXMvLi9ub2RlX21vZHVsZXMvZ2Vpc3QvZGlzdC9zYW5zLmpzP2U4NWIiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGxvY2FsRm9udCBmcm9tIFwibmV4dC9mb250L2xvY2FsXCI7XG5cbmV4cG9ydCBjb25zdCBHZWlzdFNhbnMgPSBsb2NhbEZvbnQoe1xuICBzcmM6IFwiLi9mb250cy9nZWlzdC1zYW5zL0dlaXN0LVZhcmlhYmxlLndvZmYyXCIsXG4gIHZhcmlhYmxlOiBcIi0tZm9udC1nZWlzdC1zYW5zXCIsXG4gIHdlaWdodDogXCIxMDAgOTAwXCIsXG59KTtcbiJdLCJuYW1lcyI6WyJHZWlzdFNhbnMiXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///(rsc)/./node_modules/geist/dist/sans.js\n");
/***/ })
};
;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,220 @@
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
/******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({});
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ id: moduleId,
/******/ loaded: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ var threw = true;
/******/ try {
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/ threw = false;
/******/ } finally {
/******/ if(threw) delete __webpack_module_cache__[moduleId];
/******/ }
/******/
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = __webpack_modules__;
/******/
/************************************************************************/
/******/ /* webpack/runtime/amd options */
/******/ (() => {
/******/ __webpack_require__.amdO = {};
/******/ })();
/******/
/******/ /* webpack/runtime/compat get default export */
/******/ (() => {
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = (module) => {
/******/ var getter = module && module.__esModule ?
/******/ () => (module['default']) :
/******/ () => (module);
/******/ __webpack_require__.d(getter, { a: getter });
/******/ return getter;
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/create fake namespace object */
/******/ (() => {
/******/ var getProto = Object.getPrototypeOf ? (obj) => (Object.getPrototypeOf(obj)) : (obj) => (obj.__proto__);
/******/ var leafPrototypes;
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 16: return value when it's Promise-like
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = this(value);
/******/ if(mode & 8) return value;
/******/ if(typeof value === 'object' && value) {
/******/ if((mode & 4) && value.__esModule) return value;
/******/ if((mode & 16) && typeof value.then === 'function') return value;
/******/ }
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ var def = {};
/******/ leafPrototypes = leafPrototypes || [null, getProto({}), getProto([]), getProto(getProto)];
/******/ for(var current = mode & 2 && value; typeof current == 'object' && !~leafPrototypes.indexOf(current); current = getProto(current)) {
/******/ Object.getOwnPropertyNames(current).forEach((key) => (def[key] = () => (value[key])));
/******/ }
/******/ def['default'] = () => (value);
/******/ __webpack_require__.d(ns, def);
/******/ return ns;
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/define property getters */
/******/ (() => {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = (exports, definition) => {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/ensure chunk */
/******/ (() => {
/******/ __webpack_require__.f = {};
/******/ // This file contains only the entry chunk.
/******/ // The chunk loading function for additional chunks
/******/ __webpack_require__.e = (chunkId) => {
/******/ return Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => {
/******/ __webpack_require__.f[key](chunkId, promises);
/******/ return promises;
/******/ }, []));
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/get javascript chunk filename */
/******/ (() => {
/******/ // This function allow to reference async chunks and sibling chunks for the entrypoint
/******/ __webpack_require__.u = (chunkId) => {
/******/ // return url for filenames based on template
/******/ return "" + chunkId + ".js";
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/getFullHash */
/******/ (() => {
/******/ __webpack_require__.h = () => ("3b31194daa7b21f8")
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ })();
/******/
/******/ /* webpack/runtime/make namespace object */
/******/ (() => {
/******/ // define __esModule on exports
/******/ __webpack_require__.r = (exports) => {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/node module decorator */
/******/ (() => {
/******/ __webpack_require__.nmd = (module) => {
/******/ module.paths = [];
/******/ if (!module.children) module.children = [];
/******/ return module;
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/startup entrypoint */
/******/ (() => {
/******/ __webpack_require__.X = (result, chunkIds, fn) => {
/******/ // arguments: chunkIds, moduleId are deprecated
/******/ var moduleId = chunkIds;
/******/ if(!fn) chunkIds = result, fn = () => (__webpack_require__(__webpack_require__.s = moduleId));
/******/ chunkIds.map(__webpack_require__.e, __webpack_require__)
/******/ var r = fn();
/******/ return r === undefined ? result : r;
/******/ }
/******/ })();
/******/
/******/ /* webpack/runtime/require chunk loading */
/******/ (() => {
/******/ // no baseURI
/******/
/******/ // object to store loaded chunks
/******/ // "1" means "loaded", otherwise not loaded yet
/******/ var installedChunks = {
/******/ "webpack-runtime": 1
/******/ };
/******/
/******/ // no on chunks loaded
/******/
/******/ var installChunk = (chunk) => {
/******/ var moreModules = chunk.modules, chunkIds = chunk.ids, runtime = chunk.runtime;
/******/ for(var moduleId in moreModules) {
/******/ if(__webpack_require__.o(moreModules, moduleId)) {
/******/ __webpack_require__.m[moduleId] = moreModules[moduleId];
/******/ }
/******/ }
/******/ if(runtime) runtime(__webpack_require__);
/******/ for(var i = 0; i < chunkIds.length; i++)
/******/ installedChunks[chunkIds[i]] = 1;
/******/
/******/ };
/******/
/******/ // require() chunk loading for javascript
/******/ __webpack_require__.f.require = (chunkId, promises) => {
/******/ // "1" is the signal for "already loaded"
/******/ if(!installedChunks[chunkId]) {
/******/ if("webpack-runtime" != chunkId) {
/******/ installChunk(require("./" + __webpack_require__.u(chunkId)));
/******/ } else installedChunks[chunkId] = 1;
/******/ }
/******/ };
/******/
/******/ module.exports = __webpack_require__;
/******/ __webpack_require__.C = installChunk;
/******/
/******/ // no HMR
/******/
/******/ // no HMR manifest
/******/ })();
/******/
/************************************************************************/
/******/
/******/
/******/ })()
;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
self.__BUILD_MANIFEST = {__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},sortedPages:["\u002F_app"]};self.__BUILD_MANIFEST_CB && self.__BUILD_MANIFEST_CB()

View File

@ -0,0 +1 @@
self.__SSG_MANIFEST=new Set;self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()

Binary file not shown.

View File

@ -0,0 +1 @@
{"c":[],"r":[],"m":[]}

View File

@ -0,0 +1,22 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("app/layout",{
/***/ "(app-pages-browser)/./app/globals.css":
/*!*************************!*\
!*** ./app/globals.css ***!
\*************************/
/***/ (function(module, __webpack_exports__, __webpack_require__) {
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"7fee949e42a3\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL2FwcC9nbG9iYWxzLmNzcyIsIm1hcHBpbmdzIjoiO0FBQUEsK0RBQWUsY0FBYztBQUM3QixJQUFJLElBQVUsSUFBSSxpQkFBaUIiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9fTl9FLy4vYXBwL2dsb2JhbHMuY3NzPzlkMzMiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGRlZmF1bHQgXCI3ZmVlOTQ5ZTQyYTNcIlxuaWYgKG1vZHVsZS5ob3QpIHsgbW9kdWxlLmhvdC5hY2NlcHQoKSB9XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///(app-pages-browser)/./app/globals.css\n"));
/***/ })
});

View File

@ -0,0 +1 @@
{"c":["app/layout","webpack"],"r":[],"m":[]}

View File

@ -0,0 +1,18 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("webpack",{},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ /* webpack/runtime/getFullHash */
/******/ !function() {
/******/ __webpack_require__.h = function() { return "dfbed2d3019c9946"; }
/******/ }();
/******/
/******/ }
);

27
.next/trace Normal file

File diff suppressed because one or more lines are too long

79
.next/types/app/layout.ts Normal file
View File

@ -0,0 +1,79 @@
// File: /home/polaris/Documents/daphnis/app/layout.tsx
import * as entry from '../../../app/layout.js'
import type { ResolvingMetadata, ResolvingViewport } from 'next/dist/lib/metadata/types/metadata-interface.js'
type TEntry = typeof import('../../../app/layout.js')
// Check that the entry is a valid entry
checkFields<Diff<{
default: Function
config?: {}
generateStaticParams?: Function
revalidate?: RevalidateRange<TEntry> | false
dynamic?: 'auto' | 'force-dynamic' | 'error' | 'force-static'
dynamicParams?: boolean
fetchCache?: 'auto' | 'force-no-store' | 'only-no-store' | 'default-no-store' | 'default-cache' | 'only-cache' | 'force-cache'
preferredRegion?: 'auto' | 'global' | 'home' | string | string[]
runtime?: 'nodejs' | 'experimental-edge' | 'edge'
maxDuration?: number
metadata?: any
generateMetadata?: Function
viewport?: any
generateViewport?: Function
}, TEntry, ''>>()
// Check the prop type of the entry function
checkFields<Diff<LayoutProps, FirstArg<TEntry['default']>, 'default'>>()
// Check the arguments and return type of the generateMetadata function
if ('generateMetadata' in entry) {
checkFields<Diff<LayoutProps, FirstArg<MaybeField<TEntry, 'generateMetadata'>>, 'generateMetadata'>>()
checkFields<Diff<ResolvingMetadata, SecondArg<MaybeField<TEntry, 'generateMetadata'>>, 'generateMetadata'>>()
}
// Check the arguments and return type of the generateViewport function
if ('generateViewport' in entry) {
checkFields<Diff<LayoutProps, FirstArg<MaybeField<TEntry, 'generateViewport'>>, 'generateViewport'>>()
checkFields<Diff<ResolvingViewport, SecondArg<MaybeField<TEntry, 'generateViewport'>>, 'generateViewport'>>()
}
// Check the arguments and return type of the generateStaticParams function
if ('generateStaticParams' in entry) {
checkFields<Diff<{ params: PageParams }, FirstArg<MaybeField<TEntry, 'generateStaticParams'>>, 'generateStaticParams'>>()
checkFields<Diff<{ __tag__: 'generateStaticParams', __return_type__: any[] | Promise<any[]> }, { __tag__: 'generateStaticParams', __return_type__: ReturnType<MaybeField<TEntry, 'generateStaticParams'>> }>>()
}
type PageParams = any
export interface PageProps {
params?: any
searchParams?: any
}
export interface LayoutProps {
children?: React.ReactNode
params?: any
}
// =============
// Utility types
type RevalidateRange<T> = T extends { revalidate: any } ? NonNegative<T['revalidate']> : never
// If T is unknown or any, it will be an empty {} type. Otherwise, it will be the same as Omit<T, keyof Base>.
type OmitWithTag<T, K extends keyof any, _M> = Omit<T, K>
type Diff<Base, T extends Base, Message extends string = ''> = 0 extends (1 & T) ? {} : OmitWithTag<T, keyof Base, Message>
type FirstArg<T extends Function> = T extends (...args: [infer T, any]) => any ? unknown extends T ? any : T : never
type SecondArg<T extends Function> = T extends (...args: [any, infer T]) => any ? unknown extends T ? any : T : never
type MaybeField<T, K extends string> = T extends { [k in K]: infer G } ? G extends Function ? G : never : never
function checkFields<_ extends { [k in keyof any]: never }>() {}
// https://github.com/sindresorhus/type-fest
type Numeric = number | bigint
type Zero = 0 | 0n
type Negative<T extends Numeric> = T extends Zero ? never : `${T}` extends `-${string}` ? T : never
type NonNegative<T extends Numeric> = T extends Zero ? T : Negative<T> extends never ? T : '__invalid_negative_number__'

79
.next/types/app/page.ts Normal file
View File

@ -0,0 +1,79 @@
// File: /home/polaris/Documents/daphnis/app/page.tsx
import * as entry from '../../../app/page.js'
import type { ResolvingMetadata, ResolvingViewport } from 'next/dist/lib/metadata/types/metadata-interface.js'
type TEntry = typeof import('../../../app/page.js')
// Check that the entry is a valid entry
checkFields<Diff<{
default: Function
config?: {}
generateStaticParams?: Function
revalidate?: RevalidateRange<TEntry> | false
dynamic?: 'auto' | 'force-dynamic' | 'error' | 'force-static'
dynamicParams?: boolean
fetchCache?: 'auto' | 'force-no-store' | 'only-no-store' | 'default-no-store' | 'default-cache' | 'only-cache' | 'force-cache'
preferredRegion?: 'auto' | 'global' | 'home' | string | string[]
runtime?: 'nodejs' | 'experimental-edge' | 'edge'
maxDuration?: number
metadata?: any
generateMetadata?: Function
viewport?: any
generateViewport?: Function
}, TEntry, ''>>()
// Check the prop type of the entry function
checkFields<Diff<PageProps, FirstArg<TEntry['default']>, 'default'>>()
// Check the arguments and return type of the generateMetadata function
if ('generateMetadata' in entry) {
checkFields<Diff<PageProps, FirstArg<MaybeField<TEntry, 'generateMetadata'>>, 'generateMetadata'>>()
checkFields<Diff<ResolvingMetadata, SecondArg<MaybeField<TEntry, 'generateMetadata'>>, 'generateMetadata'>>()
}
// Check the arguments and return type of the generateViewport function
if ('generateViewport' in entry) {
checkFields<Diff<PageProps, FirstArg<MaybeField<TEntry, 'generateViewport'>>, 'generateViewport'>>()
checkFields<Diff<ResolvingViewport, SecondArg<MaybeField<TEntry, 'generateViewport'>>, 'generateViewport'>>()
}
// Check the arguments and return type of the generateStaticParams function
if ('generateStaticParams' in entry) {
checkFields<Diff<{ params: PageParams }, FirstArg<MaybeField<TEntry, 'generateStaticParams'>>, 'generateStaticParams'>>()
checkFields<Diff<{ __tag__: 'generateStaticParams', __return_type__: any[] | Promise<any[]> }, { __tag__: 'generateStaticParams', __return_type__: ReturnType<MaybeField<TEntry, 'generateStaticParams'>> }>>()
}
type PageParams = any
export interface PageProps {
params?: any
searchParams?: any
}
export interface LayoutProps {
children?: React.ReactNode
params?: any
}
// =============
// Utility types
type RevalidateRange<T> = T extends { revalidate: any } ? NonNegative<T['revalidate']> : never
// If T is unknown or any, it will be an empty {} type. Otherwise, it will be the same as Omit<T, keyof Base>.
type OmitWithTag<T, K extends keyof any, _M> = Omit<T, K>
type Diff<Base, T extends Base, Message extends string = ''> = 0 extends (1 & T) ? {} : OmitWithTag<T, keyof Base, Message>
type FirstArg<T extends Function> = T extends (...args: [infer T, any]) => any ? unknown extends T ? any : T : never
type SecondArg<T extends Function> = T extends (...args: [any, infer T]) => any ? unknown extends T ? any : T : never
type MaybeField<T, K extends string> = T extends { [k in K]: infer G } ? G extends Function ? G : never : never
function checkFields<_ extends { [k in keyof any]: never }>() {}
// https://github.com/sindresorhus/type-fest
type Numeric = number | bigint
type Zero = 0 | 0n
type Negative<T extends Numeric> = T extends Zero ? never : `${T}` extends `-${string}` ? T : never
type NonNegative<T extends Numeric> = T extends Zero ? T : Negative<T> extends never ? T : '__invalid_negative_number__'

1
.next/types/package.json Normal file
View File

@ -0,0 +1 @@
{"type": "module"}

65
README.md Normal file
View File

@ -0,0 +1,65 @@
Needs BunJS and a Mysql DB
1 - create a mysql database called lachesis
make a .env in the root directory containing the following (be sure to use your own data)
DATABASE_URL="mysql://root:password@localhost:3306/lachesis"
DATABASE_AIME_URL = "mysql://root:password@localhost:3306/aime"
---
2 - delete the migrations folder(s) inside schemas/artemis and schemas/lachesis
----
3 - run the below
$ `bun db:init`
What it does:
It will create a empty data base for lachesis and pull your existing artemis one into its own schema via introspection.
`"db:init": "npx prisma migrate dev --name init --schema prisma/schemas lachesis/schema.prisma; npx prisma db pull --schema prisma/schemas/artemis/schema.prisma"`
---
4 - run the below
$ `bun lachesis:generate`
What it does:
generates the schema output
`"lachesis:generate": "prisma generate --schema=./prisma/schemas/lachesis/schema.prisma"`
---
5 - run the below
generates the schema output
$ `bun aretmis:generate`
What it does:
`"artemis:generate": "prisma generate --schema=./prisma/schemas/artemis/schema.prisma"`
---
6 - start lachesis
$ `bun run dev`
What it does:
`"dev": "next dev",`
You can look at the package.json to figure out how to migrate lachesis if you have the desire to make changes.
If you download the files as is you should just need to update the env and do a migrate to your db for lachesis, check your database after to make sure all the schemas are there.
`"lachesis:migrate": "prisma migrate dev --schema=./prisma/schemas/lachesis/schema.prisma",`

View File

@ -0,0 +1,30 @@
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@radix-ui/react-dropdown-menu";
import React from "react";
const ExtractData = () => {
return (
<Card x-chunk="aimecard">
<CardHeader>
<CardTitle className="text-2xl">Extract Jacket Art</CardTitle>
</CardHeader>
<CardContent>
<form className="grid gap-4">
<div className="grid gap-2">
<Label>Game Data Path: </Label>
<Input
name="accessCode"
type="text"
placeholder="*******************"
/>
</div>
</form>
</CardContent>
</Card>
);
};
export default ExtractData;

View File

@ -0,0 +1,10 @@
import ExtractData from "./extraction";
const FileExtractionPage = async () => {
return (
<div className="flex min-h-screen w-full flex-col">
<ExtractData />
</div>
);
};
export default FileExtractionPage;

View File

@ -0,0 +1,34 @@
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@radix-ui/react-dropdown-menu";
import React from "react";
const AdminHome = () => {
return (
<Card x-chunk="aimecard">
<CardHeader>
<CardTitle className="text-2xl">Link New Aime Card</CardTitle>
</CardHeader>
<CardContent>
<div className="text-lg mb-4">Current Access Code:</div>
<form className="grid gap-4">
<div className="grid gap-2">
<Label>Access Code</Label>
<Input
name="accessCode"
type="text"
placeholder="*******************"
/>
</div>
<Button type="submit" className="w-full">
Link
</Button>
</form>
</CardContent>
</Card>
);
};
export default AdminHome;

View File

@ -0,0 +1,14 @@
import { getAuth } from "@/auth/queries/getauth";
import AdminHome from "./home";
const ProtectedDashboardPage = async () => {
const { user } = await getAuth();
return (
<div>
<AdminHome />
</div>
);
};
export default ProtectedDashboardPage;

View File

@ -0,0 +1,10 @@
import UnlockUser from "./unlock";
const FileExtractionPage = async () => {
return (
<div className="flex min-h-screen w-full flex-col">
<UnlockUser />
</div>
);
};
export default FileExtractionPage;

View File

@ -0,0 +1,34 @@
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@radix-ui/react-dropdown-menu";
import React from "react";
const UnlockUser = () => {
return (
<Card x-chunk="aimecard">
<CardHeader>
<CardTitle className="text-2xl">Link New Aime Card</CardTitle>
</CardHeader>
<CardContent>
<div className="text-lg mb-4">Current Access Code:</div>
<form className="grid gap-4">
<div className="grid gap-2">
<Label>Access Code</Label>
<Input
name="accessCode"
type="text"
placeholder="*******************"
/>
</div>
<Button type="submit" className="w-full">
Link
</Button>
</form>
</CardContent>
</Card>
);
};
export default UnlockUser;

View File

@ -0,0 +1,35 @@
import { getAuth } from "@/auth/queries/getauth";
import { redirect } from "next/navigation";
import AdminSubNavigation from "../../../components/navigationbar/adminnavigation";
export default async function AuthenticatedLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const { user } = await getAuth();
if (!user) {
redirect("/");
}
if (user.role === "ADMIN") {
return (
<>
<main className="flex min-h-[calc(100vh-_theme(spacing.16))] flex-1 flex-col gap-4 bg-muted/40 p-4 md:gap-8 md:p-10">
<div className="mx-auto grid w-full max-w-6xl gap-2">
<h1 className="text-2xl font-semibold">Admin</h1>
</div>
<div className="mx-auto grid w-full max-w-6xl items-start gap-6 md:grid-cols-[180px_1fr] lg:grid-cols-[250px_1fr]">
<AdminSubNavigation />
{children}
</div>
</main>
</>
);
} else {
console.log("not an admin");
redirect("/home");
}
}

View File

@ -0,0 +1,36 @@
import { getAuth } from "@/auth/queries/getauth";
import { redirect } from "next/navigation";
import AdminSubNavigation from "../../../components/navigationbar/adminnavigation";
import SettingsSubMenuNavigation from "../../../components/navigationbar/settingsnavigation";
export default async function AuthenticatedLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const { user } = await getAuth();
if (!user) {
redirect("/");
}
if (user.role === "ADMIN") {
return (
<>
<main className="flex min-h-[calc(100vh-_theme(spacing.16))] flex-1 flex-col gap-4 bg-muted/40 p-4 md:gap-8 md:p-10">
<div className="mx-auto grid w-full max-w-6xl gap-2">
<h1 className="text-2xl font-semibold">Settings</h1>
</div>
<div className="mx-auto grid w-full max-w-6xl items-start gap-6 md:grid-cols-[180px_1fr] lg:grid-cols-[250px_1fr]">
<SettingsSubMenuNavigation />
{children}
</div>
</main>
</>
);
} else {
console.log("not an admin");
redirect("/home");
}
}

View File

@ -0,0 +1,137 @@
// Client-side React component
"use client";
import { useEffect, useState } from "react";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import { useToast } from "@/components/ui/use-toast";
import { LinkAimeCard } from "@/lib/LinkNewAccessCode";
import { getLachesisInUseCards } from "@/lib/GetUserAccessCode";
import SettingsSubMenuNavigation from "@/components/navigationbar/settingsnavigation";
const GeneralSettings = () => {
const { toast } = useToast();
const [accessCode, setAccessCode] = useState("");
const [isButtonDisabled, setIsButtonDisabled] = useState(true);
const [currentAccessCode, setCurrentAccessCode] = useState<string | null>(
null
);
useEffect(() => {
// Fetch current access code when component mounts
fetchCurrentAccessCode();
}, []);
const fetchCurrentAccessCode = async () => {
try {
const aimeUser = await getLachesisInUseCards();
if (aimeUser) {
setCurrentAccessCode(aimeUser.accessCode || null);
}
} catch (error) {
console.error("Error fetching access code:", error);
}
};
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setAccessCode(value);
setIsButtonDisabled(value.length !== 20);
};
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const formData = new FormData(event.currentTarget);
try {
const result = await LinkAimeCard(formData);
if (result.success) {
toast({
title: "Success",
description: "Aime Card linked successfully",
});
setAccessCode("");
fetchCurrentAccessCode();
} else {
toast({
title: "Error",
description: "Failed to link Aime Card",
});
}
} catch (error: any) {
if (error instanceof Error) {
if (error.message === "Access Code is already used by another user") {
toast({
title: "Error",
description: "Access Code is already used by another user",
});
} else if (
error.message === "Not in artemis's database, Nice try ^_^"
) {
toast({
title: "Error",
description: "Access Code not found in database",
});
} else if (
error.message === "You are currently holding this access code"
) {
toast({
title: "Error",
description: "You are currently holding this access code",
});
setAccessCode("");
} else {
toast({
title: "Error",
description: `Failed to link Aime Card: ${error.message}`,
});
}
} else {
console.error(error);
toast({
title: "Error",
description: "An unexpected error occurred. Please try again later.",
});
}
}
};
return (
<Card x-chunk="aimecard">
<CardHeader>
<CardTitle className="text-2xl">Link New Aime Card</CardTitle>
</CardHeader>
<CardContent>
<div className="text-lg mb-4">
Current Access Code: {currentAccessCode}
</div>
<form onSubmit={handleSubmit} className="grid gap-4">
<div className="grid gap-2">
<Label htmlFor="accessCode">Access Code</Label>
<Input
name="accessCode"
type="text"
placeholder="*******************"
value={accessCode}
onChange={handleInputChange}
required
/>
</div>
<Button type="submit" className="w-full" disabled={isButtonDisabled}>
Link
</Button>
</form>
</CardContent>
</Card>
);
};
export { GeneralSettings };

View File

@ -0,0 +1,14 @@
import { getAuth } from "@/auth/queries/getauth";
import { GeneralSettings } from "./home";
const ProtectedDashboardPage = async () => {
const { user } = await getAuth();
return (
<div>
<GeneralSettings />
</div>
);
};
export default ProtectedDashboardPage;

View File

@ -0,0 +1,62 @@
"use server";
import { getAuth } from "@/auth/queries/getauth";
import { artemis, lachesis } from "@/lib/prisma";
import { Argon2id } from "oslo/password";
export const updatePassword = async (
currentPassword: string,
newPassword: string,
confirmNewPassword: string
) => {
// Check if new passwords match
if (newPassword !== confirmNewPassword) {
return { error: "New passwords do not match" };
}
const { user } = await getAuth();
if (!user) {
return { error: "User not authenticated" };
}
try {
// Fetch user from database
const existingUser = await lachesis.user.findUnique({
where: {
id: user.id,
},
});
if (!existingUser) {
return { error: "User not found" };
}
// Verify current password
const isPasswordValid = await new Argon2id().verify(
existingUser.hashedPassword,
currentPassword
);
if (!isPasswordValid) {
return { error: "Current password is incorrect" };
}
// Hash new password
const hashedPassword = await new Argon2id().hash(newPassword);
// Update user's password
await lachesis.user.update({
where: {
id: user.id,
},
data: {
hashedPassword,
},
});
return { success: "Password updated successfully" };
} catch (error: any) {
return { error: "Failed to update password: " + error.message };
}
};

View File

@ -0,0 +1,10 @@
import SecuritySettings from "./security";
const SecuritySettingsPage = async () => {
return (
<div className="flex min-h-screen w-full flex-col">
<SecuritySettings />
</div>
);
};
export default SecuritySettingsPage;

View File

@ -0,0 +1,104 @@
"use client";
import React, { useState, useEffect } from "react";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { toast, useToast } from "@/components/ui/use-toast";
import { updatePassword } from "./forgotpassword";
export default function SecuritySettings() {
const [currentPassword, setCurrentPassword] = useState("");
const [newPassword, setNewPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const handleCurrentPasswordChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
setCurrentPassword(event.target.value);
};
const handleNewPasswordChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
setNewPassword(event.target.value);
};
const handleConfirmPasswordChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
setConfirmPassword(event.target.value);
};
const isButtonDisabled = !currentPassword || !newPassword || !confirmPassword;
const submit = async (data: FormData) => {
const { error } = await updatePassword(
currentPassword,
newPassword,
confirmPassword
);
if (error) {
toast({
title: "Error",
description: error,
});
} else {
toast({
title: "Success",
description: "Password updated successfully",
});
}
};
return (
<div className="grid gap-6">
<Card x-chunk="passwordreset">
<form action={submit}>
<CardHeader>
<CardTitle>Reset Password</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<Input
type="password"
name="currentPassword"
placeholder="Current Password"
value={currentPassword}
onChange={handleCurrentPasswordChange}
/>
<Input
type="password"
name="newPassword"
placeholder="New Password"
value={newPassword}
onChange={handleNewPasswordChange}
/>
<Input
type="password"
name="confirmPassword"
placeholder="Confirm Password"
value={confirmPassword}
onChange={handleConfirmPasswordChange}
/>
</CardContent>
<CardFooter className="border-t flex justify-end px-6 py-4">
<Button
type="submit"
disabled={isButtonDisabled}
style={{
backgroundColor: isButtonDisabled ? "grey" : "",
}}
>
Save
</Button>
</CardFooter>
</form>
</Card>
</div>
);
}

View File

@ -0,0 +1,14 @@
import React from "react";
import ChunithmData from "../../../components/scoreplaylog/page";
const ChunithmPage = async () => {
return (
<div>
<div className=" ">
<ChunithmData />
</div>
</div>
);
};
export default ChunithmPage;

View File

@ -0,0 +1,7 @@
import React from "react";
const Dashboard = () => {
return <div>Dashboard</div>;
};
export default Dashboard;

View File

@ -0,0 +1,24 @@
import { getAuth } from "@/auth/queries/getauth";
import { redirect } from "next/navigation";
import HeaderNavigation from "../../components/navigationbar/navigationbar";
export default async function AuthenticatedLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const { user } = await getAuth();
if (!user) {
console.log("you need to be signed in");
redirect("/");
}
return (
<>
<HeaderNavigation />
{children}
</>
);
}

View File

@ -0,0 +1,62 @@
"use server";
import { lachesis } from "@/lib/prisma";
import { randomUUID } from "crypto";
import { Resend } from "resend";
import { redirect } from "next/navigation";
const resend = new Resend(process.env.NEXT_PUBLIC_RESEND_API_KEY);
const DOMAIN = process.env.DOMAIN || "localhost:3000";
const PROTOCOL = process.env.NODE_ENV === "production" ? "https" : "http";
const sendEmail = async (email: string, token: string, userName: string) => {
try {
const { data } = await resend.emails.send({
from: "Password Reset <security@resend.dev>",
to: [email],
subject: "Reset Password Request",
text: `Hello ${userName}, someone (hopefully you) requested a password reset for this account. If you did want to reset your password, please click here: ${PROTOCOL}://${DOMAIN}/password-reset/${token}
For security reasons, this link is only valid for four hours.
If you did not request this reset, please ignore this email.`,
});
console.log("Email sent successfully:", data);
// Handle success
} catch (error) {
console.error("Error sending email:", error);
// Handle error
throw error;
}
};
export async function EmailPasswordResetLink(data: FormData) {
const email = data.get("email");
if (!email || typeof email !== "string") {
return {
error: "Invalid email",
};
}
const user = await lachesis.user.findUnique({
where: { email },
});
if (!user) {
return {
error: "This email is not registered",
};
}
const token = await lachesis.passwordResetToken.create({
data: {
id: randomUUID(),
userId: user.id,
token: `${randomUUID()}${randomUUID()}`.replace(/-/g, ""),
},
});
await sendEmail(user.email, token.token, user.username);
redirect("/forgot-password/success");
}

View File

@ -0,0 +1,43 @@
"use client";
import Link from "next/link";
import { useState } from "react";
import { ArrowLeftSquare } from "lucide-react";
import { Card } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { EmailPasswordResetLink } from "./emailforgotpassword";
export default function ForgotPassword() {
const [error, setError] = useState<string>("");
const submit = async (data: FormData) => {
const { error } = await EmailPasswordResetLink(data);
setError(error);
};
return (
<main className="max-w-xl px-4 mx-auto flex flex-col justify-center h-screen ">
<Card className="gap-4 flex flex-col p-6 ">
<form action={submit} className="flex flex-col gap-4">
{" "}
<h1 className="text-2xl font-light">Reset password</h1>
<p>
Enter your email address to get instructions for resetting your
password.
</p>
<Input name="email" type="email" placeholder="Your email..." />
{error && <p className="text-red-500">{error}</p>}
<Button type="submit">Reset Password</Button>
<Link
href="/"
className="text-sm text-neutral-700/80 flex items-center"
>
<ArrowLeftSquare />
<span>Return to Login</span>
</Link>
</form>
</Card>
</main>
);
}

View File

@ -0,0 +1,19 @@
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import Link from "next/link";
export default async function SuccessPage() {
return (
<main className="max-w-xl px-4 mx-auto flex flex-col justify-center h-screen ">
<Card className="gap-4 flex flex-col p-6">
<h1 className="text-2xl font-light mb-4">Password reset</h1>
<p className="mb-4">
If the email doesn't show up, check your spam folder.
</p>
<Button type="submit" asChild className="mt-4">
<Link href="/">Return to Login</Link>
</Button>
</Card>
</main>
);
}

View File

@ -0,0 +1,45 @@
"use client";
import Link from "next/link";
import { useState } from "react";
import { ArrowLeft } from "lucide-react";
import { Card } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { resetPassword } from "./token";
export default function ({ params }: { params: { token: string } }) {
const [error, setError] = useState<string>("");
async function submit(data: FormData) {
const { error } = await resetPassword(params.token, data);
setError(error || "");
}
return (
<main className="max-w-xl px-4 mx-auto flex flex-col justify-center h-screen ">
<Card className="gap-4 flex flex-col p-6 ">
<form action={submit} className="flex flex-col gap-4">
{" "}
<h1 className="text-2xl font-light">Choose a new password</h1>
<p>You can reset your password here.</p>
<Input name="password" type="password" placeholder="Password" />
<Input
name="confirm"
type="password"
placeholder="Confirm password"
/>
{error && <p className="text-red-500 text-sm">{error}</p>}
<Button type="submit">Reset Password</Button>
<Link
href="/"
className="text-sm text-neutral-700/80 flex items-center"
>
<ArrowLeft />
<span>Return to Login</span>
</Link>
</form>
</Card>
</main>
);
}

View File

@ -0,0 +1,67 @@
"use server";
import { lachesis } from "@/lib/prisma";
import { redirect } from "next/navigation";
import { Argon2id } from "oslo/password";
export async function resetPassword(token: string, data: FormData) {
const password = data.get("password");
const confirmPassword = data.get("confirm");
if (
!password ||
typeof password !== "string" ||
password !== confirmPassword
) {
return {
error:
"The passwords did not match. Please try retyping them and submitting again.",
};
}
const passwordResetToken = await lachesis.passwordResetToken.findUnique({
where: {
token,
createdAt: { gt: new Date(Date.now() - 1000 * 60 * 60 * 4) },
resetAt: null,
},
});
if (!passwordResetToken) {
return {
error:
"Invalid token reset request. Please try resetting your password again.",
};
}
const argon2 = new Argon2id();
const encrypted = await argon2.hash(password);
const updateUser = lachesis.user.update({
where: { id: passwordResetToken.userId },
data: {
hashedPassword: encrypted,
},
});
const updateToken = lachesis.passwordResetToken.update({
where: {
id: passwordResetToken.id,
},
data: {
resetAt: new Date(),
},
});
try {
await lachesis.$transaction([updateUser, updateToken]);
} catch (err) {
console.error(err);
return {
error:
"An unexpected error occurred. Please try again and if the problem persists, contact support.",
};
}
redirect("/password-reset/success");
}

View File

@ -0,0 +1,32 @@
"use client";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useEffect } from "react";
export default async function SuccessPage() {
const router = useRouter();
useEffect(() => {
setTimeout(() => {
router.push("/"); // Redirect to login page
}, 5000);
});
return (
<main className="max-w-xl px-4 mx-auto flex flex-col justify-center h-screen">
<Card className="gap-4 flex flex-col p-6">
<h1 className="text-2xl font-light">Your password has been updated.</h1>
<p>
You will now be redirected to the login page where you can login
again.
</p>
<Button type="submit" asChild>
<Link href="/">Return to Login</Link>
</Button>
</Card>
</main>
);
}

View File

@ -0,0 +1,38 @@
import { SharingPlaylogId, getSongsWithTitles } from "@/lib/api";
import { shareScore } from "../token";
export default async function Share({
params,
}: {
params: { token: string; id: string };
}) {
const { token, id } = params;
// Verify the token
const tokenResult = await shareScore(token);
if (tokenResult.error) {
// Handle error appropriately, e.g., render an error message
return <p>{tokenResult.error}</p>;
}
const userId = 10000;
const playlogId = parseInt(id);
const songsData = await getSongsWithTitles(userId);
const playlogIds = await SharingPlaylogId(playlogId);
const matchingSongs = songsData.filter((song) =>
playlogIds.includes(song.id)
);
const songTitles = matchingSongs.map((song) => song.title);
return (
<ul>
{songTitles.map((title) => (
<li key={title}>{title}</li>
))}
</ul>
);
}

View File

@ -0,0 +1,67 @@
"use server";
import { getAuth } from "@/auth/queries/getauth";
import { lachesis } from "@/lib/prisma";
import { randomUUID } from "crypto";
import { randomBytes } from "crypto";
import { redirect } from "next/navigation";
export async function generateShareToken(id: number): Promise<{
token?: string;
id?: string;
error?: string;
}> {
const { user } = await getAuth();
if (!user || !user.id || typeof user.id !== "string") {
return {
error: "Invalid user or user ID",
};
}
const gernatetoken = randomBytes(5).readUInt32BE(0).toString();
const token = await lachesis.linkSharingToken.create({
data: {
playlogId: id,
id: randomUUID(),
userId: user.id,
token: gernatetoken,
createdAt: new Date(),
},
});
return { token: token.token };
}
export async function shareScore(token: string) {
const PublicPage = await lachesis.linkSharingToken.findUnique({
where: {
token,
},
});
if (!PublicPage) {
return {
error: "Invalid token or token does not exist",
};
}
// Check if token has expired
const tokenAge =
new Date().getTime() - new Date(PublicPage.createdAt).getTime();
const tokenAgeLimit = 1000 * 60 * 60 * 24; // 1 day in milliseconds
if (tokenAge > tokenAgeLimit) {
await lachesis.linkSharingToken.update({
where: {
token,
},
data: {
tokenExpiredAt: new Date(),
},
});
redirect("/");
}
return { success: true };
}

BIN
app/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

76
app/globals.css Normal file
View File

@ -0,0 +1,76 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;
--radius: 0.5rem;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 212.7 26.8% 83.9%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}

27
app/layout.tsx Normal file
View File

@ -0,0 +1,27 @@
import type { Metadata } from "next";
import { GeistSans } from "geist/font/sans";
import { Toaster } from "@/components/ui/toaster";
import "./globals.css";
import ReactQueryProvider from "./provider";
export const metadata: Metadata = {
title: "Lachesis",
description: "Artemis score viewer",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en" className={GeistSans.className}>
<body className={GeistSans.className}>
<ReactQueryProvider>
<main className="">{children} </main>
</ReactQueryProvider>
<Toaster />
</body>
</html>
);
}

11
app/page.tsx Normal file
View File

@ -0,0 +1,11 @@
import { SignInForm } from "@/auth/components/signin/signin";
const PublicHomePage = () => {
return (
<div className="h-screen flex items-center justify-center">
<SignInForm />
</div>
);
};
export default PublicHomePage;

14
app/provider.tsx Normal file
View File

@ -0,0 +1,14 @@
"use client";
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
import { useState } from "react";
const ReactQueryProvider = ({ children }: { children: React.ReactNode }) => {
const [queryClient] = useState(() => new QueryClient());
return (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
);
};
export default ReactQueryProvider;

11
app/signup/page.tsx Normal file
View File

@ -0,0 +1,11 @@
import { SignUpForm } from "@/auth/components/signup/signup";
const SignUpPage = () => {
return (
<div className="h-screen flex items-center justify-center">
<SignUpForm />
</div>
);
};
export default SignUpPage;

View File

@ -0,0 +1,47 @@
"use server";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import { Argon2id } from "oslo/password";
import { lucia } from "@/lib/lucia";
import { lachesis } from "@/lib/prisma";
const signIn = async (formData: FormData) => {
const formDataRaw = {
username: formData.get("username") as string,
password: formData.get("password") as string,
};
try {
const user = await lachesis.user.findUnique({
where: { username: formDataRaw.username },
});
if (!user) {
return { error: "Incorrect username" };
}
const validPassword = await new Argon2id().verify(
user.hashedPassword,
formDataRaw.password
);
if (!validPassword) {
return { error: "Incorrect password" };
}
const session = await lucia.createSession(user.id, {});
const sessionCookie = lucia.createSessionCookie(session.id);
cookies().set(
sessionCookie.name,
sessionCookie.value,
sessionCookie.attributes
);
} catch (error: any) {
return { error: "Sign-in failed: " + error.message };
}
redirect("/home");
};
export { signIn };

View File

@ -0,0 +1,79 @@
"use client";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import Link from "next/link";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import { signIn } from "./action";
import { toast } from "@/components/ui/use-toast";
export default function SignInForm() {
const submit = async (data: FormData) => {
const { error } = await signIn(data);
if (error) {
toast({
title: "Error",
description: error,
});
} else {
toast({
title: "Success",
description: "Account created successfully",
});
}
};
return (
<Card className="mx-auto max-w-sm">
<CardHeader>
<CardTitle className="text-2xl">Sign in</CardTitle>
<CardDescription>
Enter your info below to login your account
</CardDescription>
</CardHeader>
<CardContent>
<form action={submit} className="grid gap-4">
<div className="grid gap-2">
<Label htmlFor="username">Username</Label>
<Input name="username" type="text" placeholder="clotho" required />
</div>
<div className="grid gap-2">
<Label htmlFor="password">Password</Label>
<Input
name="password"
type="password"
placeholder="********"
required
/>
</div>
<Link
href="/forgot-password"
className="ml-auto inline-block text-sm underline"
>
Forgot your password?
</Link>
<Button type="submit" className="w-full">
Sign in
</Button>
</form>
<div className="mt-4 text-center text-sm">
Need an account?{" "}
<Link href="/signup" className="underline">
Sign up
</Link>
</div>
</CardContent>
</Card>
);
}
export { SignInForm };

View File

@ -0,0 +1,26 @@
'use server';
import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';
import { lucia } from '@/lib/lucia';
import { getAuth } from '../queries/getauth';
export const signOut = async (_formData: FormData) => {
const { session } = await getAuth();
if (!session) {
redirect('/');
}
await lucia.invalidateSession(session.id);
const sessionCookie = lucia.createBlankSessionCookie();
cookies().set(
sessionCookie.name,
sessionCookie.value,
sessionCookie.attributes
);
redirect('/');
};

View File

@ -0,0 +1,99 @@
"use server";
import { generateId } from "lucia";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import { Argon2id } from "oslo/password";
import { lucia } from "@/lib/lucia";
import { lachesis, artemis } from "@/lib/prisma";
const signUp = async (formData: FormData) => {
const formDataRaw = {
username: formData.get("username") as string,
email: formData.get("email") as string,
accessCode: formData.get("accessCode") as string,
password: formData.get("password") as string,
confirmPassword: formData.get("confirmPassword") as string,
};
if (formDataRaw.password !== formDataRaw.confirmPassword) {
return { error: "Passwords do not match" };
}
try {
// Check if access code is already used in lachesis database
const existingUser = await lachesis.user.findFirst({
where: {
accessCode: formDataRaw.accessCode,
},
});
if (existingUser) {
return { error: "Access Code already in use" };
}
// Check if username is already used in lachesis database
const existingUsername = await lachesis.user.findFirst({
where: {
username: formDataRaw.username,
},
});
if (existingUsername) {
return { error: "Username is currently taken" };
}
const existingEmail = await lachesis.user.findFirst({
where: {
email: formDataRaw.email,
},
});
if (existingEmail) {
return { error: "Email is already in use" };
}
// Check if access code exists in artemis database
const existingAccessCode = await artemis.aime_card.findFirst({
where: {
access_code: formDataRaw.accessCode,
},
});
if (!existingAccessCode) {
return { error: "Not in artemis's database, Nice try ^_^" };
}
const hashedPassword = await new Argon2id().hash(formDataRaw.password);
const userId = generateId(15);
// Create user in the lachesis database
await lachesis.user.create({
data: {
id: userId,
username: formDataRaw.username,
email: formDataRaw.email,
accessCode: formDataRaw.accessCode,
hashedPassword,
},
});
// Create session and set cookie
const session = await lucia.createSession(userId, {});
const sessionCookie = lucia.createSessionCookie(session.id);
cookies().set(
sessionCookie.name,
sessionCookie.value,
sessionCookie.attributes
);
console.log("Account created");
// Redirect to home page
redirect("/home");
} catch (error: any) {
return { error: "Account creation failed: " + error.message };
}
};
export { signUp };

View File

@ -0,0 +1,107 @@
"use client";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import Link from "next/link";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import { toast, useToast } from "@/components/ui/use-toast";
import { signUp } from "./action";
import { useState } from "react";
export default function SignUpForm() {
const [error, setError] = useState<string>("");
const submit = async (data: FormData) => {
const { error } = await signUp(data);
if (error) {
toast({
title: "Error",
description: error,
});
} else {
toast({
title: "Success",
description: "Account created successfully",
});
}
};
return (
<Card className="mx-auto max-w-sm">
<CardHeader>
<CardTitle className="text-2xl">Sign up</CardTitle>
<CardDescription>
Enter your info below to create your account
</CardDescription>
</CardHeader>
<CardContent>
<form action={submit} className="grid gap-4">
<div className="grid gap-2">
<Label htmlFor="username">Username</Label>
<Input name="username" type="text" placeholder="clotho" required />
</div>
<div className="grid gap-2">
<Label htmlFor="email">Email</Label>
<Input
name="email"
type="email"
placeholder="clotho@fates.com"
required
/>
</div>
<div className="grid gap-2">
<Label htmlFor="accessCode">Access Code</Label>
<Input
name="accessCode"
type="text"
placeholder="*******************"
required
/>
</div>
<div className="grid gap-2">
<Label htmlFor="password">Password</Label>
<Input
name="password"
type="password"
placeholder="********"
required
/>
</div>
<div className="grid gap-2">
<Label htmlFor="confirmPassword">Confirm Password</Label>
<Input
name="confirmPassword"
type="password"
placeholder="********"
required
/>
</div>
<Button type="submit" className="w-full">
Sign up
</Button>
</form>
<div className="mt-4 text-center text-sm">
Already have an account?{" "}
<Link href="/" className="underline">
Sign in
</Link>
</div>
</CardContent>
</Card>
);
}
export { SignUpForm };

41
auth/queries/getauth.ts Normal file
View File

@ -0,0 +1,41 @@
import { cookies } from "next/headers";
import { cache } from "react";
import type { Session, User } from "lucia";
import { lucia } from "@/lib/lucia";
export const getAuth = cache(
async (): Promise<
{ user: User; session: Session } | { user: null; session: null }
> => {
const sessionId = cookies().get(lucia.sessionCookieName)?.value ?? null;
if (!sessionId) {
return {
user: null,
session: null,
};
}
const result = await lucia.validateSession(sessionId);
try {
if (result.session && result.session.fresh) {
const sessionCookie = lucia.createSessionCookie(result.session.id);
cookies().set(
sessionCookie.name,
sessionCookie.value,
sessionCookie.attributes
);
}
if (!result.session) {
const sessionCookie = lucia.createBlankSessionCookie();
cookies().set(
sessionCookie.name,
sessionCookie.value,
sessionCookie.attributes
);
}
} catch {}
return result;
}
);

BIN
bun.lockb Executable file

Binary file not shown.

17
components.json Normal file
View File

@ -0,0 +1,17 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "app/globals.css",
"baseColor": "slate",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}

View File

@ -0,0 +1,36 @@
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
const NAV_ITEMS = [
{ href: "/admin/home", label: "Home" },
{ href: "/admin/unlock", label: "Unlock User" },
{ href: "/admin/extraction", label: "Extract Game Files" },
];
const AdminSubNavigation = () => {
const pathname = usePathname();
return (
<nav className="grid gap-4 text-sm text-muted-foreground">
{NAV_ITEMS.map(({ href, label }) => {
const isActive = pathname === href;
return (
<Link
key={href}
href={href}
className={`${
isActive ? "font-semibold text-primary" : "text-muted-foreground"
} text-sm`}
>
{label}
</Link>
);
})}
</nav>
);
};
export default AdminSubNavigation;

View File

@ -0,0 +1,36 @@
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
const NAV_ITEMS = [
{ href: "/home", label: "Home" },
{ href: "/chunithm", label: "Chunithm" },
{ href: "/maimai", label: "Maimai" },
];
const NavigationMenuDesktop = () => {
const pathname = usePathname();
return (
<nav className="hidden flex-col gap-6 text-lg font-medium md:flex md:flex-row md:items-center md:gap-5 md:text-sm lg:gap-6">
{NAV_ITEMS.map(({ href, label }) => {
const isActive = pathname === href;
return (
<Link
key={href}
href={href}
className={`${
isActive ? "font-semibold text-primary" : "text-muted-foreground"
} text-sm`}
>
{label}
</Link>
);
})}
</nav>
);
};
export default NavigationMenuDesktop;

View File

@ -0,0 +1,36 @@
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
const NAV_ITEMS = [
{ href: "/home", label: "Home" },
{ href: "/chunithm", label: "Chunithm" },
{ href: "/maimai", label: "Maimai" },
];
const NavigationMenuMobile = () => {
const pathname = usePathname();
return (
<nav className="grid gap-6 text-lg font-medium">
{NAV_ITEMS.map(({ href, label }) => {
const isActive = pathname === href;
return (
<Link
key={href}
href={href}
className={`${
isActive ? "font-semibold text-primary" : "text-muted-foreground"
} text-sm`}
>
{label}
</Link>
);
})}
</nav>
);
};
export default NavigationMenuMobile;

View File

@ -0,0 +1,94 @@
import React from "react";
import Link from "next/link";
import { CircleUser, Menu, Search } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
import { getAuth } from "@/auth/queries/getauth";
import NavigationMenuDesktop from "./desktopNavBar";
import NavigationMenuMobile from "./mobileNavBar";
import { signOut } from "@/auth/components/signout";
const HeaderNavigation = async () => {
const { user } = await getAuth();
return (
<>
{user && (
<header className="sticky top-0 flex h-16 items-center gap-4 border-b bg-background px-4 md:px-6">
<NavigationMenuDesktop />
<Sheet>
<SheetTrigger asChild>
<Button
variant="outline"
size="icon"
className="shrink-0 md:hidden"
>
<Menu className="h-5 w-5" />
<span className="sr-only">Toggle navigation menu</span>
</Button>
</SheetTrigger>
<SheetContent side="left">
<NavigationMenuMobile />
</SheetContent>
</Sheet>
<div className="flex w-full items-center gap-4 md:ml-auto md:gap-2 lg:gap-4">
<form className="ml-auto flex-1 sm:flex-initial">
<div className="relative">
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
<Input
type="search"
placeholder="Search"
className="pl-8 sm:w-[300px] md:w-[200px] lg:w-[300px]"
/>
</div>
</form>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="secondary"
size="icon"
className="rounded-full"
>
<CircleUser className="h-5 w-5" />
<span className="sr-only">Toggle user menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>
<Link className="text-primary" href="/settings/home">
Settings
</Link>
</DropdownMenuItem>
{user.role === "ADMIN" && (
<DropdownMenuItem>
<Link className="text-primary" href="/admin/home">
Admin
</Link>
</DropdownMenuItem>
)}
<DropdownMenuSeparator />
<form action={signOut}>
<Button className="bg-transparent text-primary hover:bg-transparent pl-2 font-normal">
Logout
</Button>
</form>
</DropdownMenuContent>
</DropdownMenu>
</div>
</header>
)}
</>
);
};
export default HeaderNavigation;

View File

@ -0,0 +1,35 @@
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
const NAV_ITEMS = [
{ href: "/settings/home", label: "General" },
{ href: "/settings/security", label: "Security" },
];
const SettingsSubMenuNavigation = () => {
const pathname = usePathname();
return (
<nav className="grid gap-4 text-sm text-muted-foreground">
{NAV_ITEMS.map(({ href, label }) => {
const isActive = pathname === href;
return (
<Link
key={href}
href={href}
className={`${
isActive ? "font-semibold text-primary" : "text-muted-foreground"
} text-sm`}
>
{label}
</Link>
);
})}
</nav>
);
};
export default SettingsSubMenuNavigation;

View File

@ -0,0 +1,105 @@
"use client";
import { ColumnDef } from "@tanstack/react-table";
import { MoreHorizontal } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Badge } from "@/components/ui/badge";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { generateShareToken } from "@/app/(sharing)/[token]/token";
export type Song = {
title: string;
artist: string;
score?: number | null;
userPlayDate: string | null;
isFullCombo: boolean | null;
isAllJustice: boolean | null;
id: number;
};
export const columns: ColumnDef<Song>[] = [
{
accessorKey: "title",
header: "Title",
cell: ({ row }) => (
<div>
<div className="font-medium ">
{row.original.title}
<span className="space-x-2 pl-2">
{row.original.isFullCombo && (
<Badge className="rounded-sm">Full Combo</Badge>
)}
{row.original.isAllJustice && (
<Badge className="rounded-sm bg-yellow-500 text-black">
All Justice
</Badge>
)}
</span>
</div>
<div className="text-sm text-muted-foreground truncate inline-block w-[200px] ">
{row.original.artist}
</div>
</div>
),
},
{
accessorKey: "score",
header: "Score",
cell: ({ row }) => row.original.score?.toLocaleString(),
},
{
accessorKey: "userPlayDate",
header: "Date",
},
{
id: "actions",
header: () => <div className="text-left pl-2">More</div>,
cell: ({ row }) => {
const song = row.original;
const [error, setError] = useState<string>("");
const router = useRouter();
const handleGenerateShareToken = async () => {
const { token, error } = await generateShareToken(row.original.id);
if (error) {
setError(error);
} else {
router.push(`/${token}/${row.original.id}`);
}
};
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem
onClick={() => navigator.clipboard.writeText(song.title)}
>
Copy song title
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={handleGenerateShareToken}>
View details
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
},
},
];

Some files were not shown because too many files have changed in this diff Show More