added ui form for course creation

This commit is contained in:
Rajpreet0 2025-12-07 14:25:22 +01:00
parent a746685f6e
commit e41d745bd5
13 changed files with 1354 additions and 21 deletions

View file

@ -13,6 +13,7 @@ const eslintConfig = defineConfig([
"build/**",
"src/components/ui/**",
"src/generated/prisma/**",
"src/components/layout/RichTextEditor.tsx",
"next-env.d.ts",
]),
]);

804
package-lock.json generated
View file

@ -17,6 +17,10 @@
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-tooltip": "^1.2.8",
"@supabase/ssr": "^0.7.0",
"@tiptap/extension-placeholder": "^3.13.0",
"@tiptap/extension-underline": "^3.13.0",
"@tiptap/react": "^3.13.0",
"@tiptap/starter-kit": "^3.13.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"dotenv": "^17.2.3",
@ -3267,6 +3271,12 @@
"integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
"license": "MIT"
},
"node_modules/@remirror/core-constants": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-3.0.0.tgz",
"integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==",
"license": "MIT"
},
"node_modules/@rolldown/pluginutils": {
"version": "1.0.0-beta.43",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.43.tgz",
@ -3949,6 +3959,453 @@
"@testing-library/dom": ">=7.21.4"
}
},
"node_modules/@tiptap/core": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.13.0.tgz",
"integrity": "sha512-iUelgiTMgPVMpY5ZqASUpk8mC8HuR9FWKaDzK27w9oWip9tuB54Z8mePTxNcQaSPb6ErzEaC8x8egrRt7OsdGQ==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/pm": "^3.13.0"
}
},
"node_modules/@tiptap/extension-blockquote": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-3.13.0.tgz",
"integrity": "sha512-K1z/PAIIwEmiWbzrP//4cC7iG1TZknDlF1yb42G7qkx2S2X4P0NiqX7sKOej3yqrPjKjGwPujLMSuDnCF87QkQ==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/core": "^3.13.0"
}
},
"node_modules/@tiptap/extension-bold": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-3.13.0.tgz",
"integrity": "sha512-VYiDN9EEwR6ShaDLclG8mphkb/wlIzqfk7hxaKboq1G+NSDj8PcaSI9hldKKtTCLeaSNu6UR5nkdu/YHdzYWTw==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/core": "^3.13.0"
}
},
"node_modules/@tiptap/extension-bubble-menu": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.13.0.tgz",
"integrity": "sha512-qZ3j2DBsqP9DjG2UlExQ+tHMRhAnWlCKNreKddKocb/nAFrPdBCtvkqIEu+68zPlbLD4ukpoyjUklRJg+NipFg==",
"license": "MIT",
"optional": true,
"dependencies": {
"@floating-ui/dom": "^1.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/core": "^3.13.0",
"@tiptap/pm": "^3.13.0"
}
},
"node_modules/@tiptap/extension-bullet-list": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-3.13.0.tgz",
"integrity": "sha512-fFQmmEUoPzRGiQJ/KKutG35ZX21GE+1UCDo8Q6PoWH7Al9lex47nvyeU1BiDYOhcTKgIaJRtEH5lInsOsRJcSA==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/extension-list": "^3.13.0"
}
},
"node_modules/@tiptap/extension-code": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-3.13.0.tgz",
"integrity": "sha512-sF5raBni6iSVpXWvwJCAcOXw5/kZ+djDHx1YSGWhopm4+fsj0xW7GvVO+VTwiFjZGKSw+K5NeAxzcQTJZd3Vhw==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/core": "^3.13.0"
}
},
"node_modules/@tiptap/extension-code-block": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-3.13.0.tgz",
"integrity": "sha512-kIwfQ4iqootsWg9e74iYJK54/YMIj6ahUxEltjZRML5z/h4gTDcQt2eTpnEC8yjDjHeUVOR94zH9auCySyk9CQ==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/core": "^3.13.0",
"@tiptap/pm": "^3.13.0"
}
},
"node_modules/@tiptap/extension-document": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-3.13.0.tgz",
"integrity": "sha512-RjU7hTJwjKXIdY57o/Pc+Yr8swLkrwT7PBQ/m+LCX5oO/V2wYoWCjoBYnK5KSHrWlNy/aLzC33BvLeqZZ9nzlQ==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/core": "^3.13.0"
}
},
"node_modules/@tiptap/extension-dropcursor": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-3.13.0.tgz",
"integrity": "sha512-m7GPT3c/83ni+bbU8c+3dpNa8ug+aQ4phNB1Q52VQG3oTonDJnZS7WCtn3lB/Hi1LqoqMtEHwhepU2eD+JeXqQ==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/extensions": "^3.13.0"
}
},
"node_modules/@tiptap/extension-floating-menu": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-3.13.0.tgz",
"integrity": "sha512-OsezV2cMofZM4c13gvgi93IEYBUzZgnu8BXTYZQiQYekz4bX4uulBmLa1KOA9EN71FzS+SoLkXHU0YzlbLjlxA==",
"license": "MIT",
"optional": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@floating-ui/dom": "^1.0.0",
"@tiptap/core": "^3.13.0",
"@tiptap/pm": "^3.13.0"
}
},
"node_modules/@tiptap/extension-gapcursor": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-3.13.0.tgz",
"integrity": "sha512-KVxjQKkd964nin+1IdM2Dvej/Jy4JTMcMgq5seusUhJ9T9P8F9s2D5Iefwgkps3OCzub/aF+eAsZe+1P5KSIgA==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/extensions": "^3.13.0"
}
},
"node_modules/@tiptap/extension-hard-break": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-3.13.0.tgz",
"integrity": "sha512-nH1OBaO+/pakhu+P1jF208mPgB70IKlrR/9d46RMYoYbqJTNf4KVLx5lHAOHytIhjcNg+MjyTfJWfkK+dyCCyg==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/core": "^3.13.0"
}
},
"node_modules/@tiptap/extension-heading": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-3.13.0.tgz",
"integrity": "sha512-8VKWX8waYPtUWN97J89em9fOtxNteh6pvUEd0htcOAtoxjt2uZjbW5N4lKyWhNKifZBrVhH2Cc2NUPuftCVgxw==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/core": "^3.13.0"
}
},
"node_modules/@tiptap/extension-horizontal-rule": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.13.0.tgz",
"integrity": "sha512-ZUFyORtjj22ib8ykbxRhWFQOTZjNKqOsMQjaAGof30cuD2DN5J5pMz7Haj2fFRtLpugWYH+f0Mi+WumQXC3hCw==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/core": "^3.13.0",
"@tiptap/pm": "^3.13.0"
}
},
"node_modules/@tiptap/extension-italic": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.13.0.tgz",
"integrity": "sha512-XbVTgmzk1kgUMTirA6AGdLTcKHUvEJoh3R4qMdPtwwygEOe7sBuvKuLtF6AwUtpnOM+Y3tfWUTNEDWv9AcEdww==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/core": "^3.13.0"
}
},
"node_modules/@tiptap/extension-link": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-3.13.0.tgz",
"integrity": "sha512-LuFPJ5GoL12GHW4A+USsj60O90pLcwUPdvEUSWewl9USyG6gnLnY/j5ZOXPYH7LiwYW8+lhq7ABwrDF2PKyBbA==",
"license": "MIT",
"dependencies": {
"linkifyjs": "^4.3.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/core": "^3.13.0",
"@tiptap/pm": "^3.13.0"
}
},
"node_modules/@tiptap/extension-list": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.13.0.tgz",
"integrity": "sha512-MMFH0jQ4LeCPkJJFyZ77kt6eM/vcKujvTbMzW1xSHCIEA6s4lEcx9QdZMPpfmnOvTzeoVKR4nsu2t2qT9ZXzAw==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/core": "^3.13.0",
"@tiptap/pm": "^3.13.0"
}
},
"node_modules/@tiptap/extension-list-item": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-3.13.0.tgz",
"integrity": "sha512-63NbcS/XeQP2jcdDEnEAE3rjJICDj8y1SN1h/MsJmSt1LusnEo8WQ2ub86QELO6XnD3M04V03cY6Knf6I5mTkw==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/extension-list": "^3.13.0"
}
},
"node_modules/@tiptap/extension-list-keymap": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-list-keymap/-/extension-list-keymap-3.13.0.tgz",
"integrity": "sha512-P+HtIa1iwosb1feFc8B/9MN5EAwzS+/dZ0UH0CTF2E4wnp5Z9OMxKl1IYjfiCwHzZrU5Let+S/maOvJR/EmV0g==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/extension-list": "^3.13.0"
}
},
"node_modules/@tiptap/extension-ordered-list": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-3.13.0.tgz",
"integrity": "sha512-QuDyLzuK/3vCvx9GeKhgvHWrGECBzmJyAx6gli2HY+Iil7XicbfltV4nvhIxgxzpx3LDHLKzJN9pBi+2MzX60g==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/extension-list": "^3.13.0"
}
},
"node_modules/@tiptap/extension-paragraph": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-3.13.0.tgz",
"integrity": "sha512-9csQde1i0yeZI5oQQ9e1GYNtGL2JcC2d8Fwtw9FsGC8yz2W0h+Fmk+3bc2kobbtO5LGqupSc1fKM8fAg5rSRDg==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/core": "^3.13.0"
}
},
"node_modules/@tiptap/extension-placeholder": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-3.13.0.tgz",
"integrity": "sha512-Au4ktRBraQktX9gjSzGWyJV6kPof7+kOhzE8ej+rOMjIrHbx3DCHy1CJWftSO9BbqIyonjsFmm4nE+vjzZ3Z5Q==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/extensions": "^3.13.0"
}
},
"node_modules/@tiptap/extension-strike": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-3.13.0.tgz",
"integrity": "sha512-VHhWNqTAMOfrC48m2FcPIZB0nhl6XHQviAV16SBc+EFznKNv9tQUsqQrnuQ2y6ZVfqq5UxvZ3hKF/JlN/Ff7xw==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/core": "^3.13.0"
}
},
"node_modules/@tiptap/extension-text": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.13.0.tgz",
"integrity": "sha512-VcZIna93rixw7hRkHGCxDbL3kvJWi80vIT25a2pXg0WP1e7Pi3nBYvZIL4SQtkbBCji9EHrbZx3p8nNPzfazYw==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/core": "^3.13.0"
}
},
"node_modules/@tiptap/extension-underline": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-3.13.0.tgz",
"integrity": "sha512-VDQi+UYw0tFnfghpthJTFmtJ3yx90kXeDwFvhmT8G+O+si5VmP05xYDBYBmYCix5jqKigJxEASiBL0gYOgMDEg==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/core": "^3.13.0"
}
},
"node_modules/@tiptap/extensions": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.13.0.tgz",
"integrity": "sha512-i7O0ptSibEtTy+2PIPsNKEvhTvMaFJg1W4Oxfnbuxvaigs7cJV9Q0lwDUcc7CPsNw2T1+44wcxg431CzTvdYoA==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/core": "^3.13.0",
"@tiptap/pm": "^3.13.0"
}
},
"node_modules/@tiptap/pm": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.13.0.tgz",
"integrity": "sha512-WKR4ucALq+lwx0WJZW17CspeTpXorbIOpvKv5mulZica6QxqfMhn8n1IXCkDws/mCoLRx4Drk5d377tIjFNsvQ==",
"license": "MIT",
"dependencies": {
"prosemirror-changeset": "^2.3.0",
"prosemirror-collab": "^1.3.1",
"prosemirror-commands": "^1.6.2",
"prosemirror-dropcursor": "^1.8.1",
"prosemirror-gapcursor": "^1.3.2",
"prosemirror-history": "^1.4.1",
"prosemirror-inputrules": "^1.4.0",
"prosemirror-keymap": "^1.2.2",
"prosemirror-markdown": "^1.13.1",
"prosemirror-menu": "^1.2.4",
"prosemirror-model": "^1.24.1",
"prosemirror-schema-basic": "^1.2.3",
"prosemirror-schema-list": "^1.5.0",
"prosemirror-state": "^1.4.3",
"prosemirror-tables": "^1.6.4",
"prosemirror-trailing-node": "^3.0.0",
"prosemirror-transform": "^1.10.2",
"prosemirror-view": "^1.38.1"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
}
},
"node_modules/@tiptap/react": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/react/-/react-3.13.0.tgz",
"integrity": "sha512-VqpqNZ9qtPr3pWK4NsZYxXgLSEiAnzl6oS7tEGmkkvJbcGSC+F7R13Xc9twv/zT5QCLxaHdEbmxHbuAIkrMgJQ==",
"license": "MIT",
"dependencies": {
"@types/use-sync-external-store": "^0.0.6",
"fast-equals": "^5.3.3",
"use-sync-external-store": "^1.4.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"optionalDependencies": {
"@tiptap/extension-bubble-menu": "^3.13.0",
"@tiptap/extension-floating-menu": "^3.13.0"
},
"peerDependencies": {
"@tiptap/core": "^3.13.0",
"@tiptap/pm": "^3.13.0",
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"@types/react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/@tiptap/starter-kit": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-3.13.0.tgz",
"integrity": "sha512-Ojn6sRub04CRuyQ+9wqN62JUOMv+rG1vXhc2s6DCBCpu28lkCMMW+vTe7kXJcEdbot82+5swPbERw9vohswFzg==",
"license": "MIT",
"dependencies": {
"@tiptap/core": "^3.13.0",
"@tiptap/extension-blockquote": "^3.13.0",
"@tiptap/extension-bold": "^3.13.0",
"@tiptap/extension-bullet-list": "^3.13.0",
"@tiptap/extension-code": "^3.13.0",
"@tiptap/extension-code-block": "^3.13.0",
"@tiptap/extension-document": "^3.13.0",
"@tiptap/extension-dropcursor": "^3.13.0",
"@tiptap/extension-gapcursor": "^3.13.0",
"@tiptap/extension-hard-break": "^3.13.0",
"@tiptap/extension-heading": "^3.13.0",
"@tiptap/extension-horizontal-rule": "^3.13.0",
"@tiptap/extension-italic": "^3.13.0",
"@tiptap/extension-link": "^3.13.0",
"@tiptap/extension-list": "^3.13.0",
"@tiptap/extension-list-item": "^3.13.0",
"@tiptap/extension-list-keymap": "^3.13.0",
"@tiptap/extension-ordered-list": "^3.13.0",
"@tiptap/extension-paragraph": "^3.13.0",
"@tiptap/extension-strike": "^3.13.0",
"@tiptap/extension-text": "^3.13.0",
"@tiptap/extension-underline": "^3.13.0",
"@tiptap/extensions": "^3.13.0",
"@tiptap/pm": "^3.13.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
}
},
"node_modules/@tybys/wasm-util": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
@ -4044,6 +4501,28 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/linkify-it": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
"integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
"license": "MIT"
},
"node_modules/@types/markdown-it": {
"version": "14.1.2",
"resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz",
"integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==",
"license": "MIT",
"dependencies": {
"@types/linkify-it": "^5",
"@types/mdurl": "^2"
}
},
"node_modules/@types/mdurl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
"integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
"license": "MIT"
},
"node_modules/@types/node": {
"version": "20.19.24",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.24.tgz",
@ -4074,6 +4553,12 @@
"@types/react": "^19.2.0"
}
},
"node_modules/@types/use-sync-external-store": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.46.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.2.tgz",
@ -4875,7 +5360,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true,
"license": "Python-2.0"
},
"node_modules/aria-hidden": {
@ -5514,6 +5998,12 @@
"node": ">=18"
}
},
"node_modules/crelt": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
"license": "MIT"
},
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@ -6112,7 +6602,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
@ -6607,6 +7096,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/fast-equals": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.3.3.tgz",
"integrity": "sha512-/boTcHZeIAQ2r/tL11voclBHDeP9WPxLt+tyAbVSyyXuUFyh0Tne7gJZTqGbxnvj79TjLdCXLOY7UIPhyG5MTw==",
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/fast-glob": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
@ -8164,6 +8662,21 @@
"url": "https://opencollective.com/parcel"
}
},
"node_modules/linkify-it": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
"integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
"license": "MIT",
"dependencies": {
"uc.micro": "^2.0.0"
}
},
"node_modules/linkifyjs": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.2.tgz",
"integrity": "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==",
"license": "MIT"
},
"node_modules/lint-staged": {
"version": "16.2.6",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.2.6.tgz",
@ -8292,6 +8805,35 @@
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
"node_modules/markdown-it": {
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
"integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1",
"entities": "^4.4.0",
"linkify-it": "^5.0.0",
"mdurl": "^2.0.0",
"punycode.js": "^2.3.1",
"uc.micro": "^2.1.0"
},
"bin": {
"markdown-it": "bin/markdown-it.mjs"
}
},
"node_modules/markdown-it/node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@ -8309,6 +8851,12 @@
"dev": true,
"license": "CC0-1.0"
},
"node_modules/mdurl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
"license": "MIT"
},
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@ -8750,6 +9298,12 @@
"node": ">= 0.8.0"
}
},
"node_modules/orderedmap": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz",
"integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==",
"license": "MIT"
},
"node_modules/own-keys": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
@ -9010,6 +9564,201 @@
"react-is": "^16.13.1"
}
},
"node_modules/prosemirror-changeset": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.3.1.tgz",
"integrity": "sha512-j0kORIBm8ayJNl3zQvD1TTPHJX3g042et6y/KQhZhnPrruO8exkTgG8X+NRpj7kIyMMEx74Xb3DyMIBtO0IKkQ==",
"license": "MIT",
"dependencies": {
"prosemirror-transform": "^1.0.0"
}
},
"node_modules/prosemirror-collab": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.1.tgz",
"integrity": "sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==",
"license": "MIT",
"dependencies": {
"prosemirror-state": "^1.0.0"
}
},
"node_modules/prosemirror-commands": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz",
"integrity": "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==",
"license": "MIT",
"dependencies": {
"prosemirror-model": "^1.0.0",
"prosemirror-state": "^1.0.0",
"prosemirror-transform": "^1.10.2"
}
},
"node_modules/prosemirror-dropcursor": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.2.tgz",
"integrity": "sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==",
"license": "MIT",
"dependencies": {
"prosemirror-state": "^1.0.0",
"prosemirror-transform": "^1.1.0",
"prosemirror-view": "^1.1.0"
}
},
"node_modules/prosemirror-gapcursor": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.4.0.tgz",
"integrity": "sha512-z00qvurSdCEWUIulij/isHaqu4uLS8r/Fi61IbjdIPJEonQgggbJsLnstW7Lgdk4zQ68/yr6B6bf7sJXowIgdQ==",
"license": "MIT",
"dependencies": {
"prosemirror-keymap": "^1.0.0",
"prosemirror-model": "^1.0.0",
"prosemirror-state": "^1.0.0",
"prosemirror-view": "^1.0.0"
}
},
"node_modules/prosemirror-history": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.5.0.tgz",
"integrity": "sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==",
"license": "MIT",
"dependencies": {
"prosemirror-state": "^1.2.2",
"prosemirror-transform": "^1.0.0",
"prosemirror-view": "^1.31.0",
"rope-sequence": "^1.3.0"
}
},
"node_modules/prosemirror-inputrules": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.5.1.tgz",
"integrity": "sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==",
"license": "MIT",
"dependencies": {
"prosemirror-state": "^1.0.0",
"prosemirror-transform": "^1.0.0"
}
},
"node_modules/prosemirror-keymap": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.3.tgz",
"integrity": "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==",
"license": "MIT",
"dependencies": {
"prosemirror-state": "^1.0.0",
"w3c-keyname": "^2.2.0"
}
},
"node_modules/prosemirror-markdown": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.13.2.tgz",
"integrity": "sha512-FPD9rHPdA9fqzNmIIDhhnYQ6WgNoSWX9StUZ8LEKapaXU9i6XgykaHKhp6XMyXlOWetmaFgGDS/nu/w9/vUc5g==",
"license": "MIT",
"dependencies": {
"@types/markdown-it": "^14.0.0",
"markdown-it": "^14.0.0",
"prosemirror-model": "^1.25.0"
}
},
"node_modules/prosemirror-menu": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.2.5.tgz",
"integrity": "sha512-qwXzynnpBIeg1D7BAtjOusR+81xCp53j7iWu/IargiRZqRjGIlQuu1f3jFi+ehrHhWMLoyOQTSRx/IWZJqOYtQ==",
"license": "MIT",
"dependencies": {
"crelt": "^1.0.0",
"prosemirror-commands": "^1.0.0",
"prosemirror-history": "^1.0.0",
"prosemirror-state": "^1.0.0"
}
},
"node_modules/prosemirror-model": {
"version": "1.25.4",
"resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz",
"integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==",
"license": "MIT",
"dependencies": {
"orderedmap": "^2.0.0"
}
},
"node_modules/prosemirror-schema-basic": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.4.tgz",
"integrity": "sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==",
"license": "MIT",
"dependencies": {
"prosemirror-model": "^1.25.0"
}
},
"node_modules/prosemirror-schema-list": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz",
"integrity": "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==",
"license": "MIT",
"dependencies": {
"prosemirror-model": "^1.0.0",
"prosemirror-state": "^1.0.0",
"prosemirror-transform": "^1.7.3"
}
},
"node_modules/prosemirror-state": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz",
"integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==",
"license": "MIT",
"dependencies": {
"prosemirror-model": "^1.0.0",
"prosemirror-transform": "^1.0.0",
"prosemirror-view": "^1.27.0"
}
},
"node_modules/prosemirror-tables": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.8.3.tgz",
"integrity": "sha512-wbqCR/RlRPRe41a4LFtmhKElzBEfBTdtAYWNIGHM6X2e24NN/MTNUKyXjjphfAfdQce37Kh/5yf765mLPYDe7Q==",
"license": "MIT",
"dependencies": {
"prosemirror-keymap": "^1.2.3",
"prosemirror-model": "^1.25.4",
"prosemirror-state": "^1.4.4",
"prosemirror-transform": "^1.10.5",
"prosemirror-view": "^1.41.4"
}
},
"node_modules/prosemirror-trailing-node": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-3.0.0.tgz",
"integrity": "sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==",
"license": "MIT",
"dependencies": {
"@remirror/core-constants": "3.0.0",
"escape-string-regexp": "^4.0.0"
},
"peerDependencies": {
"prosemirror-model": "^1.22.1",
"prosemirror-state": "^1.4.2",
"prosemirror-view": "^1.33.8"
}
},
"node_modules/prosemirror-transform": {
"version": "1.10.5",
"resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.10.5.tgz",
"integrity": "sha512-RPDQCxIDhIBb1o36xxwsaeAvivO8VLJcgBtzmOwQ64bMtsVFh5SSuJ6dWSxO1UsHTiTXPCgQm3PDJt7p6IOLbw==",
"license": "MIT",
"dependencies": {
"prosemirror-model": "^1.21.0"
}
},
"node_modules/prosemirror-view": {
"version": "1.41.4",
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.4.tgz",
"integrity": "sha512-WkKgnyjNncri03Gjaz3IFWvCAE94XoiEgvtr0/r2Xw7R8/IjK3sKLSiDoCHWcsXSAinVaKlGRZDvMCsF1kbzjA==",
"license": "MIT",
"dependencies": {
"prosemirror-model": "^1.20.0",
"prosemirror-state": "^1.0.0",
"prosemirror-transform": "^1.1.0"
}
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@ -9020,6 +9769,15 @@
"node": ">=6"
}
},
"node_modules/punycode.js": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
"integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/pure-rand": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz",
@ -9382,6 +10140,12 @@
"fsevents": "~2.3.2"
}
},
"node_modules/rope-sequence": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz",
"integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==",
"license": "MIT"
},
"node_modules/run-parallel": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@ -10381,6 +11145,12 @@
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/uc.micro": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
"integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
"license": "MIT"
},
"node_modules/unbox-primitive": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
@ -10526,6 +11296,15 @@
}
}
},
"node_modules/use-sync-external-store": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
"integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
"license": "MIT",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/vite": {
"version": "7.1.12",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz",
@ -10705,6 +11484,12 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/w3c-keyname": {
"version": "2.2.8",
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
"license": "MIT"
},
"node_modules/w3c-xmlserializer": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
@ -11075,21 +11860,6 @@
"optional": true
}
}
},
"node_modules/@next/swc-win32-x64-msvc": {
"version": "16.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.1.tgz",
"integrity": "sha512-XeEUJsE4JYtfrXe/LaJn3z1pD19fK0Q6Er8Qoufi+HqvdO4LEPyCxLUt4rxA+4RfYo6S9gMlmzCMU2F+AatFqQ==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
}
}
}

View file

@ -22,6 +22,10 @@
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-tooltip": "^1.2.8",
"@supabase/ssr": "^0.7.0",
"@tiptap/extension-placeholder": "^3.13.0",
"@tiptap/extension-underline": "^3.13.0",
"@tiptap/react": "^3.13.0",
"@tiptap/starter-kit": "^3.13.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"dotenv": "^17.2.3",

View file

@ -0,0 +1,7 @@
import CourseCreateView from "@/src/modules/courses/ui/views/course-create-view";
const CourseCreate = () => {
return <CourseCreateView />;
};
export default CourseCreate;

View file

@ -1,5 +1,7 @@
import CourseView from "@/src/modules/courses/ui/views/course-view";
const Course = () => {
return <div>Course</div>;
return <CourseView />;
};
export default Course;

View file

@ -20,7 +20,7 @@ export default function RootLayout({
}>) {
return (
<html lang="en" className={salmondFont.className}>
<body className={`antialiased`}>
<body className={`antialiased bg-muted`}>
<AuthInitializer />
<main>{children}</main>
<Toaster

View file

@ -0,0 +1,66 @@
"use client";
import {
DropdownMenu,
DropdownMenuGroup,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
} from "../ui/dropdown-menu";
import { Input } from "../ui/input";
import { Label } from "../ui/label";
import { ChevronDown } from "lucide-react";
interface GenericDropdownProps {
label: string;
selected: string;
onSelect: (value: string) => void;
options: { value: string; label: string }[];
error?: string;
placeholder?: string;
}
const GenericDropdown: React.FC<GenericDropdownProps> = ({
label,
selected,
onSelect,
options,
error,
placeholder = "Auswählen...",
}) => {
const displayValue = options.find((opt) => opt.value === selected)?.label || "";
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<div className="relative w-full">
<Label className="p-1 mb-2 text-blue text-lg font-semibold">{label}</Label>
<Input
className="h-[50px] border-blue rounded-xl w-full cursor-pointer pr-10"
value={displayValue}
placeholder={placeholder}
readOnly
/>
<ChevronDown
className="absolute right-3 top-[75%] transform -translate-y-1/2 text-blue pointer-events-none"
size={24}
/>
{error && <p className="text-red-500 text-sm mt-1">{error}</p>}
</div>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-(--radix-dropdown-menu-trigger-width)">
<DropdownMenuGroup>
<DropdownMenuItem onSelect={() => onSelect("")} className="text-gray-500">
Auswahl löschen
</DropdownMenuItem>
{options.map((option) => (
<DropdownMenuItem key={option.value} onSelect={() => onSelect(option.value)}>
{option.label}
</DropdownMenuItem>
))}
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
);
};
export default GenericDropdown;

View file

@ -0,0 +1,93 @@
"use client";
import {
DropdownMenu,
DropdownMenuGroup,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
} from "../ui/dropdown-menu";
import { Input } from "../ui/input";
import { Label } from "../ui/label";
import { ChevronDown } from "lucide-react";
import { Checkbox } from "../ui/checkbox";
interface MultiSelectDropdownProps {
label: string;
selected: string[];
onSelect: (values: string[]) => void;
options: { value: string; label: string }[];
error?: string;
}
const MultiSelectDropdown: React.FC<MultiSelectDropdownProps> = ({
label,
selected,
onSelect,
options,
error,
}) => {
const toggleOption = (value: string) => {
if (selected.includes(value)) {
onSelect(selected.filter((item) => item !== value));
} else {
onSelect([...selected, value]);
}
};
const displayValue =
selected.length > 0
? options
.filter((opt) => selected.includes(opt.value))
.map((opt) => opt.label)
.join(", ")
: "";
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<div className="relative w-full">
<Label className="p-1 mb-2 text-blue text-lg font-semibold">{label}</Label>
<Input
className="h-[50px] border-blue rounded-xl w-full cursor-pointer pr-10"
value={displayValue}
placeholder="Auswählen..."
readOnly
/>
<ChevronDown
className="absolute right-3 top-[75%] transform -translate-y-1/2 text-blue pointer-events-none"
size={24}
/>
{error && <p className="text-red-500 text-sm mt-1">{error}</p>}
</div>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-(--radix-dropdown-menu-trigger-width)">
<DropdownMenuGroup>
<DropdownMenuItem
onSelect={(e) => {
e.preventDefault();
onSelect([]);
}}
className="text-gray-500"
>
Auswahl löschen
</DropdownMenuItem>
{options.map((option) => (
<DropdownMenuItem
key={option.value}
onSelect={(e) => {
e.preventDefault();
toggleOption(option.value);
}}
className="flex items-center gap-2"
>
<Checkbox checked={selected.includes(option.value)} className="h-4 w-4" />
{option.label}
</DropdownMenuItem>
))}
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
);
};
export default MultiSelectDropdown;

View file

@ -0,0 +1,203 @@
"use client";
import { useEditor, EditorContent } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import Placeholder from "@tiptap/extension-placeholder";
import Underline from "@tiptap/extension-underline";
import { Label } from "../ui/label";
import {
Bold,
Italic,
Underline as UnderlineIcon,
List,
ListOrdered,
Heading1,
Heading2,
Heading3,
} from "lucide-react";
interface RichTextEditorProps {
label: string;
value: string;
onChange: (value: string) => void;
placeholder?: string;
error?: string;
}
const RichTextEditor: React.FC<RichTextEditorProps> = ({
label,
value,
onChange,
placeholder = "Text eingeben...",
error,
}) => {
const editor = useEditor({
extensions: [
StarterKit,
Underline,
Placeholder.configure({
placeholder,
}),
],
content: value,
immediatelyRender: false,
onUpdate: ({ editor }) => {
onChange(editor.getHTML());
},
editorProps: {
attributes: {
class: "focus:outline-none min-h-[150px] p-4",
},
},
});
if (!editor) {
return null;
}
const MenuButton = ({
onClick,
active,
children,
}: {
onClick: () => void;
active: boolean;
children: React.ReactNode;
}) => (
<button
onClick={onClick}
type="button"
className={`p-2 rounded hover:bg-gray-100 transition-colors ${
active ? "bg-blue/10 text-blue" : "text-gray-600"
}`}
>
{children}
</button>
);
return (
<div className="w-full">
<Label className="p-1 mb-2 text-blue text-lg font-semibold">{label}</Label>
<style jsx global>{`
.editor-content .ProseMirror {
outline: none;
}
.editor-content h1 {
font-size: 2em;
font-weight: bold;
margin-top: 0.5em;
margin-bottom: 0.5em;
line-height: 1.2;
}
.editor-content h2 {
font-size: 1.5em;
font-weight: bold;
margin-top: 0.5em;
margin-bottom: 0.5em;
line-height: 1.3;
}
.editor-content h3 {
font-size: 1.25em;
font-weight: bold;
margin-top: 0.5em;
margin-bottom: 0.5em;
line-height: 1.4;
}
.editor-content ul {
list-style-type: disc;
padding-left: 1.5em;
margin-top: 0.5em;
margin-bottom: 0.5em;
}
.editor-content ol {
list-style-type: decimal;
padding-left: 1.5em;
margin-top: 0.5em;
margin-bottom: 0.5em;
}
.editor-content li {
margin-bottom: 0.25em;
}
.editor-content strong {
font-weight: bold;
}
.editor-content em {
font-style: italic;
}
.editor-content u {
text-decoration: underline;
}
.editor-content p {
margin-bottom: 0.5em;
}
.editor-content .ProseMirror p.is-editor-empty:first-child::before {
color: #adb5bd;
content: attr(data-placeholder);
float: left;
height: 0;
pointer-events: none;
}
`}</style>
<div className="border border-blue rounded-xl overflow-hidden">
{/* Toolbar */}
<div className="flex gap-1 p-2 border-b border-gray-200 bg-gray-50 flex-wrap">
<MenuButton
onClick={() => editor.chain().focus().toggleBold().run()}
active={editor.isActive("bold")}
>
<Bold size={18} />
</MenuButton>
<MenuButton
onClick={() => editor.chain().focus().toggleItalic().run()}
active={editor.isActive("italic")}
>
<Italic size={18} />
</MenuButton>
<MenuButton
onClick={() => editor.chain().focus().toggleUnderline().run()}
active={editor.isActive("underline")}
>
<UnderlineIcon size={18} />
</MenuButton>
<div className="w-px h-6 bg-gray-300 mx-1 self-center" />
<MenuButton
onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}
active={editor.isActive("heading", { level: 1 })}
>
<Heading1 size={18} />
</MenuButton>
<MenuButton
onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}
active={editor.isActive("heading", { level: 2 })}
>
<Heading2 size={18} />
</MenuButton>
<MenuButton
onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()}
active={editor.isActive("heading", { level: 3 })}
>
<Heading3 size={18} />
</MenuButton>
<div className="w-px h-6 bg-gray-300 mx-1 self-center" />
<MenuButton
onClick={() => editor.chain().focus().toggleBulletList().run()}
active={editor.isActive("bulletList")}
>
<List size={18} />
</MenuButton>
<MenuButton
onClick={() => editor.chain().focus().toggleOrderedList().run()}
active={editor.isActive("orderedList")}
>
<ListOrdered size={18} />
</MenuButton>
</div>
{/* Editor Content */}
<EditorContent editor={editor} className="bg-white editor-content" />
</div>
{error && <p className="text-red-500 text-sm mt-1">{error}</p>}
</div>
);
};
export default RichTextEditor;

View file

@ -14,7 +14,7 @@ const buttonVariants = cva(
destructive:
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline:
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
"bg-transparent text-blue border border-blue cursor-pointer",
secondary:
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
ghost:

View file

@ -0,0 +1,157 @@
"use client";
import { useState } from "react";
import InputComponent from "@/src/components/layout/InputComponent";
import GenericDropdown from "@/src/components/layout/GenericDropdown";
import MultiSelectDropdown from "@/src/components/layout/MultiSelectDropdown";
import RichTextEditor from "@/src/components/layout/RichTextEditor";
import { Button } from "@/src/components/ui/button";
import { Checkbox } from "@/src/components/ui/checkbox";
import { Label } from "@/src/components/ui/label";
// Hardcoded data
const SPORTS = [
{ value: "football", label: "Fußball" },
{ value: "basketball", label: "Basketball" },
{ value: "volleyball", label: "Volleyball" },
{ value: "tennis", label: "Tennis" },
{ value: "swimming", label: "Schwimmen" },
{ value: "yoga", label: "Yoga" },
];
const TRAINERS = [
{ value: "trainer1", label: "Max Mustermann" },
{ value: "trainer2", label: "Anna Schmidt" },
{ value: "trainer3", label: "Peter Weber" },
{ value: "trainer4", label: "Lisa Müller" },
];
const ROOMS = [
{ value: "room1", label: "Sporthalle 1" },
{ value: "room2", label: "Sporthalle 2" },
{ value: "room3", label: "Gymnastikraum" },
{ value: "room4", label: "Schwimmbad" },
{ value: "room5", label: "Tennisplatz" },
];
const FREQUENCIES = [
{ value: "daily", label: "Täglich" },
{ value: "weekly", label: "Wöchentlich" },
{ value: "biweekly", label: "Alle 2 Wochen" },
{ value: "monthly", label: "Monatlich" },
{ value: "custom", label: "Individuell" },
];
const WEEKDAYS = [
{ value: "monday", label: "Montag" },
{ value: "tuesday", label: "Dienstag" },
{ value: "wednesday", label: "Mittwoch" },
{ value: "thursday", label: "Donnerstag" },
{ value: "friday", label: "Freitag" },
{ value: "saturday", label: "Samstag" },
{ value: "sunday", label: "Sonntag" },
];
const CourseCreateView = () => {
const [selectedSport, setSelectedSport] = useState("");
const [selectedTrainers, setSelectedTrainers] = useState<string[]>([]);
const [selectedRoom, setSelectedRoom] = useState("");
const [description, setDescription] = useState("");
const [isStandingOrder, setIsStandingOrder] = useState(false);
const [selectedFrequency, setSelectedFrequency] = useState("");
const [selectedWeekdays, setSelectedWeekdays] = useState<string[]>([]);
return (
<div>
<h1 className="text-3xl font-bold">Kurs anlegen </h1>
<p className="text-xl mt-2">
Alle Kurse können nach dem Speichern angepasst oder gelöscht werden
</p>
<div className="mt-6 flex flex-col gap-4 max-w-xl">
<InputComponent isLabel label="Kursnamen" type="text" id="course-create-name" />
<GenericDropdown
label="Sportart"
selected={selectedSport}
onSelect={setSelectedSport}
options={SPORTS}
/>
<InputComponent isLabel label="Datum" type="date" id="course-create-date" />
<InputComponent isLabel label="Uhrzeit" type="time" id="course-create-time" />
<MultiSelectDropdown
label="Trainer"
selected={selectedTrainers}
onSelect={setSelectedTrainers}
options={TRAINERS}
/>
<GenericDropdown
label="Raum"
selected={selectedRoom}
onSelect={setSelectedRoom}
options={ROOMS}
/>
<RichTextEditor
label="Was man mitbringen sollte"
value={description}
onChange={setDescription}
placeholder="Beschreibe, was die Teilnehmer mitbringen sollten..."
/>
<div className="flex items-center gap-2">
<Checkbox
id="course-create-standing-order"
className="shadow-none h-5 w-5 bg-white border-blue cursor-pointer"
checked={isStandingOrder}
onCheckedChange={(checked) => {
setIsStandingOrder(checked === true);
if (!checked) {
setSelectedFrequency("");
setSelectedWeekdays([]);
}
}}
/>
<Label htmlFor="course-create-standing-order" className="cursor-pointer text-lg mt-2">
Als Dauerauftrag anlegen
</Label>
</div>
{isStandingOrder && (
<>
<GenericDropdown
label="Wie oft soll dieser Kurs stattfinden?"
selected={selectedFrequency}
onSelect={(value) => {
setSelectedFrequency(value);
if (value !== "custom") {
setSelectedWeekdays([]);
}
}}
options={FREQUENCIES}
/>
{selectedFrequency === "custom" && (
<MultiSelectDropdown
label="An welchen Tagen soll der Kurs stattfinden?"
selected={selectedWeekdays}
onSelect={setSelectedWeekdays}
options={WEEKDAYS}
/>
)}
</>
)}
<div className="flex gap-2">
<Button>Veröffentlichen</Button>
<Button variant="outline">Abbrechen</Button>
</div>
<button className="mt-6 w-fit cursor-pointer hover:underline">Entwurf speichern</button>
<button className="mb-6 w-fit cursor-pointer hover:underline">Als Vorlage speichern</button>
</div>
</div>
);
};
export default CourseCreateView;

View file

@ -0,0 +1,29 @@
"use client";
import { Button } from "@/src/components/ui/button";
import { useRouter } from "next/navigation";
const CourseView = () => {
const router = useRouter();
return (
<div>
<div className="w-full flex items-center justify-between p-2">
<h1 className="text-4xl font-bold">Kurse</h1>
<div className="flex gap-4">
<Button
onClick={() => router.push("/courses/create")}
className="text-sm tracking-wide font-normal"
>
Kurs anlegen
</Button>
<Button variant="outline" className="text-sm tracking-wide font-normal">
Kurs löschen
</Button>
</div>
</div>
</div>
);
};
export default CourseView;

View file

@ -7,7 +7,8 @@ import { usePathname } from "next/navigation";
const menuTitles: Record<string, string> = {
"/dashboard": "Dashboard",
"/courses": "Kurse",
"/courses": "Kurs Übersicht",
"/courses/create": "Erstelle einen Kurs",
};
const DashboardNavbar = () => {