From 970045446b801256588f53343a885e90d339daae Mon Sep 17 00:00:00 2001 From: maxime Date: Wed, 14 Feb 2024 22:26:41 +0100 Subject: [PATCH] remove php, the front is now independant and uses react-router to serve the pages --- .eslintrc.cjs | 18 ++ .gitignore | 62 ++--- README.md | 1 - composer.json | 18 -- config.php | 33 --- format.sh | 9 - front/ViewRenderer.tsx | 19 -- front/style/theme/dark.css | 9 - index.html | 13 + package.json | 13 +- phpstan.neon | 12 - profiles/dev-config-profile.php | 43 --- profiles/prod-config-profile.php | 32 --- public/.htaccess | 4 - public/api/.htaccess | 4 - public/api/index.php | 99 ------- public/assets | 1 - public/front | 1 - public/index.php | 129 --------- sql/database.php | 32 --- sql/setup-tables.sql | 50 ---- src/Api/API.php | 65 ----- src/Api/APIControl.php | 45 ---- src/Api/Controller/APIAccountsController.php | 109 -------- src/Api/Controller/APIAuthController.php | 44 ---- src/Api/Controller/APIServerController.php | 45 ---- src/Api/Controller/APITacticController.php | 87 ------- src/Api/Controller/APITeamController.php | 79 ------ src/App.tsx | 37 +++ src/App/App.php | 98 ------- src/App/AppControl.php | 44 ---- src/App/Controller/AuthController.php | 99 ------- src/App/Controller/EditorController.php | 87 ------- src/App/Controller/TeamController.php | 246 ------------------ src/App/Controller/UserController.php | 66 ----- src/App/Controller/VisualizerController.php | 39 --- src/App/Session/MutableSessionHandle.php | 22 -- src/App/Session/PhpSessionHandle.php | 38 --- src/App/Session/SessionHandle.php | 23 -- src/App/Validator/TacticValidator.php | 19 -- src/App/ViewHttpResponse.php | 75 ------ src/App/Views/account_settings.twig | 23 -- src/App/Views/add_member.html.twig | 118 --------- src/App/Views/delete_member.html.twig | 73 ------ src/App/Views/display_auth_confirm.html.twig | 46 ---- src/App/Views/display_login.html.twig | 108 -------- src/App/Views/display_register.html.twig | 123 --------- src/App/Views/display_results.html.twig | 18 -- src/App/Views/display_team.html.twig | 109 -------- src/App/Views/display_teams.html.twig | 61 ----- src/App/Views/edit_team.html.twig | 81 ------ src/App/Views/error.html.twig | 57 ---- src/App/Views/home.twig | 97 ------- src/App/Views/insert_team.html.twig | 81 ------ src/App/Views/list_team_by_name.html.twig | 79 ------ src/App/react-display-file.php | 58 ----- src/App/react-display.php | 13 - {front => src}/Constants.ts | 0 src/Core/Action.php | 70 ----- src/Core/Connection.php | 61 ----- src/Core/Control.php | 53 ---- .../ControlSchemaErrorResponseFactory.php | 14 - src/Core/Data/Account.php | 41 --- src/Core/Data/CourtType.php | 61 ----- src/Core/Data/Member.php | 57 ---- src/Core/Data/TacticInfo.php | 62 ----- src/Core/Data/Team.php | 38 --- src/Core/Data/TeamInfo.php | 50 ---- src/Core/Data/User.php | 84 ------ src/Core/Gateway/AccountGateway.php | 180 ------------- src/Core/Gateway/MemberGateway.php | 101 ------- src/Core/Gateway/TacticInfoGateway.php | 151 ----------- src/Core/Gateway/TeamGateway.php | 186 ------------- src/Core/Http/HttpCodes.php | 18 -- src/Core/Http/HttpRequest.php | 81 ------ src/Core/Http/HttpResponse.php | 65 ----- src/Core/Http/JsonHttpResponse.php | 28 -- src/Core/Model/AuthModel.php | 78 ------ src/Core/Model/TacticModel.php | 117 --------- src/Core/Model/TeamModel.php | 164 ------------ src/Core/Validation/ComposedValidator.php | 26 -- src/Core/Validation/DefaultValidators.php | 146 ----------- src/Core/Validation/FieldValidationFail.php | 42 --- src/Core/Validation/FunctionValidator.php | 21 -- .../Validation/SimpleFunctionValidator.php | 33 --- src/Core/Validation/Validation.php | 29 --- src/Core/Validation/ValidationFail.php | 56 ---- src/Core/Validation/Validator.php | 23 -- {front => src}/Fetcher.ts | 0 {front => src}/assets/account.svg | 0 {front => src}/assets/court/full_court.svg | 0 {front => src}/assets/court/half_court.svg | 0 {front => src}/assets/favicon.ico | Bin {front => src}/assets/icon/account.png | Bin {front => src}/assets/icon/account.svg | 0 src/assets/icon/add.svg | 1 + {front => src}/assets/icon/arrow.svg | 0 {front => src}/assets/icon/ball.svg | 0 {front => src}/assets/icon/remove.svg | 0 {front => src}/assets/logo.svg | 0 {front => src}/assets/logo192.png | Bin {front => src}/assets/logo512.png | Bin src/assets/react.svg | 1 + src/assets/vite.svg | 1 + {front => src}/components/Rack.tsx | 0 {front => src}/components/TitleInput.tsx | 0 .../components/actions/ArrowAction.tsx | 0 .../components/actions/BallAction.tsx | 0 .../components/arrows/BendableArrow.tsx | 0 .../components/editor/BallPiece.tsx | 0 .../components/editor/BasketCourt.tsx | 11 +- .../components}/editor/CourtAction.tsx | 0 .../components/editor/CourtBall.tsx | 0 .../components/editor/CourtPlayer.tsx | 0 .../components/editor/PlayerPiece.tsx | 0 .../components/editor/SavingState.tsx | 0 {front => src}/editor/ActionsDomains.ts | 0 {front => src}/editor/PlayerDomains.ts | 0 {front => src}/editor/RackedItems.ts | 0 .../editor/StepContentDomains.ts | 0 src/editor/TacticContentDomains.ts | 243 +++++++++++++++++ {front => src}/geo/Box.ts | 0 {front => src}/geo/Pos.ts | 0 src/index.css | 25 ++ src/main.tsx | 11 + {front => src}/model/Team.ts | 0 {front => src}/model/User.ts | 0 {front => src}/model/tactic/Action.ts | 0 {front => src}/model/tactic/CourtObjects.ts | 0 {front => src}/model/tactic/Player.ts | 0 {front => src}/model/tactic/Tactic.ts | 0 src/model/tactic/TacticInfo.ts | 39 +++ src/pages/404.tsx | 11 + src/pages/CreateTeamPage.tsx | 4 + {front/views => src/pages}/Editor.tsx | 60 +++-- {front/views => src/pages}/Home.tsx | 13 +- .../pages/NewTacticPage.tsx | 2 +- {front/views => src/pages}/TeamPanel.tsx | 66 +++-- {front/views => src/pages}/Visualizer.tsx | 0 .../views => src/pages}/template/Header.tsx | 2 +- {front => src}/style/actions/arrow_action.css | 0 .../style/actions/remove_action.css | 0 {front => src}/style/ball.css | 0 {front => src}/style/bendable_arrows.css | 0 {front => src}/style/editor.css | 1 + {front => src}/style/home/home.css | 6 +- {front => src}/style/home/personnal_space.css | 2 +- {front => src}/style/home/side_menu.css | 8 +- {front => src}/style/new_tactic_panel.css | 0 {front => src}/style/player.css | 0 src/style/steps_tree.css | 87 +++++++ {front => src}/style/team_panel.css | 0 {front => src}/style/template/header.css | 2 +- {front => src}/style/theme/default.css | 8 + {front => src}/style/title_input.css | 0 {front => src}/style/visualizer.css | 0 src/vite-env.d.ts | 1 + tsconfig.json | 36 ++- tsconfig.node.json | 11 + verify.sh | 9 - vite.config.ts | 56 +--- 161 files changed, 670 insertions(+), 5297 deletions(-) create mode 100644 .eslintrc.cjs delete mode 100644 composer.json delete mode 100644 config.php delete mode 100755 format.sh delete mode 100644 front/ViewRenderer.tsx delete mode 100644 front/style/theme/dark.css create mode 100644 index.html delete mode 100644 phpstan.neon delete mode 100644 profiles/dev-config-profile.php delete mode 100644 profiles/prod-config-profile.php delete mode 100644 public/.htaccess delete mode 100644 public/api/.htaccess delete mode 100644 public/api/index.php delete mode 120000 public/assets delete mode 120000 public/front delete mode 100644 public/index.php delete mode 100644 sql/database.php delete mode 100644 sql/setup-tables.sql delete mode 100644 src/Api/API.php delete mode 100644 src/Api/APIControl.php delete mode 100644 src/Api/Controller/APIAccountsController.php delete mode 100644 src/Api/Controller/APIAuthController.php delete mode 100644 src/Api/Controller/APIServerController.php delete mode 100644 src/Api/Controller/APITacticController.php delete mode 100644 src/Api/Controller/APITeamController.php create mode 100644 src/App.tsx delete mode 100644 src/App/App.php delete mode 100644 src/App/AppControl.php delete mode 100644 src/App/Controller/AuthController.php delete mode 100644 src/App/Controller/EditorController.php delete mode 100644 src/App/Controller/TeamController.php delete mode 100644 src/App/Controller/UserController.php delete mode 100644 src/App/Controller/VisualizerController.php delete mode 100644 src/App/Session/MutableSessionHandle.php delete mode 100644 src/App/Session/PhpSessionHandle.php delete mode 100644 src/App/Session/SessionHandle.php delete mode 100644 src/App/Validator/TacticValidator.php delete mode 100644 src/App/ViewHttpResponse.php delete mode 100644 src/App/Views/account_settings.twig delete mode 100644 src/App/Views/add_member.html.twig delete mode 100644 src/App/Views/delete_member.html.twig delete mode 100644 src/App/Views/display_auth_confirm.html.twig delete mode 100644 src/App/Views/display_login.html.twig delete mode 100644 src/App/Views/display_register.html.twig delete mode 100644 src/App/Views/display_results.html.twig delete mode 100644 src/App/Views/display_team.html.twig delete mode 100644 src/App/Views/display_teams.html.twig delete mode 100644 src/App/Views/edit_team.html.twig delete mode 100644 src/App/Views/error.html.twig delete mode 100644 src/App/Views/home.twig delete mode 100644 src/App/Views/insert_team.html.twig delete mode 100644 src/App/Views/list_team_by_name.html.twig delete mode 100755 src/App/react-display-file.php delete mode 100644 src/App/react-display.php rename {front => src}/Constants.ts (100%) delete mode 100644 src/Core/Action.php delete mode 100644 src/Core/Connection.php delete mode 100644 src/Core/Control.php delete mode 100644 src/Core/ControlSchemaErrorResponseFactory.php delete mode 100755 src/Core/Data/Account.php delete mode 100755 src/Core/Data/CourtType.php delete mode 100755 src/Core/Data/Member.php delete mode 100644 src/Core/Data/TacticInfo.php delete mode 100755 src/Core/Data/Team.php delete mode 100644 src/Core/Data/TeamInfo.php delete mode 100644 src/Core/Data/User.php delete mode 100644 src/Core/Gateway/AccountGateway.php delete mode 100644 src/Core/Gateway/MemberGateway.php delete mode 100644 src/Core/Gateway/TacticInfoGateway.php delete mode 100644 src/Core/Gateway/TeamGateway.php delete mode 100644 src/Core/Http/HttpCodes.php delete mode 100644 src/Core/Http/HttpRequest.php delete mode 100644 src/Core/Http/HttpResponse.php delete mode 100644 src/Core/Http/JsonHttpResponse.php delete mode 100644 src/Core/Model/AuthModel.php delete mode 100644 src/Core/Model/TacticModel.php delete mode 100644 src/Core/Model/TeamModel.php delete mode 100644 src/Core/Validation/ComposedValidator.php delete mode 100644 src/Core/Validation/DefaultValidators.php delete mode 100644 src/Core/Validation/FieldValidationFail.php delete mode 100644 src/Core/Validation/FunctionValidator.php delete mode 100644 src/Core/Validation/SimpleFunctionValidator.php delete mode 100644 src/Core/Validation/Validation.php delete mode 100644 src/Core/Validation/ValidationFail.php delete mode 100644 src/Core/Validation/Validator.php rename {front => src}/Fetcher.ts (100%) rename {front => src}/assets/account.svg (100%) rename {front => src}/assets/court/full_court.svg (100%) rename {front => src}/assets/court/half_court.svg (100%) rename {front => src}/assets/favicon.ico (100%) rename {front => src}/assets/icon/account.png (100%) rename {front => src}/assets/icon/account.svg (100%) create mode 100644 src/assets/icon/add.svg rename {front => src}/assets/icon/arrow.svg (100%) rename {front => src}/assets/icon/ball.svg (100%) rename {front => src}/assets/icon/remove.svg (100%) rename {front => src}/assets/logo.svg (100%) rename {front => src}/assets/logo192.png (100%) rename {front => src}/assets/logo512.png (100%) create mode 100644 src/assets/react.svg create mode 100644 src/assets/vite.svg rename {front => src}/components/Rack.tsx (100%) rename {front => src}/components/TitleInput.tsx (100%) rename {front => src}/components/actions/ArrowAction.tsx (100%) rename {front => src}/components/actions/BallAction.tsx (100%) rename {front => src}/components/arrows/BendableArrow.tsx (100%) rename {front => src}/components/editor/BallPiece.tsx (100%) rename {front => src}/components/editor/BasketCourt.tsx (88%) rename {front/views => src/components}/editor/CourtAction.tsx (100%) rename {front => src}/components/editor/CourtBall.tsx (100%) rename {front => src}/components/editor/CourtPlayer.tsx (100%) rename {front => src}/components/editor/PlayerPiece.tsx (100%) rename {front => src}/components/editor/SavingState.tsx (100%) rename {front => src}/editor/ActionsDomains.ts (100%) rename {front => src}/editor/PlayerDomains.ts (100%) rename {front => src}/editor/RackedItems.ts (100%) rename front/editor/TacticContentDomains.ts => src/editor/StepContentDomains.ts (100%) create mode 100644 src/editor/TacticContentDomains.ts rename {front => src}/geo/Box.ts (100%) rename {front => src}/geo/Pos.ts (100%) create mode 100644 src/index.css create mode 100644 src/main.tsx rename {front => src}/model/Team.ts (100%) rename {front => src}/model/User.ts (100%) rename {front => src}/model/tactic/Action.ts (100%) rename {front => src}/model/tactic/CourtObjects.ts (100%) rename {front => src}/model/tactic/Player.ts (100%) rename {front => src}/model/tactic/Tactic.ts (100%) create mode 100644 src/model/tactic/TacticInfo.ts create mode 100644 src/pages/404.tsx create mode 100644 src/pages/CreateTeamPage.tsx rename {front/views => src/pages}/Editor.tsx (95%) rename {front/views => src/pages}/Home.tsx (95%) rename front/views/NewTacticPanel.tsx => src/pages/NewTacticPage.tsx (97%) rename {front/views => src/pages}/TeamPanel.tsx (72%) rename {front/views => src/pages}/Visualizer.tsx (100%) rename {front/views => src/pages}/template/Header.tsx (95%) rename {front => src}/style/actions/arrow_action.css (100%) rename {front => src}/style/actions/remove_action.css (100%) rename {front => src}/style/ball.css (100%) rename {front => src}/style/bendable_arrows.css (100%) rename {front => src}/style/editor.css (99%) rename {front => src}/style/home/home.css (84%) rename {front => src}/style/home/personnal_space.css (93%) rename {front => src}/style/home/side_menu.css (81%) rename {front => src}/style/new_tactic_panel.css (100%) rename {front => src}/style/player.css (100%) create mode 100644 src/style/steps_tree.css rename {front => src}/style/team_panel.css (100%) rename {front => src}/style/template/header.css (95%) rename {front => src}/style/theme/default.css (74%) rename {front => src}/style/title_input.css (100%) rename {front => src}/style/visualizer.css (100%) create mode 100644 src/vite-env.d.ts create mode 100644 tsconfig.node.json delete mode 100755 verify.sh diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..d6c9537 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,18 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parser: '@typescript-eslint/parser', + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, +} diff --git a/.gitignore b/.gitignore index e5a863d..265f50c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,46 +1,26 @@ -.vs -.vscode -.idea -.code -.vite +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* -vendor -.nfs* -composer.lock -*.phar +node_modules dist -.guard -outputs - -# sqlite database files -*.sqlite - -views-mappings.php -.env.PROD - -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js -package-lock.json +dist-ssr +*.local -# testing -/coverage - -# production -/build - -# misc +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea .DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? -.php-cs-fixer.cache \ No newline at end of file +package-lock.json \ No newline at end of file diff --git a/README.md b/README.md index 9a0df84..17f2ded 100644 --- a/README.md +++ b/README.md @@ -4,4 +4,3 @@ This repository hosts the IQBall application for web ## Read the docs ! You can find some additional documentation in the [Documentation](Documentation) folder, and in the [wiki](https://codefirst.iut.uca.fr/git/IQBall/Application-Web/wiki). - diff --git a/composer.json b/composer.json deleted file mode 100644 index 1d3a4d7..0000000 --- a/composer.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "autoload": { - "psr-4": { - "IQBall\\": "src/" - } - }, - "require": { - "altorouter/altorouter": "1.2.0", - "ext-json": "*", - "ext-pdo": "*", - "ext-pdo_sqlite": "*", - "twig/twig":"^2.0", - "phpstan/phpstan": "*" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.38" - } -} diff --git a/config.php b/config.php deleted file mode 100644 index a3871c6..0000000 --- a/config.php +++ /dev/null @@ -1,33 +0,0 @@ - - - , - ) -} diff --git a/front/style/theme/dark.css b/front/style/theme/dark.css deleted file mode 100644 index bdd4824..0000000 --- a/front/style/theme/dark.css +++ /dev/null @@ -1,9 +0,0 @@ -:root { - --main-color: #191a21; - --second-color: #282a36; - --third-color: #303341; - --accent-color: #ffa238; - --main-contrast-color: #e6edf3; - --font-title: Helvetica; - --font-content: Helvetica; -} diff --git a/index.html b/index.html new file mode 100644 index 0000000..73324da --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + React + TS + + +
+ + + diff --git a/package.json b/package.json index 966a0e9..9904fe4 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@loadable/component": "^5.16.3", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -10,9 +11,11 @@ "@types/node": "^16.18.59", "@types/react": "^18.2.31", "@types/react-dom": "^18.2.14", + "eslint-plugin-react-refresh": "^0.4.5", "react": "^18.2.0", "react-dom": "^18.2.0", "react-draggable": "^4.4.6", + "react-router-dom": "^6.22.0", "typescript": "^5.2.2", "vite": "^4.5.0", "vite-plugin-css-injected-by-js": "^3.3.0" @@ -25,14 +28,14 @@ "tsc": "tsc" }, "devDependencies": { - "@vitejs/plugin-react": "^4.1.0", - "prettier": "^3.1.0", - "typescript": "^5.2.2", - "vite-plugin-svgr": "^4.1.0", "@typescript-eslint/eslint-plugin": "^6.11.0", "@typescript-eslint/parser": "^6.11.0", + "@vitejs/plugin-react": "^4.1.0", "eslint": "^8.53.0", "eslint-plugin-react": "^7.33.2", - "eslint-plugin-react-hooks": "^4.6.0" + "eslint-plugin-react-hooks": "^4.6.0", + "prettier": "^3.1.0", + "typescript": "^5.2.2", + "vite-plugin-svgr": "^4.1.0" } } diff --git a/phpstan.neon b/phpstan.neon deleted file mode 100644 index 346baaa..0000000 --- a/phpstan.neon +++ /dev/null @@ -1,12 +0,0 @@ -parameters: - phpVersion: 70400 - level: 6 - paths: - - src - scanFiles: - - config.php - - sql/database.php - - profiles/dev-config-profile.php - - profiles/prod-config-profile.php - excludePaths: - - src/App/react-display-file.php diff --git a/profiles/dev-config-profile.php b/profiles/dev-config-profile.php deleted file mode 100644 index b8a50be..0000000 --- a/profiles/dev-config-profile.php +++ /dev/null @@ -1,43 +0,0 @@ -insertAccount($name, $email, AuthModel::generateToken(), password_hash("123456", PASSWORD_DEFAULT), "https://cdn.pixabay.com/photo/2015/10/05/22/37/blank-profile-picture-973460_960_720.png"); - $accounts->setIsAdmin($id, true); - } - - foreach ($defaultTeams as $name) { - $id = $teams->insert($name, "https://lebasketographe.fr/wp-content/uploads/2019/11/nom-equipes-nba.jpg", "#1a2b3c", "#FF00AA"); - } -} - -function _get_base_path(): string { - return ""; -} diff --git a/profiles/prod-config-profile.php b/profiles/prod-config-profile.php deleted file mode 100644 index 224f8de..0000000 --- a/profiles/prod-config-profile.php +++ /dev/null @@ -1,32 +0,0 @@ -setBasePath($basePath); - - $router->map("OPTIONS", "*", Action::noAuth(fn() => HttpResponse::fromCode(HttpCodes::OK))); - - $router->map("POST", "/auth", Action::noAuth(fn() => getAuthController()->authorize())); - $router->map("POST", "/tactic/[i:id]/edit/name", Action::auth(fn(int $id, Account $acc) => getTacticController()->updateName($id, $acc))); - $router->map("POST", "/tactic/[i:id]/save", Action::auth(fn(int $id, Account $acc) => getTacticController()->saveContent($id, $acc))); - $router->map("GET", "/admin/list-users", Action::noAuth(fn() => getAccountController()->listUsers($_GET))); - $router->map("GET", "/admin/user/[i:id]", Action::noAuth(fn(int $id) => getAccountController()->getUser($id))); - $router->map("GET", "/admin/user/[i:id]/space", Action::noAuth(fn(int $id) => getTacticController()->getUserTactics($id))); - $router->map("POST", "/admin/user/add", Action::noAuth(fn() => getAccountController()->addUser())); - $router->map("POST", "/admin/user/remove-all", Action::noAuth(fn() => getAccountController()->removeUsers())); - $router->map("POST", "/admin/user/[i:id]/update", Action::noAuth(fn(int $id) => getAccountController()->updateUser($id))); - $router->map("GET", "/admin/server-info", Action::noAuth(fn() => getServerController()->getServerInfo())); - $router->map("GET", "/admin/list-team", Action::noAuth(fn() => getAPITeamController()->listTeams($_GET))); - $router->map("POST", "/admin/add-team", Action::noAuth(fn() => getAPITeamController()->addTeam())); - $router->map("POST", "/admin/delete-teams", Action::noAuth(fn() => getAPITeamController()->deleteTeamSelected())); - $router->map("POST", "/admin/team/[i:id]/update", Action::noAuth(fn(int $id) => getAPITeamController()->updateTeam($id))); - - return $router; -} - -/** - * Defines the way of being authorised through the API - * By checking if an Authorisation header is set, and by expecting its value to be a valid token of an account. - * If the header is not set, fallback to the App's PHP session system, and try to extract the account from it. - * @return Account|null - * @throws Exception - */ -function tryGetAuthorization(): ?Account { - $headers = getallheaders(); - - // If no authorization header is set, try fallback to php session. - if (!isset($headers['Authorization'])) { - $session = PhpSessionHandle::init(); - return $session->getAccount(); - } - $token = $headers['Authorization']; - $gateway = new AccountGateway(new Connection(get_database())); - return $gateway->getAccountFromToken($token); -} - -Api::consume(API::handleMatch(getRoutes()->match(), fn() => tryGetAuthorization())); diff --git a/public/assets b/public/assets deleted file mode 120000 index 7b299d9..0000000 --- a/public/assets +++ /dev/null @@ -1 +0,0 @@ -../front/assets \ No newline at end of file diff --git a/public/front b/public/front deleted file mode 120000 index c1394c9..0000000 --- a/public/front +++ /dev/null @@ -1 +0,0 @@ -../front \ No newline at end of file diff --git a/public/index.php b/public/index.php deleted file mode 100644 index 5d93c4c..0000000 --- a/public/index.php +++ /dev/null @@ -1,129 +0,0 @@ -addFunction(new TwigFunction('path', fn(string $str) => "$basePath$str")); - - return $twig; -} - -function getRoutes(): AltoRouter { - global $basePath; - - $ar = new AltoRouter(); - $ar->setBasePath($basePath); - - //authentication - $ar->map("GET", "/login", Action::noAuth(fn() => getAuthController()->displayLogin())); - $ar->map("GET", "/register", Action::noAuth(fn() => getAuthController()->displayRegister())); - $ar->map("POST", "/login", Action::noAuth(fn(SessionHandle $s) => getAuthController()->login($_POST, $s))); - $ar->map("POST", "/register", Action::noAuth(fn(SessionHandle $s) => getAuthController()->register($_POST, $s))); - - //user-related - $ar->map("GET", "/", Action::auth(fn(SessionHandle $s) => getUserController()->home($s))); - $ar->map("GET", "/home", Action::auth(fn(SessionHandle $s) => getUserController()->home($s))); - $ar->map("GET", "/settings", Action::auth(fn(SessionHandle $s) => getUserController()->settings($s))); - $ar->map("GET", "/disconnect", Action::auth(fn(MutableSessionHandle $s) => getUserController()->disconnect($s))); - - - //tactic-related - $ar->map("GET", "/tactic/[i:id]/view", Action::auth(fn(int $id, SessionHandle $s) => getVisualizerController()->openVisualizer($id, $s))); - $ar->map("GET", "/tactic/[i:id]/edit", Action::auth(fn(int $id, SessionHandle $s) => getEditorController()->openEditor($id, $s))); - // don't require an authentication to run this action. - // If the user is not connected, the tactic will never save. - $ar->map("GET", "/tactic/new", Action::noAuth(fn() => getEditorController()->createNew())); - $ar->map("GET", "/tactic/new/plain", Action::noAuth(fn(SessionHandle $s) => getEditorController()->createNewOfKind(CourtType::plain(), $s))); - $ar->map("GET", "/tactic/new/half", Action::noAuth(fn(SessionHandle $s) => getEditorController()->createNewOfKind(CourtType::half(), $s))); - - //team-related - $ar->map("GET", "/team/new", Action::auth(fn(SessionHandle $s) => getTeamController()->displayCreateTeam($s))); - $ar->map("POST", "/team/new", Action::auth(fn(SessionHandle $s) => getTeamController()->submitTeam($_POST, $s))); - $ar->map("GET", "/team/search", Action::auth(fn(SessionHandle $s) => getTeamController()->displayListTeamByName($s))); - $ar->map("POST", "/team/search", Action::auth(fn(SessionHandle $s) => getTeamController()->listTeamByName($_POST, $s))); - $ar->map("GET", "/team/[i:id]", Action::auth(fn(int $id, SessionHandle $s) => getTeamController()->displayTeam($id, $s))); - $ar->map("GET", "/team/[i:id]/delete", Action::auth(fn(int $id, SessionHandle $s) => getTeamController()->deleteTeamById($id, $s))); - $ar->map("GET", "/team/[i:id]/addMember", Action::auth(fn(int $id, SessionHandle $s) => getTeamController()->displayAddMember($id, $s))); - $ar->map("POST", "/team/[i:id]/addMember", Action::auth(fn(int $id, SessionHandle $s) => getTeamController()->addMember($id, $_POST, $s))); - $ar->map("GET", "/team/[i:idTeam]/remove/[i:idMember]", Action::auth(fn(int $idTeam, int $idMember, SessionHandle $s) => getTeamController()->deleteMember($idTeam, $idMember, $s))); - $ar->map("GET", "/team/[i:id]/edit", Action::auth(fn(int $idTeam, SessionHandle $s) => getTeamController()->displayEditTeam($idTeam, $s))); - $ar->map("POST", "/team/[i:id]/edit", Action::auth(fn(int $idTeam, SessionHandle $s) => getTeamController()->editTeam($idTeam, $_POST, $s))); - - - return $ar; -} - -function runMatch($match, MutableSessionHandle $session): HttpResponse { - global $basePath; - if (!$match) { - return ViewHttpResponse::twig("error.html.twig", [ - 'failures' => [ValidationFail::notFound("Could not find page {$_SERVER['REQUEST_URI']}.")], - ], HttpCodes::NOT_FOUND); - } - - return App::runAction($basePath . '/login', $match['target'], $match['params'], $session); -} - -//this is a global variable -$basePath = get_base_path(); - -App::render(runMatch(getRoutes()->match(), PhpSessionHandle::init()), fn() => getTwig()); diff --git a/sql/database.php b/sql/database.php deleted file mode 100644 index 7dcd5fc..0000000 --- a/sql/database.php +++ /dev/null @@ -1,32 +0,0 @@ -query("SELECT COUNT(*) FROM sqlite_master WHERE type = 'table'")->fetchColumn() > 0; - - if ($database_exists) { - return $pdo; - } - - foreach (scandir(__DIR__) as $file) { - if (preg_match("/.*\.sql$/i", $file)) { - $content = file_get_contents(__DIR__ . "/" . $file); - - $pdo->exec($content); - } - } - - init_database($pdo); - - return $pdo; -} diff --git a/sql/setup-tables.sql b/sql/setup-tables.sql deleted file mode 100644 index 2971de9..0000000 --- a/sql/setup-tables.sql +++ /dev/null @@ -1,50 +0,0 @@ --- drop tables here -DROP TABLE IF EXISTS Account; -DROP TABLE IF EXISTS Tactic; -DROP TABLE IF EXISTS Team; -DROP TABLE IF EXISTS User; -DROP TABLE IF EXISTS Member; - -CREATE TABLE Admins -( - id integer PRIMARY KEY REFERENCES Account -); - -CREATE TABLE Account -( - id integer PRIMARY KEY AUTOINCREMENT, - email varchar UNIQUE NOT NULL, - username varchar NOT NULL, - token varchar UNIQUE NOT NULL, - hash varchar NOT NULL, - profile_picture varchar NOT NULL -); - -CREATE TABLE Tactic -( - id integer PRIMARY KEY AUTOINCREMENT, - name varchar NOT NULL, - creation_date timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL, - owner integer NOT NULL, - content varchar DEFAULT '{"components": []}' NOT NULL, - court_type varchar CHECK ( court_type IN ('HALF', 'PLAIN')) NOT NULL, - FOREIGN KEY (owner) REFERENCES Account -); - -CREATE TABLE Team -( - id integer PRIMARY KEY AUTOINCREMENT NOT NULL, - name varchar NOT NULL, - picture varchar NOT NULL, - main_color varchar NOT NULL, - second_color varchar NOT NULL -); - -CREATE TABLE Member -( - id_team integer NOT NULL, - id_user integer NOT NULL, - role text CHECK (role IN ('COACH', 'PLAYER')) NOT NULL, - FOREIGN KEY (id_team) REFERENCES Team (id), - FOREIGN KEY (id_user) REFERENCES Account (id) -); diff --git a/src/Api/API.php b/src/Api/API.php deleted file mode 100644 index 6893eaa..0000000 --- a/src/Api/API.php +++ /dev/null @@ -1,65 +0,0 @@ -getCode()); - - header('Access-Control-Allow-Origin: *'); - header('Access-Control-Allow-Headers: *'); - - - foreach ($response->getHeaders() as $header => $value) { - header("$header: $value"); - } - - if ($response instanceof JsonHttpResponse) { - header('Content-type: application/json'); - echo $response->getJson(); - } elseif (get_class($response) != HttpResponse::class) { - throw new Exception("API returned unknown Http Response"); - } - } - - - /** - * @param array|false $match - * @param callable(): Account $tryGetAuthorization function to return account authorisation for the given action (if required) - * @return HttpResponse - * @throws Exception - */ - public static function handleMatch($match, callable $tryGetAuthorization): HttpResponse { - if (!$match) { - return new JsonHttpResponse([ValidationFail::notFound("not found")], HttpCodes::NOT_FOUND); - } - - $action = $match['target']; - if (!$action instanceof Action) { - throw new Exception("routed action is not an AppAction object."); - } - - $account = null; - - if ($action->getAuthType() != Action::NO_AUTH) { - $account = call_user_func($tryGetAuthorization); - if ($account == null) { - return new JsonHttpResponse([ValidationFail::unauthorized("Missing or invalid 'Authorization' header.")], HttpCodes::UNAUTHORIZED); - } - - if ($action->getAuthType() == Action::AUTH_ADMIN && !$account->getUser()->isAdmin()) { - return new JsonHttpResponse([ValidationFail::unauthorized()], HttpCodes::UNAUTHORIZED); - } - } - - return $action->run($match['params'], $account); - } -} diff --git a/src/Api/APIControl.php b/src/Api/APIControl.php deleted file mode 100644 index 751fbfb..0000000 --- a/src/Api/APIControl.php +++ /dev/null @@ -1,45 +0,0 @@ - $schema an array of `fieldName => DefaultValidators` which represents the request object schema - * @param callable(HttpRequest): HttpResponse $run the callback to run if the request is valid according to the given schema. - * The callback must accept an HttpRequest, and return an HttpResponse object. - * @return HttpResponse - */ - public static function runChecked(array $schema, callable $run): HttpResponse { - return Control::runChecked($schema, $run, self::errorFactory()); - } - - /** - * Runs given callback, if the given request data array validates the given schema. - * @param array $data the request's data array. - * @param array $schema an array of `fieldName => DefaultValidators` which represents the request object schema - * @param callable(HttpRequest): HttpResponse $run the callback to run if the request is valid according to the given schema. - * The callback must accept an HttpRequest, and return an HttpResponse object. - * @return HttpResponse - */ - public static function runCheckedFrom(array $data, array $schema, callable $run): HttpResponse { - return Control::runCheckedFrom($data, $schema, $run, self::errorFactory()); - } - -} diff --git a/src/Api/Controller/APIAccountsController.php b/src/Api/Controller/APIAccountsController.php deleted file mode 100644 index fd7e88e..0000000 --- a/src/Api/Controller/APIAccountsController.php +++ /dev/null @@ -1,109 +0,0 @@ -accounts = $accounts; - $this->authModel = $model; - - } - - - /** - * @param array $request - * @return HttpResponse - */ - public function listUsers(array $request): HttpResponse { - return APIControl::runCheckedFrom($request, [ - 'start' => [DefaultValidators::isUnsignedInteger()], - 'n' => [DefaultValidators::isIntInRange(0, 250)], - 'search' => [DefaultValidators::lenBetween(0, 256)], - ], function (HttpRequest $req) { - $accounts = $this->accounts->searchAccounts(intval($req['start']), intval($req['n']), $req["search"]); - $users = array_map(fn(Account $acc) => $acc->getUser(), $accounts); - return new JsonHttpResponse([ - "users" => $users, - "totalCount" => $this->accounts->totalCount(), - ]); - }); - } - - /** - * @param int $userId - * @return HttpResponse given user information. - */ - public function getUser(int $userId): HttpResponse { - $acc = $this->accounts->getAccount($userId); - - if ($acc == null) { - return new JsonHttpResponse([ValidationFail::notFound("User not found")], HttpCodes::NOT_FOUND); - } - - return new JsonHttpResponse($acc->getUser()); - } - - public function addUser(): HttpResponse { - return APIControl::runChecked([ - "username" => [DefaultValidators::name()], - "email" => [DefaultValidators::email()], - "password" => [DefaultValidators::password()], - "isAdmin" => [DefaultValidators::bool()], - ], function (HttpRequest $req) { - $model = new AuthModel($this->accounts); - - $account = $model->register($req["username"], $req["password"], $req["email"]); - if ($account == null) { - return new JsonHttpResponse([new ValidationFail("already exists", "An account with provided email ")], HttpCodes::FORBIDDEN); - } - - return new JsonHttpResponse([ - "id" => $account->getUser()->getId(), - ]); - }); - } - - public function removeUsers(): HttpResponse { - return APIControl::runChecked([ - "identifiers" => [DefaultValidators::array(), DefaultValidators::forall(DefaultValidators::isUnsignedInteger())], - ], function (HttpRequest $req) { - $this->accounts->removeAccounts($req["identifiers"]); - return HttpResponse::fromCode(HttpCodes::OK); - }); - } - - public function updateUser(int $id): HttpResponse { - return APIControl::runChecked([ - "email" => [DefaultValidators::email()], - "username" => [DefaultValidators::name()], - "isAdmin" => [DefaultValidators::bool()], - ], function (HttpRequest $req) use ($id) { - $mailAccount = $this->accounts->getAccountFromMail($req["email"]); - - if ($mailAccount != null && $mailAccount->getUser()->getId() != $id) { - return new JsonHttpResponse([new ValidationFail("email exists", "The provided mail address already exists for another account.")], HttpCodes::FORBIDDEN); - } - - $this->authModel->update($id, $req["email"], $req["username"], $req["isAdmin"]); - return HttpResponse::fromCode(HttpCodes::OK); - }); - } -} diff --git a/src/Api/Controller/APIAuthController.php b/src/Api/Controller/APIAuthController.php deleted file mode 100644 index c715803..0000000 --- a/src/Api/Controller/APIAuthController.php +++ /dev/null @@ -1,44 +0,0 @@ -model = $model; - } - - - /** - * From given email address and password, authenticate the user and respond with its authorization token. - * @return HttpResponse - */ - public function authorize(): HttpResponse { - return APIControl::runChecked([ - "email" => [DefaultValidators::email(), DefaultValidators::lenBetween(5, 256)], - "password" => [DefaultValidators::password()], - ], function (HttpRequest $req) { - $failures = []; - $account = $this->model->login($req["email"], $req["password"], $failures); - - if (!empty($failures)) { - return new JsonHttpResponse($failures, HttpCodes::UNAUTHORIZED); - } - - return new JsonHttpResponse(["authorization" => $account->getToken()]); - }); - } -} diff --git a/src/Api/Controller/APIServerController.php b/src/Api/Controller/APIServerController.php deleted file mode 100644 index 1c82d3e..0000000 --- a/src/Api/Controller/APIServerController.php +++ /dev/null @@ -1,45 +0,0 @@ -basePath = $basePath; - $this->pdo = $pdo; - } - - private function countLines(string $table): int { - $stmnt = $this->pdo->prepare("SELECT count(*) FROM $table"); - $stmnt->execute(); - $res = $stmnt->fetch(\PDO::FETCH_BOTH); - return $res[0]; - } - - - /** - * @return HttpResponse some (useless) information about the server - */ - public function getServerInfo(): HttpResponse { - - return new JsonHttpResponse([ - 'base_path' => $this->basePath, - 'date' => (int) gettimeofday(true) * 1000, - 'database' => [ - 'accounts' => $this->countLines("Account") . " line(s)", - 'tactics' => $this->countLines("Tactic") . " line(s)", - 'teams' => $this->countLines("Team") . " line(s)", - ], - ]); - } - -} diff --git a/src/Api/Controller/APITacticController.php b/src/Api/Controller/APITacticController.php deleted file mode 100644 index eb00ff5..0000000 --- a/src/Api/Controller/APITacticController.php +++ /dev/null @@ -1,87 +0,0 @@ -model = $model; - } - - /** - * update name of tactic, specified by tactic identifier, given in url. - * @param int $tactic_id - * @param Account $account - * @return HttpResponse - */ - public function updateName(int $tactic_id, Account $account): HttpResponse { - return APIControl::runChecked([ - "name" => [DefaultValidators::lenBetween(1, 50), DefaultValidators::nameWithSpaces()], - ], function (HttpRequest $request) use ($tactic_id, $account) { - - $failures = $this->model->updateName($tactic_id, $request["name"], $account->getUser()->getId()); - - if (!empty($failures)) { - //TODO find a system to handle Unauthorized error codes more easily from failures. - return new JsonHttpResponse($failures, HttpCodes::BAD_REQUEST); - } - - return HttpResponse::fromCode(HttpCodes::OK); - }); - } - - /** - * @param int $id - * @param Account $account - * @return HttpResponse - */ - public function saveContent(int $id, Account $account): HttpResponse { - return APIControl::runChecked([ - "content" => [], - ], function (HttpRequest $req) use ($id) { - //TODO verify that the account has the rights to update the tactic content - if ($fail = $this->model->updateContent($id, json_encode($req["content"]))) { - return new JsonHttpResponse([$fail], HttpCodes::BAD_REQUEST); - } - return HttpResponse::fromCode(HttpCodes::OK); - }); - } - - - /** - * @param int $userId - * @return HttpResponse given user information. - */ - public function getUserTactics(int $userId): HttpResponse { - $tactics = $this->model->listAllOf($userId); - - $response = array_map(fn(TacticInfo $t) => [ - 'id' => $t->getId(), - 'name' => $t->getName(), - 'court' => $t->getCourtType(), - 'creation_date' => $t->getCreationDate(), - - ], $tactics); - - return new JsonHttpResponse($response); - } - - -} diff --git a/src/Api/Controller/APITeamController.php b/src/Api/Controller/APITeamController.php deleted file mode 100644 index 270468d..0000000 --- a/src/Api/Controller/APITeamController.php +++ /dev/null @@ -1,79 +0,0 @@ -teamModel = $teamModel; - } - - /** - * @param array $req_params - * @return HttpResponse - */ - public function listTeams(array $req_params): HttpResponse { - return APIControl::runCheckedFrom($req_params, [ - 'start' => [DefaultValidators::isUnsignedInteger()], - 'n' => [DefaultValidators::isUnsignedInteger()], - ], function (HttpRequest $req) { - $teams = $this->teamModel->listAll(intval($req['start']), intval($req['n'])); - return new JsonHttpResponse([ - "totalCount" => $this->teamModel->countTeam(), - "teams" => $teams, - ]); - }); - } - - public function addTeam(): HttpResponse { - return APIControl::runChecked([ - 'name' => [DefaultValidators::name()], - 'picture' => [DefaultValidators::isURL()], - 'mainColor' => [DefaultValidators::hexColor()], - 'secondaryColor' => [DefaultValidators::hexColor()], - - ], function (HttpRequest $req) { - $this->teamModel->createTeam($req['name'], $req['picture'], $req['mainColor'], $req['secondaryColor']); - return HttpResponse::fromCode(HttpCodes::OK); - }); - } - - public function deleteTeamSelected(): HttpResponse { - return APIControl::runChecked([ - 'teams' => [], - ], function (HttpRequest $req) { - $this->teamModel->deleteTeamSelected($req['teams']); - return HttpResponse::fromCode(HttpCodes::OK); - }); - } - - public function updateTeam(int $id): HttpResponse { - return APIControl::runChecked([ - 'name' => [DefaultValidators::name()], - 'picture' => [DefaultValidators::isURL()], - 'mainColor' => [DefaultValidators::hexColor()], - 'secondaryColor' => [DefaultValidators::hexColor()], - ], function (HttpRequest $req) { - $this->teamModel->editTeam($req['id'], $req['name'], $req['picture'], $req['mainColor'], $req['secondaryColor']); - return HttpResponse::fromCode(HttpCodes::OK); - }); - } - - -} diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..843568d --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,37 @@ +import { + BrowserRouter, + createBrowserRouter, + createRoutesFromElements, + Route, + RouterProvider, + Routes +} from "react-router-dom"; + +import loadable from "@loadable/component"; + + +const HomePage = loadable(() => import("./pages/Home.tsx")) +const NotFoundPage = loadable(() => import("./pages/404.tsx")) +const CreateTeamPage = loadable(() => import("./pages/CreateTeamPage.tsx")) +const TeamPanelPage = loadable(() => import("./pages/TeamPanel.tsx")) +const NewTacticPage = loadable(() => import("./pages/NewTacticPage.tsx")) +const Editor = loadable(() => import("./pages/Editor.tsx")) + + +export default function App() { + return ( +
+ + + }/> + }/> + }/> + }/> + }/> + }/> + }/> + + +
+ ) +} \ No newline at end of file diff --git a/src/App/App.php b/src/App/App.php deleted file mode 100644 index 557ad13..0000000 --- a/src/App/App.php +++ /dev/null @@ -1,98 +0,0 @@ -getCode()); - - foreach ($response->getHeaders() as $header => $value) { - header("$header: $value"); - } - - if ($response instanceof ViewHttpResponse) { - self::renderView($response, $twigSupplier); - } elseif ($response instanceof JsonHttpResponse) { - header('Content-type: application/json'); - echo $response->getJson(); - } - } - - /** - * renders (prints out) given ViewHttpResponse to the client - * @param ViewHttpResponse $response - * @param callable(): Environment $twigSupplier - * @return void - * @throws LoaderError - * @throws RuntimeError - * @throws SyntaxError - */ - private static function renderView(ViewHttpResponse $response, callable $twigSupplier): void { - $file = $response->getFile(); - $args = $response->getArguments(); - - switch ($response->getViewKind()) { - case ViewHttpResponse::REACT_VIEW: - send_react_front($file, $args); - break; - case ViewHttpResponse::TWIG_VIEW: - try { - $twig = call_user_func($twigSupplier); - $twig->display($file, $args); - } catch (RuntimeError|SyntaxError|LoaderError $e) { - http_response_code(500); - echo "There was an error rendering your view, please refer to an administrator.\nlogs date: " . date("YYYD, d M Y H:i:s"); - throw $e; - } - break; - } - } - - /** - * run a user action, and return the generated response - * @param string $authRoute the route towards an authentication page to response with a redirection - * if the run action requires auth but session does not contain a logged-in account. - * @param Action $action - * @param mixed[] $params - * @param MutableSessionHandle $session - * @return HttpResponse - */ - public static function runAction(string $authRoute, Action $action, array $params, MutableSessionHandle $session): HttpResponse { - if ($action->getAuthType() != Action::NO_AUTH) { - $account = $session->getAccount(); - if ($account == null) { - // put in the session the initial url the user wanted to get - $session->setInitialTarget($_SERVER['REQUEST_URI']); - return HttpResponse::redirectAbsolute($authRoute); - } - - if ($action->getAuthType() == Action::AUTH_ADMIN && !$account->getUser()->isAdmin()) { - return new JsonHttpResponse([ValidationFail::unauthorized()], HttpCodes::UNAUTHORIZED); - } - - } - - return $action->run($params, $session); - } - -} diff --git a/src/App/AppControl.php b/src/App/AppControl.php deleted file mode 100644 index c313e69..0000000 --- a/src/App/AppControl.php +++ /dev/null @@ -1,44 +0,0 @@ - $failures], HttpCodes::BAD_REQUEST); - } - }; - } - - /** - * Runs given callback, if the request's payload json validates the given schema. - * @param array $schema an array of `fieldName => DefaultValidators` which represents the request object schema - * @param callable(HttpRequest): HttpResponse $run the callback to run if the request is valid according to the given schema. - * The callback must accept an HttpRequest, and return an HttpResponse object. - * @return HttpResponse - */ - public static function runChecked(array $schema, callable $run): HttpResponse { - return Control::runChecked($schema, $run, self::errorFactory()); - } - - /** - * Runs given callback, if the given request data array validates the given schema. - * @param array $data the request's data array. - * @param array $schema an array of `fieldName => DefaultValidators` which represents the request object schema - * @param callable(HttpRequest): HttpResponse $run the callback to run if the request is valid according to the given schema. - * The callback must accept an HttpRequest, and return an HttpResponse object. - * @return HttpResponse - */ - public static function runCheckedFrom(array $data, array $schema, callable $run): HttpResponse { - return Control::runCheckedFrom($data, $schema, $run, self::errorFactory()); - } - -} diff --git a/src/App/Controller/AuthController.php b/src/App/Controller/AuthController.php deleted file mode 100644 index 27b268a..0000000 --- a/src/App/Controller/AuthController.php +++ /dev/null @@ -1,99 +0,0 @@ -model = $model; - } - - public function displayRegister(): HttpResponse { - return ViewHttpResponse::twig("display_register.html.twig", []); - } - - /** - * registers given account - * @param mixed[] $requestData - * @param MutableSessionHandle $session - * @return HttpResponse - */ - public function register(array $requestData, MutableSessionHandle $session): HttpResponse { - $fails = []; - $request = HttpRequest::from($requestData, $fails, [ - "username" => [DefaultValidators::name(), DefaultValidators::lenBetween(2, 32)], - "password" => [DefaultValidators::password()], - "confirmpassword" => [DefaultValidators::password()], - "email" => [DefaultValidators::email(), DefaultValidators::lenBetween(5, 256)], - ]); - - if (!empty($fails)) { - return ViewHttpResponse::twig("display_register.html.twig", ['fails' => $fails, 'username' => $requestData['username'], 'email' => $requestData['email'], 'password' => $requestData['password'], 'confirmpassword' => $requestData['confirmpassword'], 'accept' => true]); - } - - if ($request["password"] != $request['confirmpassword']) { - $fails[] = new FieldValidationFail("confirmpassword", "Le mot de passe et la confirmation ne sont pas les mêmes."); - } - - $account = $this->model->register($request['username'], $request["password"], $request['email']); - - if (!$account) { - $fails[] = new FieldValidationFail("email", "L'email existe déjà"); - } - - if (!empty($fails)) { - return ViewHttpResponse::twig("display_register.html.twig", ['fails' => $fails, 'password' => $requestData['password'], 'confirmpassword' => $requestData['confirmpassword'], 'accept' => true]); - } - $session->setAccount($account); - - $target_url = $session->getInitialTarget(); - if ($target_url != null) { - return HttpResponse::redirectAbsolute($target_url); - } - - return HttpResponse::redirect("/home"); - } - - - public function displayLogin(): HttpResponse { - return ViewHttpResponse::twig("display_login.html.twig", []); - } - - /** - * logins given account credentials - * @param mixed[] $request - * @param MutableSessionHandle $session - * @return HttpResponse - */ - public function login(array $request, MutableSessionHandle $session): HttpResponse { - $fails = []; - $account = $this->model->login($request['email'], $request['password'], $fails); - if (!empty($fails)) { - return ViewHttpResponse::twig("display_login.html.twig", ['fails' => $fails, 'password' => $request['password'], 'email' => $request['email']]); - } - - $session->setAccount($account); - - $target_url = $session->getInitialTarget(); - $session->setInitialTarget(null); - if ($target_url != null) { - return HttpResponse::redirectAbsolute($target_url); - } - - return HttpResponse::redirect("/home"); - } - -} diff --git a/src/App/Controller/EditorController.php b/src/App/Controller/EditorController.php deleted file mode 100644 index ec21324..0000000 --- a/src/App/Controller/EditorController.php +++ /dev/null @@ -1,87 +0,0 @@ -model = $model; - } - - /** - * @param TacticInfo $tactic - * @return ViewHttpResponse the editor view for given tactic - */ - private function openEditorFor(TacticInfo $tactic): ViewHttpResponse { - return ViewHttpResponse::react("views/Editor.tsx", [ - "id" => $tactic->getId(), - "name" => $tactic->getName(), - "content" => $tactic->getContent(), - "courtType" => $tactic->getCourtType()->name(), - ]); - } - - public function createNew(): ViewHttpResponse { - return ViewHttpResponse::react("views/NewTacticPanel.tsx", []); - } - - /** - * @return ViewHttpResponse the editor view for a test tactic. - */ - private function openTestEditor(CourtType $courtType): ViewHttpResponse { - return ViewHttpResponse::react("views/Editor.tsx", [ - "id" => -1, //-1 id means that the editor will not support saves - "name" => TacticModel::TACTIC_DEFAULT_NAME, - "content" => '{"components": []}', - "courtType" => $courtType->name(), - ]); - } - - /** - * creates a new empty tactic, with default name - * If the given session does not contain a connected account, - * open a test editor. - * @param SessionHandle $session - * @param CourtType $type - * @return ViewHttpResponse the editor view - */ - public function createNewOfKind(CourtType $type, SessionHandle $session): ViewHttpResponse { - - $action = $session->getAccount(); - - if ($action == null) { - return $this->openTestEditor($type); - } - - $tactic = $this->model->makeNewDefault($session->getAccount()->getUser()->getId(), $type); - return $this->openEditorFor($tactic); - } - - /** - * returns an editor view for a given tactic - * @param int $id the targeted tactic identifier - * @param SessionHandle $session - * @return ViewHttpResponse - */ - public function openEditor(int $id, SessionHandle $session): ViewHttpResponse { - $tactic = $this->model->get($id); - - $failure = TacticValidator::validateAccess($id, $tactic, $session->getAccount()->getUser()->getId()); - - if ($failure != null) { - return ViewHttpResponse::twig('error.html.twig', ['failures' => [$failure]], HttpCodes::NOT_FOUND); - } - - return $this->openEditorFor($tactic); - } -} diff --git a/src/App/Controller/TeamController.php b/src/App/Controller/TeamController.php deleted file mode 100644 index 048d182..0000000 --- a/src/App/Controller/TeamController.php +++ /dev/null @@ -1,246 +0,0 @@ -model = $model; - } - - /** - * @param SessionHandle $session - * @return ViewHttpResponse the team creation panel - */ - public function displayCreateTeam(SessionHandle $session): ViewHttpResponse { - return ViewHttpResponse::twig("insert_team.html.twig", []); - } - - /** - * @param SessionHandle $session - * @return ViewHttpResponse the team panel to delete a member - */ - public function displayDeleteMember(SessionHandle $session): ViewHttpResponse { - return ViewHttpResponse::twig("delete_member.html.twig", []); - } - - /** - * create a new team from given request name, mainColor, secondColor and picture url - * @param array $request - * @param SessionHandle $session - * @return HttpResponse - */ - public function submitTeam(array $request, SessionHandle $session): HttpResponse { - $failures = []; - $request = HttpRequest::from($request, $failures, [ - "name" => [DefaultValidators::lenBetween(1, 32), DefaultValidators::nameWithSpaces()], - "main_color" => [DefaultValidators::hexColor()], - "second_color" => [DefaultValidators::hexColor()], - "picture" => [DefaultValidators::isURL()], - ]); - if (!empty($failures)) { - $badFields = []; - foreach ($failures as $e) { - if ($e instanceof FieldValidationFail) { - $badFields[] = $e->getFieldName(); - } - } - return ViewHttpResponse::twig('insert_team.html.twig', ['bad_fields' => $badFields]); - } - $teamId = $this->model->createTeam($request['name'], $request['picture'], $request['main_color'], $request['second_color']); - $this->model->addMember($session->getAccount()->getUser()->getEmail(), $teamId, 'COACH'); - return HttpResponse::redirect('/team/' . $teamId); - } - - /** - * @param SessionHandle $session - * @return ViewHttpResponse the panel to search a team by its name - */ - public function displayListTeamByName(SessionHandle $session): ViewHttpResponse { - return ViewHttpResponse::twig("list_team_by_name.html.twig", []); - } - - /** - * returns a view that contains all the teams description whose name matches the given name needle. - * @param array $request - * @param SessionHandle $session - * @return HttpResponse - */ - public function listTeamByName(array $request, SessionHandle $session): HttpResponse { - $errors = []; - $request = HttpRequest::from($request, $errors, [ - "name" => [DefaultValidators::lenBetween(1, 32), DefaultValidators::nameWithSpaces()], - ]); - - if (!empty($errors) && $errors[0] instanceof FieldValidationFail) { - $badField = $errors[0]->getFieldName(); - return ViewHttpResponse::twig('list_team_by_name.html.twig', ['bad_field' => $badField]); - } - - $teams = $this->model->listByName($request['name'], $session->getAccount()->getUser()->getId()); - - if (empty($teams)) { - return ViewHttpResponse::twig('display_teams.html.twig', []); - } - return ViewHttpResponse::twig('display_teams.html.twig', ['teams' => $teams]); - } - - /** - * Delete a team with its id - * @param int $id - * @param SessionHandle $session - * @return HttpResponse - */ - public function deleteTeamById(int $id, SessionHandle $session): HttpResponse { - $a = $session->getAccount(); - $ret = $this->model->deleteTeam($a->getUser()->getEmail(), $id); - if($ret != 0) { - return ViewHttpResponse::twig('display_team.html.twig', ['notDeleted' => true]); - } - return HttpResponse::redirect('/'); - } - - /** - * Display a team with its id - * @param int $id - * @param SessionHandle $session - * @return ViewHttpResponse a view that displays given team information - */ - public function displayTeam(int $id, SessionHandle $session): ViewHttpResponse { - $result = $this->model->getTeam($id, $session->getAccount()->getUser()->getId()); - if($result == null) { - return ViewHttpResponse::twig('error.html.twig', [ - 'failures' => [ValidationFail::unauthorized("Vous n'avez pas accès à cette équipe.")], - ], HttpCodes::FORBIDDEN); - } - $role = $this->model->isCoach($id, $session->getAccount()->getUser()->getEmail()); - - return ViewHttpResponse::react( - 'views/TeamPanel.tsx', - [ - 'team' => [ - "info" => $result->getInfo(), - "members" => $result->listMembers(), - ], - 'isCoach' => $role, - 'currentUserId' => $session->getAccount()->getUser()->getId()] - ); - } - - /** - * @param int $idTeam - * @param SessionHandle $session - * @return ViewHttpResponse the team panel to add a member - */ - public function displayAddMember(int $idTeam, SessionHandle $session): ViewHttpResponse { - return ViewHttpResponse::twig("add_member.html.twig", ['idTeam' => $idTeam]); - } - - /** - * add a member to a team - * @param int $idTeam - * @param array $request - * @param SessionHandle $session - * @return HttpResponse - */ - public function addMember(int $idTeam, array $request, SessionHandle $session): HttpResponse { - $errors = []; - if(!$this->model->isCoach($idTeam, $session->getAccount()->getUser()->getEmail())) { - return ViewHttpResponse::twig('error.html.twig', [ - 'failures' => [ValidationFail::unauthorized("Vous n'avez pas accès à cette action pour cette équipe.")], - ], HttpCodes::FORBIDDEN); - } - $request = HttpRequest::from($request, $errors, [ - "email" => [DefaultValidators::email(), DefaultValidators::lenBetween(5, 256)], - ]); - if(!empty($errors)) { - return ViewHttpResponse::twig('add_member.html.twig', ['badEmail' => true,'idTeam' => $idTeam]); - } - $ret = $this->model->addMember($request['email'], $idTeam, $request['role']); - - switch($ret) { - case -1: - return ViewHttpResponse::twig('add_member.html.twig', ['notFound' => true,'idTeam' => $idTeam]); - case -2: - return ViewHttpResponse::twig('add_member.html.twig', ['alreadyExisting' => true,'idTeam' => $idTeam]); - default: - return HttpResponse::redirect('/team/' . $idTeam); - } - } - - /** - * remove a member from a team with their ids - * @param int $idTeam - * @param int $idMember - * @param SessionHandle $session - * @return HttpResponse - */ - public function deleteMember(int $idTeam, int $idMember, SessionHandle $session): HttpResponse { - if(!$this->model->isCoach($idTeam, $session->getAccount()->getUser()->getEmail())) { - return ViewHttpResponse::twig('error.html.twig', [ - 'failures' => [ValidationFail::unauthorized("Vous n'avez pas accès à cette action pour cette équipe.")], - ], HttpCodes::FORBIDDEN); - } - $teamId = $this->model->deleteMember($idMember, $idTeam); - if($teamId == -1 || $session->getAccount()->getUser()->getId() == $idMember) { - return HttpResponse::redirect('/'); - } - return $this->displayTeam($teamId, $session); - } - - /** - * @param int $idTeam - * @param SessionHandle $session - * @return ViewHttpResponse - */ - public function displayEditTeam(int $idTeam, SessionHandle $session): ViewHttpResponse { - return ViewHttpResponse::twig("edit_team.html.twig", ['team' => $this->model->getTeam($idTeam, $session->getAccount()->getUser()->getId())]); - } - - /** - * @param int $idTeam - * @param array $request - * @param SessionHandle $session - * @return HttpResponse - */ - public function editTeam(int $idTeam, array $request, SessionHandle $session): HttpResponse { - if(!$this->model->isCoach($idTeam, $session->getAccount()->getUser()->getEmail())) { - return ViewHttpResponse::twig('error.html.twig', [ - 'failures' => [ValidationFail::unauthorized("Vous n'avez pas accès à cette action pour cette équipe.")], - ], HttpCodes::FORBIDDEN); - } - $failures = []; - $request = HttpRequest::from($request, $failures, [ - "name" => [DefaultValidators::lenBetween(1, 32), DefaultValidators::nameWithSpaces()], - "main_color" => [DefaultValidators::hexColor()], - "second_color" => [DefaultValidators::hexColor()], - "picture" => [DefaultValidators::isURL()], - ]); - if (!empty($failures)) { - $badFields = []; - foreach ($failures as $e) { - if ($e instanceof FieldValidationFail) { - $badFields[] = $e->getFieldName(); - } - } - return ViewHttpResponse::twig('edit_team.html.twig', ['bad_fields' => $badFields]); - } - $this->model->editTeam($idTeam, $request['name'], $request['picture'], $request['main_color'], $request['second_color']); - return HttpResponse::redirect('/team/' . $idTeam); - } -} diff --git a/src/App/Controller/UserController.php b/src/App/Controller/UserController.php deleted file mode 100644 index 616bf54..0000000 --- a/src/App/Controller/UserController.php +++ /dev/null @@ -1,66 +0,0 @@ -tactics = $tactics; - $this->teams = $teams; - } - - /** - * @param SessionHandle $session - * @return ViewHttpResponse the home page view - */ - public function home(SessionHandle $session): ViewHttpResponse { - $limitNbTactics = 5; - - $user = $session->getAccount()->getUser(); - - $lastTactics = $this->tactics->getLast($limitNbTactics, $user->getId()); - $allTactics = $this->tactics->getAll($user->getId()); - $name = $user->getName(); - - if ($this->teams != null) { - $teams = $this->teams->getAll($user->getId()); - } else { - $teams = []; - } - - return ViewHttpResponse::react("views/Home.tsx", [ - "lastTactics" => $lastTactics, - "allTactics" => $allTactics, - "teams" => $teams, - "username" => $name, - ]); - } - - /** - * @return ViewHttpResponse account settings page - */ - public function settings(SessionHandle $session): ViewHttpResponse { - return ViewHttpResponse::react("views/Settings.tsx", []); - } - - public function disconnect(MutableSessionHandle $session): HttpResponse { - $session->destroy(); - return HttpResponse::redirect("/"); - } - -} diff --git a/src/App/Controller/VisualizerController.php b/src/App/Controller/VisualizerController.php deleted file mode 100644 index 946f6d0..0000000 --- a/src/App/Controller/VisualizerController.php +++ /dev/null @@ -1,39 +0,0 @@ -tacticModel = $tacticModel; - } - - /** - * Opens a visualisation page for the tactic specified by its identifier in the url. - * @param int $id - * @param SessionHandle $session - * @return HttpResponse - */ - public function openVisualizer(int $id, SessionHandle $session): HttpResponse { - $tactic = $this->tacticModel->get($id); - - $failure = TacticValidator::validateAccess($id, $tactic, $session->getAccount()->getUser()->getId()); - - if ($failure != null) { - return ViewHttpResponse::twig('error.html.twig', ['failures' => [$failure]], HttpCodes::NOT_FOUND); - } - - return ViewHttpResponse::react("views/Visualizer.tsx", ["name" => $tactic->getName()]); - } -} diff --git a/src/App/Session/MutableSessionHandle.php b/src/App/Session/MutableSessionHandle.php deleted file mode 100644 index 14871b6..0000000 --- a/src/App/Session/MutableSessionHandle.php +++ /dev/null @@ -1,22 +0,0 @@ -getOwnerId() != $ownerId) { - return ValidationFail::unauthorized("Vous ne pouvez pas accéder à cette tactique."); - } - return null; - } - -} diff --git a/src/App/ViewHttpResponse.php b/src/App/ViewHttpResponse.php deleted file mode 100644 index dfbd1da..0000000 --- a/src/App/ViewHttpResponse.php +++ /dev/null @@ -1,75 +0,0 @@ - View arguments - */ - private array $arguments; - /** - * @var int Kind of view, see {@link self::TWIG_VIEW} and {@link self::REACT_VIEW} - */ - private int $kind; - - /** - * @param int $code - * @param int $kind - * @param string $file - * @param array $arguments - */ - private function __construct(int $kind, string $file, array $arguments, int $code = HttpCodes::OK) { - parent::__construct($code, []); - $this->kind = $kind; - $this->file = $file; - $this->arguments = $arguments; - } - - public function getViewKind(): int { - return $this->kind; - } - - public function getFile(): string { - return $this->file; - } - - /** - * @return array - */ - public function getArguments(): array { - return $this->arguments; - } - - /** - * Create a twig view response - * @param string $file - * @param array $arguments - * @param int $code - * @return ViewHttpResponse - */ - public static function twig(string $file, array $arguments, int $code = HttpCodes::OK): ViewHttpResponse { - return new ViewHttpResponse(self::TWIG_VIEW, $file, $arguments, $code); - } - - /** - * Create a react view response - * @param string $file - * @param array $arguments - * @param int $code - * @return ViewHttpResponse - */ - public static function react(string $file, array $arguments, int $code = HttpCodes::OK): ViewHttpResponse { - return new ViewHttpResponse(self::REACT_VIEW, $file, $arguments, $code); - } - -} diff --git a/src/App/Views/account_settings.twig b/src/App/Views/account_settings.twig deleted file mode 100644 index 04d7437..0000000 --- a/src/App/Views/account_settings.twig +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - Paramètres - - - - - - - -

Paramètres

- - \ No newline at end of file diff --git a/src/App/Views/add_member.html.twig b/src/App/Views/add_member.html.twig deleted file mode 100644 index cfee16d..0000000 --- a/src/App/Views/add_member.html.twig +++ /dev/null @@ -1,118 +0,0 @@ - - - - - Ajouter un membre - - - -
-

IQBall

-
- -
-

Ajouter un membre à votre équipe

-
-
- - - {% if badEmail %} -

Email invalide

- {% endif %} - {%if notFound %} -

Cette personne n'a pas été trouvé

- {% endif %} - {% if alreadyExisting %} -

Cette personne est déjà dans l'équipe

- {% endif %} - - -
- Rôle du membre dans l'équipe : -
- - -
-
- - -
-
- -
-
- -
-
-
- - - \ No newline at end of file diff --git a/src/App/Views/delete_member.html.twig b/src/App/Views/delete_member.html.twig deleted file mode 100644 index 3fa5ccd..0000000 --- a/src/App/Views/delete_member.html.twig +++ /dev/null @@ -1,73 +0,0 @@ - - - - - Ajouter un membre - - - - -
-

Supprimez un membre de votre équipe

-
-
- - - - -
-
- -
-
-
- - - \ No newline at end of file diff --git a/src/App/Views/display_auth_confirm.html.twig b/src/App/Views/display_auth_confirm.html.twig deleted file mode 100644 index 60c63b2..0000000 --- a/src/App/Views/display_auth_confirm.html.twig +++ /dev/null @@ -1,46 +0,0 @@ - - - - - Profil Utilisateur - - - - - - - - \ No newline at end of file diff --git a/src/App/Views/display_login.html.twig b/src/App/Views/display_login.html.twig deleted file mode 100644 index 6e2d063..0000000 --- a/src/App/Views/display_login.html.twig +++ /dev/null @@ -1,108 +0,0 @@ - - - - Connexion - - - - - -
-

Se connecter

-
-
- - {% for name in fails %} - - {% endfor %} - - - - - - Vous n'avez pas de compte ? -

-
- -
-
-
-
- - \ No newline at end of file diff --git a/src/App/Views/display_register.html.twig b/src/App/Views/display_register.html.twig deleted file mode 100644 index 1f5a9c9..0000000 --- a/src/App/Views/display_register.html.twig +++ /dev/null @@ -1,123 +0,0 @@ - - - - S'enregistrer - - - - - -
-

S'enregistrer

-
-
- - {% for name in fails %} - - {% endfor %} - - - - - - - - -

-
- Vous avez déjà un compte ? -
-
- -
-
-
- - - - \ No newline at end of file diff --git a/src/App/Views/display_results.html.twig b/src/App/Views/display_results.html.twig deleted file mode 100644 index a33546b..0000000 --- a/src/App/Views/display_results.html.twig +++ /dev/null @@ -1,18 +0,0 @@ - - - - Twig view - - - -

Hello world

- - -{% for v in results %} -

username: {{ v.name }}

-

description: {{ v.description }}

-{% endfor %} - - - - \ No newline at end of file diff --git a/src/App/Views/display_team.html.twig b/src/App/Views/display_team.html.twig deleted file mode 100644 index 8928e84..0000000 --- a/src/App/Views/display_team.html.twig +++ /dev/null @@ -1,109 +0,0 @@ - - - - - Twig view - - - -
-

IQBall

-
- -
- {% if notDeleted %} - -

Cette équipe ne peut être supprimée.

-
- {% endif %} -{% if team is defined %} -
-
-

{{ team.getInfo().getName() }}

- -
-
-

Couleur principale :

-
-
-

Couleur secondaire :

-
-
-
- {% if isCoach %} - - - {% endif %} - {% for m in team.listMembers() %} -
-

{{ m.getUserId() }}

- {% if m.getRole().isCoach() %} -

: Coach

- {% else %} -

: Joueur

- {% endif %} -
- {% endfor %} -
-{% else %} -
-

Cette équipe ne peut être affichée

-
-{% endif %} -
- - \ No newline at end of file diff --git a/src/App/Views/display_teams.html.twig b/src/App/Views/display_teams.html.twig deleted file mode 100644 index 3e3ab12..0000000 --- a/src/App/Views/display_teams.html.twig +++ /dev/null @@ -1,61 +0,0 @@ - - - - - Twig view - - - -
-

IQBall

-
-
-{% if teams is empty %} -

Aucune équipe n'a été trouvée

-
-

Chercher une équipe

-
-
- - -
-
- -
-
-
-{% else %} - {% for t in teams %} -
-

Nom de l'équipe : {{ t.getName() }}

- logo de l'équipe -
- {% endfor %} -{% endif %} -
- - \ No newline at end of file diff --git a/src/App/Views/edit_team.html.twig b/src/App/Views/edit_team.html.twig deleted file mode 100644 index 409d71a..0000000 --- a/src/App/Views/edit_team.html.twig +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Insertion view - - - - -
-

Modifier votre équipe

-
-
- - - - - - - - -
-
- -
-
-
- - - \ No newline at end of file diff --git a/src/App/Views/error.html.twig b/src/App/Views/error.html.twig deleted file mode 100644 index bf90319..0000000 --- a/src/App/Views/error.html.twig +++ /dev/null @@ -1,57 +0,0 @@ - - - - - Error - - - - -

IQBall

- -{% for fail in failures %} -

{{ fail.getKind() }} : {{ fail.getMessage() }}

-{% endfor %} - - - - - - \ No newline at end of file diff --git a/src/App/Views/home.twig b/src/App/Views/home.twig deleted file mode 100644 index 2438ca1..0000000 --- a/src/App/Views/home.twig +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - Page d'accueil - - - - - - -
-

IQ CourtObjects

-
- Account logo -

Mon profil -

-

-
- -

Mes équipes

- - - -{% if recentTeam != null %} - {% for team in recentTeam %} -
-

{{ team.name }}

-
- {% endfor %} -{% else %} -

Aucune équipe créée !

-{% endif %} - -

Mes strategies

- - - -{% if recentTactic != null %} - {% for tactic in recentTactic %} -
-

{{ tactic.id }} - {{ tactic.name }} - {{ tactic.creation_date }}

- -
- {% endfor %} -{% else %} -

Aucune tactique créée !

-{% endif %} - - - \ No newline at end of file diff --git a/src/App/Views/insert_team.html.twig b/src/App/Views/insert_team.html.twig deleted file mode 100644 index 0c10114..0000000 --- a/src/App/Views/insert_team.html.twig +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Insertion view - - - - -
-

Créer une équipe

-
-
- - - - - - - - -
-
- -
-
-
- - - \ No newline at end of file diff --git a/src/App/Views/list_team_by_name.html.twig b/src/App/Views/list_team_by_name.html.twig deleted file mode 100644 index 092a149..0000000 --- a/src/App/Views/list_team_by_name.html.twig +++ /dev/null @@ -1,79 +0,0 @@ - - - - - Insertion view - - - -
-

IQBall

-
-
-

Chercher une équipe

-
-
- - -
-
- -
-
-
- - - \ No newline at end of file diff --git a/src/App/react-display-file.php b/src/App/react-display-file.php deleted file mode 100755 index 2dfcd11..0000000 --- a/src/App/react-display-file.php +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - "> - - - - - Document - - - - - - - -
- - - - - - - - - \ No newline at end of file diff --git a/src/App/react-display.php b/src/App/react-display.php deleted file mode 100644 index 5baf41b..0000000 --- a/src/App/react-display.php +++ /dev/null @@ -1,13 +0,0 @@ - $arguments arguments to pass to the rendered react component - * The arguments must be a json-encodable key/value dictionary. - * @return void - */ -function send_react_front(string $url, array $arguments) { - // the $url and $argument values are used into the included file - require_once "react-display-file.php"; -} diff --git a/front/Constants.ts b/src/Constants.ts similarity index 100% rename from front/Constants.ts rename to src/Constants.ts diff --git a/src/Core/Action.php b/src/Core/Action.php deleted file mode 100644 index df40ea9..0000000 --- a/src/Core/Action.php +++ /dev/null @@ -1,70 +0,0 @@ -action = $action; - $this->authType = $authType; - } - - public function getAuthType(): int { - return $this->authType; - } - - /** - * Runs an action - * @param mixed[] $params - * @param S $session - * @return HttpResponse - */ - public function run(array $params, $session): HttpResponse { - $params = array_values($params); - $params[] = $session; - return call_user_func_array($this->action, $params); - } - - /** - * @param callable(mixed[], S): HttpResponse $action - * @return Action an action that does not require to have an authorization. - */ - public static function noAuth(callable $action): Action { - return new Action($action, self::NO_AUTH); - } - - /** - * @param callable(mixed[], S): HttpResponse $action - * @return Action an action that does require to have an authorization. - */ - public static function auth(callable $action): Action { - return new Action($action, self::AUTH_USER); - } - - /** - * @param callable(mixed[], S): HttpResponse $action - * @return Action an action that does require to have an authorization, and to be an administrator. - */ - public static function admin(callable $action): Action { - return new Action($action, self::AUTH_ADMIN); - } -} diff --git a/src/Core/Connection.php b/src/Core/Connection.php deleted file mode 100644 index 1dd559d..0000000 --- a/src/Core/Connection.php +++ /dev/null @@ -1,61 +0,0 @@ -pdo = $pdo; - } - - public function lastInsertId(): string { - return $this->pdo->lastInsertId(); - } - - /** - * execute a request - * @param string $query - * @param array> $args - * @return void - */ - public function exec(string $query, array $args) { - $stmnt = $this->prep($query, $args); - $stmnt->execute(); - } - - /** - * Execute a request, and return the returned rows - * @param string $query the SQL request - * @param array> $args an array containing the arguments label, value and type: ex: `[":label" => [$value, PDO::PARAM_TYPE]` - * @return array[] the returned rows of the request - */ - public function fetch(string $query, array $args): array { - $stmnt = $this->prep($query, $args); - $stmnt->execute(); - return $stmnt->fetchAll(PDO::FETCH_ASSOC); - } - - /** - * @param string $query - * @param array> $args - * @return \PDOStatement - */ - private function prep(string $query, array $args): \PDOStatement { - $stmnt = $this->pdo->prepare($query); - foreach ($args as $name => $value) { - $stmnt->bindValue($name, $value[0], $value[1]); - } - return $stmnt; - } - - public function prepare(string $query): \PDOStatement { - return $this->pdo->prepare($query); - } - -} diff --git a/src/Core/Control.php b/src/Core/Control.php deleted file mode 100644 index 51d6622..0000000 --- a/src/Core/Control.php +++ /dev/null @@ -1,53 +0,0 @@ - $schema an array of `fieldName => DefaultValidators` which represents the request object schema - * @param callable(HttpRequest): HttpResponse $run the callback to run if the request is valid according to the given schema. - * The callback must accept an HttpRequest, and return an HttpResponse object. - * @param ControlSchemaErrorResponseFactory $errorFactory an error factory to use if the request does not validate the required schema - * @return HttpResponse - */ - public static function runChecked(array $schema, callable $run, ControlSchemaErrorResponseFactory $errorFactory): HttpResponse { - $request_body = file_get_contents('php://input'); - $payload_obj = json_decode($request_body); - if (!$payload_obj instanceof \stdClass) { - $fail = new ValidationFail("bad-payload", "request body is not a valid json object"); - return $errorFactory->apply([$fail]); - - } - $payload = get_object_vars($payload_obj); - return self::runCheckedFrom($payload, $schema, $run, $errorFactory); - } - - /** - * Runs given callback, if the given request data array validates the given schema. - * @param array $data the request's data array. - * @param array $schema an array of `fieldName => DefaultValidators` which represents the request object schema - * @param callable(HttpRequest): HttpResponse $run the callback to run if the request is valid according to the given schema. - * The callback must accept an HttpRequest, and return an HttpResponse object. - * @param ControlSchemaErrorResponseFactory $errorFactory an error factory to use if the request does not validate the required schema - * @return HttpResponse - */ - public static function runCheckedFrom(array $data, array $schema, callable $run, ControlSchemaErrorResponseFactory $errorFactory): HttpResponse { - $fails = []; - $request = HttpRequest::from($data, $fails, $schema); - - if (!empty($fails)) { - return $errorFactory->apply($fails); - - } - - return call_user_func_array($run, [$request]); - } -} diff --git a/src/Core/ControlSchemaErrorResponseFactory.php b/src/Core/ControlSchemaErrorResponseFactory.php deleted file mode 100644 index 9882a65..0000000 --- a/src/Core/ControlSchemaErrorResponseFactory.php +++ /dev/null @@ -1,14 +0,0 @@ -token = $token; - $this->user = $user; - } - - public function getToken(): string { - return $this->token; - } - - /** - * @return User - */ - public function getUser(): User { - return $this->user; - } - -} diff --git a/src/Core/Data/CourtType.php b/src/Core/Data/CourtType.php deleted file mode 100755 index caad45c..0000000 --- a/src/Core/Data/CourtType.php +++ /dev/null @@ -1,61 +0,0 @@ - self::COURT_HALF) { - throw new InvalidArgumentException("Valeur du rôle invalide"); - } - $this->value = $val; - } - - public static function plain(): CourtType { - return new CourtType(CourtType::COURT_PLAIN); - } - - public static function half(): CourtType { - return new CourtType(CourtType::COURT_HALF); - } - - public function name(): string { - switch ($this->value) { - case self::COURT_HALF: - return "HALF"; - case self::COURT_PLAIN: - return "PLAIN"; - } - die("unreachable"); - } - - public static function fromName(string $name): ?CourtType { - switch ($name) { - case "HALF": - return CourtType::half(); - case "PLAIN": - return CourtType::plain(); - default: - return null; - } - } - - public function isPlain(): bool { - return ($this->value == self::COURT_PLAIN); - } - - public function isHalf(): bool { - return ($this->value == self::COURT_HALF); - } - -} diff --git a/src/Core/Data/Member.php b/src/Core/Data/Member.php deleted file mode 100755 index 30e4202..0000000 --- a/src/Core/Data/Member.php +++ /dev/null @@ -1,57 +0,0 @@ -user = $user; - $this->teamId = $teamId; - $this->role = $role; - } - - /** - * @return string - */ - public function getRole(): string { - return $this->role; - } - - /** - * @return int - */ - public function getTeamId(): int { - return $this->teamId; - } - - /** - * @return User - */ - public function getUser(): User { - return $this->user; - } - - - public function jsonSerialize() { - return get_object_vars($this); - } -} diff --git a/src/Core/Data/TacticInfo.php b/src/Core/Data/TacticInfo.php deleted file mode 100644 index c3b8667..0000000 --- a/src/Core/Data/TacticInfo.php +++ /dev/null @@ -1,62 +0,0 @@ -id = $id; - $this->name = $name; - $this->ownerId = $ownerId; - $this->creationDate = $creationDate; - $this->courtType = $type; - $this->content = $content; - } - - /** - * @return string - */ - public function getContent(): string { - return $this->content; - } - - public function getId(): int { - return $this->id; - } - - public function getName(): string { - return $this->name; - } - - /** - * @return int - */ - public function getOwnerId(): int { - return $this->ownerId; - } - - public function getCourtType(): CourtType { - return $this->courtType; - } - - /** - * @return int - */ - public function getCreationDate(): int { - return $this->creationDate; - } -} diff --git a/src/Core/Data/Team.php b/src/Core/Data/Team.php deleted file mode 100755 index 7adeb49..0000000 --- a/src/Core/Data/Team.php +++ /dev/null @@ -1,38 +0,0 @@ -info = $info; - $this->members = $members; - } - - public function getInfo(): TeamInfo { - return $this->info; - } - - /** - * @return Member[] - */ - public function listMembers(): array { - return $this->members; - } - - public function jsonSerialize() { - return get_object_vars($this); - } - - -} diff --git a/src/Core/Data/TeamInfo.php b/src/Core/Data/TeamInfo.php deleted file mode 100644 index 964990c..0000000 --- a/src/Core/Data/TeamInfo.php +++ /dev/null @@ -1,50 +0,0 @@ -id = $id; - $this->name = $name; - $this->picture = $picture; - $this->mainColor = $mainColor; - $this->secondColor = $secondColor; - } - - public function getId(): int { - return $this->id; - } - - public function getName(): string { - return $this->name; - } - - public function getPicture(): string { - return $this->picture; - } - - public function getMainColor(): string { - return $this->mainColor; - } - - public function getSecondColor(): string { - return $this->secondColor; - } - - public function jsonSerialize() { - return get_object_vars($this); - } -} diff --git a/src/Core/Data/User.php b/src/Core/Data/User.php deleted file mode 100644 index 8471929..0000000 --- a/src/Core/Data/User.php +++ /dev/null @@ -1,84 +0,0 @@ -email = $email; - $this->name = $name; - $this->id = $id; - $this->profilePicture = $profilePicture; - $this->isAdmin = $isAdmin; - } - - /** - * @return bool - */ - public function isAdmin(): bool { - return $this->isAdmin; - } - - /** - * @return string - */ - public function getEmail(): string { - return $this->email; - } - - /** - * @return string - */ - public function getName(): string { - return $this->name; - } - - /** - * @return int - */ - public function getId(): int { - return $this->id; - } - - /** - * @return string - */ - public function getProfilePicture(): string { - return $this->profilePicture; - } - - public function jsonSerialize() { - return get_object_vars($this); - } -} diff --git a/src/Core/Gateway/AccountGateway.php b/src/Core/Gateway/AccountGateway.php deleted file mode 100644 index 1a0c689..0000000 --- a/src/Core/Gateway/AccountGateway.php +++ /dev/null @@ -1,180 +0,0 @@ -con = $con; - } - - public function insertAccount(string $name, string $email, string $token, string $hash, string $profilePicture): int { - $this->con->exec("INSERT INTO Account(username, hash, email, token,profile_picture) VALUES (:username,:hash,:email,:token,:profile_pic)", [ - ':username' => [$name, PDO::PARAM_STR], - ':hash' => [$hash, PDO::PARAM_STR], - ':email' => [$email, PDO::PARAM_STR], - ':token' => [$token, PDO::PARAM_STR], - ':profile_pic' => [$profilePicture, PDO::PARAM_STR], - ]); - return intval($this->con->lastInsertId()); - } - - public function updateAccount(int $id, string $name, string $email, string $token, bool $isAdmin): void { - $this->con->exec("UPDATE Account SET username = :username, email = :email, token = :token WHERE id = :id", [ - ':username' => [$name, PDO::PARAM_STR], - ':email' => [$email, PDO::PARAM_STR], - ':token' => [$token, PDO::PARAM_STR], - ':id' => [$id, PDO::PARAM_INT], - ]); - $this->setIsAdmin($id, $isAdmin); - } - - public function isAdmin(int $id): bool { - $stmnt = $this->con->prepare("SELECT * FROM Admins WHERE id = :id"); - $stmnt->bindValue(':id', $id, PDO::PARAM_INT); - $stmnt->execute(); - $result = $stmnt->fetchAll(PDO::FETCH_ASSOC); - - return !empty($result); - } - - /** - * promote or demote a user to server administrator - * @param int $id - * @param bool $isAdmin true to promote, false to demote - * @return bool true if the given user exists - */ - public function setIsAdmin(int $id, bool $isAdmin): bool { - if ($isAdmin) { - $stmnt = $this->con->prepare("INSERT INTO Admins VALUES(:id)"); - } else { - $stmnt = $this->con->prepare("DELETE FROM Admins WHERE id = :id"); - } - - $stmnt->bindValue(':id', $id); - $stmnt->execute(); - - return $stmnt->rowCount() > 0; - } - - /** - * @param string $email - * @return array|null - */ - private function getRowsFromMail(string $email): ?array { - return $this->con->fetch("SELECT * FROM Account WHERE email = :email", [':email' => [$email, PDO::PARAM_STR]])[0] ?? null; - } - - /** - * @param string $email - * @return string|null the hashed user's password, or null if the given mail does not exist - */ - public function getHash(string $email): ?string { - $results = $this->getRowsFromMail($email); - if ($results == null) { - return null; - } - return $results['hash']; - } - - /** - * @param string $email - * @return bool true if the given email exists in the database - */ - public function exists(string $email): bool { - return $this->getRowsFromMail($email) != null; - } - - /** - * @param string $email - * @return Account|null - */ - public function getAccountFromMail(string $email): ?Account { - $acc = $this->getRowsFromMail($email); - if (empty($acc)) { - return null; - } - - return new Account($acc["token"], new User($email, $acc["username"], $acc["id"], $acc["profile_picture"], $this->isAdmin($acc["id"]))); - } - - /** - * @param string $token get an account from given token - * @return Account|null - */ - public function getAccountFromToken(string $token): ?Account { - $stmnt = $this->con->prepare("SELECT * FROM Account WHERE token = :token"); - $stmnt->bindValue(':token', $token); - return $this->getAccountFrom($stmnt); - } - - /** - * @param int $id get an account from given identifier - * @return Account|null - */ - public function getAccount(int $id): ?Account { - $stmnt = $this->con->prepare("SELECT * FROM Account WHERE id = :id"); - $stmnt->bindValue(':id', $id); - return $this->getAccountFrom($stmnt); - } - - private function getAccountFrom(\PDOStatement $stmnt): ?Account { - $stmnt->execute(); - $acc = $stmnt->fetch(PDO::FETCH_ASSOC); - - if ($acc == null) { - return null; - } - - return new Account($acc["token"], new User($acc["email"], $acc["username"], $acc["id"], $acc["profile_picture"], $this->isAdmin($acc["id"]))); - } - - /** - * Return a list containing n accounts from a given starting index - * - * @param integer $n the number of accounts to retrieve - * @param int $start starting index of the list content - * @return Account[] - */ - public function searchAccounts(int $start, int $n, ?string $searchString): array { - $res = $this->con->fetch( - "SELECT * FROM Account WHERE username LIKE '%' || :search || '%' OR email LIKE '%' || :search || '%' ORDER BY username, email LIMIT :offset, :n", - [ - ":offset" => [$start, PDO::PARAM_INT], - ":n" => [$n, PDO::PARAM_INT], - ":search" => [$searchString ?? "", PDO::PARAM_STR], - ] - ); - return array_map(fn(array $acc) => new Account($acc["token"], new User($acc["email"], $acc["username"], $acc["id"], $acc["profile_picture"], $this->isAdmin($acc["id"]))), $res); - - } - - /** - * returns the total amount of accounts in the database - * @return int - */ - public function totalCount(): int { - return $this->con->fetch("SELECT count(*) FROM Account", [])[0]['count(*)']; - } - - /** - * remove a bunch of account identifiers - * @param int[] $accountIds - */ - public function removeAccounts(array $accountIds): void { - foreach ($accountIds as $accountId) { - $this->con->fetch("DELETE FROM Account WHERE id = :accountId", [ - ":accountId" => [$accountId, PDO::PARAM_INT], - ]); - } - } -} diff --git a/src/Core/Gateway/MemberGateway.php b/src/Core/Gateway/MemberGateway.php deleted file mode 100644 index f79ff60..0000000 --- a/src/Core/Gateway/MemberGateway.php +++ /dev/null @@ -1,101 +0,0 @@ -con = $con; - } - - /** - * insert member to a team - * @param int $idTeam - * @param int $userId - * @param string $role - * @return void - */ - public function insert(int $idTeam, int $userId, string $role): void { - $this->con->exec( - "INSERT INTO Member(id_team, id_user, role) VALUES (:id_team, :id_user, :role)", - [ - ":id_team" => [$idTeam, PDO::PARAM_INT], - ":id_user" => [$userId, PDO::PARAM_INT], - ":role" => [$role, PDO::PARAM_STR], - ] - ); - } - - /** - * @param int $teamId - * @return Member[] - */ - public function getMembersOfTeam(int $teamId): array { - $rows = $this->con->fetch( - "SELECT a.id,a.email,a.username,a.profile_picture,m.role FROM Account a,team t,Member m WHERE t.id = :id AND m.id_team = t.id AND m.id_user = a.id", - [ - ":id" => [$teamId, PDO::PARAM_INT], - ] - ); - return array_map(fn($row) => new Member(new User($row['email'], $row['username'], $row['id'], $row['profile_picture'], $row['is_admin']), $teamId, $row['role']), $rows); - - } - - /** - * remove member from given team - * @param int $idTeam - * @param int $idMember - * @return void - */ - public function remove(int $idTeam, int $idMember): void { - $this->con->exec( - "DELETE FROM Member WHERE id_team = :id_team AND id_user = :id_user", - [ - ":id_team" => [$idTeam, PDO::PARAM_INT], - ":id_user" => [$idMember, PDO::PARAM_INT], - ] - ); - } - - /** - * @param string $email - * @param int $idTeam - * @return bool - */ - public function isCoach(string $email, int $idTeam): bool { - $result = $this->con->fetch( - "SELECT role FROM Member WHERE id_team=:team AND id_user = (SELECT id FROM Account WHERE email=:email)", - [ - "team" => [$idTeam, PDO::PARAM_INT], - "email" => [$email, PDO::PARAM_STR], - ] - )[0]['role']; - - return $result == 'COACH'; - } - - /** - * @param int $idTeam - * @param int $idCurrentUser - * @return bool - */ - public function isMemberOfTeam(int $idTeam, int $idCurrentUser): bool { - $result = $this->con->fetch( - "SELECT id_user FROM Member WHERE id_team = :team AND id_user = :user", - [ - "team" => [$idTeam, PDO::PARAM_INT], - "user" => [$idCurrentUser, PDO::PARAM_INT], - ] - ); - return !empty($result); - } -} diff --git a/src/Core/Gateway/TacticInfoGateway.php b/src/Core/Gateway/TacticInfoGateway.php deleted file mode 100644 index d4b81e0..0000000 --- a/src/Core/Gateway/TacticInfoGateway.php +++ /dev/null @@ -1,151 +0,0 @@ -con = $con; - } - - /** - * get tactic information from given identifier - * @param int $id - * @return TacticInfo|null - */ - public function get(int $id): ?TacticInfo { - $res = $this->con->fetch( - "SELECT * FROM Tactic WHERE id = :id", - [":id" => [$id, PDO::PARAM_INT]] - ); - - if (!isset($res[0])) { - return null; - } - - $row = $res[0]; - - $type = CourtType::fromName($row['court_type']); - return new TacticInfo($id, $row["name"], strtotime($row["creation_date"]), $row["owner"], $type, $row['content']); - } - - - /** - * Return the nb last tactics created - * - * @param integer $nb - * @return array> - */ - public function getLast(int $nb, int $ownerId): ?array { - $res = $this->con->fetch( - "SELECT * - FROM Tactic - WHERE owner = :ownerId - ORDER BY creation_date DESC - LIMIT :nb", - [ - ":ownerId" => [$ownerId, PDO::PARAM_INT],":nb" => [$nb, PDO::PARAM_INT], - ] - ); - if (count($res) == 0) { - return []; - } - return $res; - } - - /** - * Get all the tactics of the owner - * - * @return array> - */ - public function getAll(int $ownerId): ?array { - $res = $this->con->fetch( - "SELECT * - FROM Tactic - WHERE owner = :ownerId - ORDER BY name DESC", - [ - ":ownerId" => [$ownerId, PDO::PARAM_INT], - ] - ); - if (count($res) == 0) { - return []; - } - return $res; - } - - - /** - * Return a list containing the nth last tactics of a given user id - * - * @param integer $user_id - * @return TacticInfo[] - */ - public function listAllOf(int $user_id): array { - $res = $this->con->fetch( - "SELECT * FROM Tactic WHERE owner = :owner_id ORDER BY creation_date DESC", - [ - ":owner_id" => [$user_id, PDO::PARAM_STR], - ] - ); - return array_map(fn(array $t) => new TacticInfo($t['id'], $t["name"], strtotime($t["creation_date"]), $t["owner"], CourtType::fromName($t['court_type']), $t['content']), $res); - } - - - /** - * @param string $name - * @param int $owner - * @param CourtType $type - * @return int inserted tactic id - */ - public function insert(string $name, int $owner, CourtType $type): int { - $this->con->exec( - "INSERT INTO Tactic(name, owner, court_type) VALUES(:name, :owner, :court_type)", - [ - ":name" => [$name, PDO::PARAM_STR], - ":owner" => [$owner, PDO::PARAM_INT], - ":court_type" => [$type->name(), PDO::PARAM_STR], - ] - ); - return intval($this->con->lastInsertId()); - } - - /** - * update name of given tactic identifier - * @param int $id - * @param string $name - * @return bool - */ - public function updateName(int $id, string $name): bool { - $stmnt = $this->con->prepare("UPDATE Tactic SET name = :name WHERE id = :id"); - $stmnt->execute([ - ":name" => $name, - ":id" => $id, - ]); - return $stmnt->rowCount() == 1; - } - - /*** - * Updates a given tactics content - * @param int $id - * @param string $json - * @return bool - */ - public function updateContent(int $id, string $json): bool { - $stmnt = $this->con->prepare("UPDATE Tactic SET content = :content WHERE id = :id"); - $stmnt->execute([ - ":content" => $json, - ":id" => $id, - ]); - return $stmnt->rowCount() == 1; - } -} diff --git a/src/Core/Gateway/TeamGateway.php b/src/Core/Gateway/TeamGateway.php deleted file mode 100644 index 4309a49..0000000 --- a/src/Core/Gateway/TeamGateway.php +++ /dev/null @@ -1,186 +0,0 @@ -con = $con; - } - - /** - * @param string $name - * @param string $picture - * @param string $mainColor - * @param string $secondColor - * @return int the inserted team identifier - */ - public function insert(string $name, string $picture, string $mainColor, string $secondColor): int { - $this->con->exec( - "INSERT INTO team(name, picture, main_color, second_color) VALUES (:team_name , :picture, :main_color, :second_color)", - [ - ":team_name" => [$name, PDO::PARAM_STR], - ":picture" => [$picture, PDO::PARAM_STR], - ":main_color" => [$mainColor, PDO::PARAM_STR], - ":second_color" => [$secondColor, PDO::PARAM_STR], - ] - ); - return intval($this->con->lastInsertId()); - } - - /** - * @param string $name - * @param int $id - * @return TeamInfo[] - */ - public function listByName(string $name, int $id): array { - $result = $this->con->fetch( - "SELECT t.* FROM team t, Member m WHERE t.name LIKE '%' || :name || '%' AND t.id=m.id_team AND m.id_user=:id", - [ - ":name" => [$name, PDO::PARAM_STR], - "id" => [$id, PDO::PARAM_INT], - ] - ); - - return array_map(fn($row) => new TeamInfo($row['id'], $row['name'], $row['picture'], $row['main_color'], $row['second_color']), $result); - } - - /** - * @param int $id - * @return TeamInfo|null - */ - public function getTeamById(int $id): ?TeamInfo { - $row = $this->con->fetch( - "SELECT * FROM team WHERE id = :id", - [ - ":id" => [$id, PDO::PARAM_INT], - ] - )[0] ?? null; - if ($row == null) { - return null; - } - return new TeamInfo($row['id'], $row['name'], $row['picture'], $row['main_color'], $row['second_color']); - } - - /** - * @param string $name - * @return int|null - */ - public function getTeamIdByName(string $name): ?int { - return $this->con->fetch( - "SELECT id FROM team WHERE name = :name", - [ - ":name" => [$name, PDO::PARAM_INT], - ] - )[0]['id'] ?? null; - } - - /** - * @param int $idTeam - */ - public function deleteTeam(int $idTeam): void { - $this->con->exec( - "DELETE FROM Member WHERE id_team=:team", - [ - "team" => [$idTeam, PDO::PARAM_INT], - ] - ); - $this->con->exec( - "DELETE FROM TEAM WHERE id=:team", - [ - "team" => [$idTeam, PDO::PARAM_INT], - ] - ); - } - - /** - * @param int $idTeam - * @param string $newName - * @param string $newPicture - * @param string $newMainColor - * @param string $newSecondColor - * @return void - */ - public function editTeam(int $idTeam, string $newName, string $newPicture, string $newMainColor, string $newSecondColor) { - $this->con->exec( - "UPDATE team - SET name = :newName, - picture = :newPicture, - main_color = :newMainColor, - second_color = :newSecondColor - WHERE id = :team", - [ - "team" => [$idTeam, PDO::PARAM_INT], - "newName" => [$newName, PDO::PARAM_STR], - "newPicture" => [$newPicture, PDO::PARAM_STR], - "newMainColor" => [$newMainColor, PDO::PARAM_STR], - "newSecondColor" => [$newSecondColor, PDO::PARAM_STR], - ] - ); - - } - - /** - * @param int $user - * @return array - */ - public function getAll(int $user): array { - return $this->con->fetch( - "SELECT t.* FROM team t,Member m WHERE m.id_team = t.id AND m.id_user= :idUser ", - [ - "idUser" => [$user, PDO::PARAM_INT], - ] - ); - } - - /** - * @param int $start - * @param int $n - * @return TeamInfo[] - */ - public function listAll(int $start, int $n): array { - $rows = $this->con->fetch( - "SELECT * FROM Team LIMIT :start, :n", - [ - ":start" => [$start, PDO::PARAM_INT], - ":n" => [$n, PDO::PARAM_INT], - ] - ); - return array_map(fn($row) => new TeamInfo($row['id'], $row['name'], $row['picture'], $row['main_color'], $row['second_color']), $rows); - } - - public function countTeam(): int { - $result = $this->con->fetch( - "SELECT count(*) as count FROM Team", - [] - ); - if (empty($result) || !isset($result[0]['count'])) { - return 0; - } - return $result[0]['count']; - } - - /** - * @param array $selectedTeams - * @return void - */ - public function deleteTeamSelected(array $selectedTeams): void { - foreach ($selectedTeams as $team) { - $this->con->exec( - "DELETE FROM TEAM WHERE id=:team", - [ - "team" => [$team, PDO::PARAM_INT], - ] - ); - } - } - - -} diff --git a/src/Core/Http/HttpCodes.php b/src/Core/Http/HttpCodes.php deleted file mode 100644 index 1903f0c..0000000 --- a/src/Core/Http/HttpCodes.php +++ /dev/null @@ -1,18 +0,0 @@ - - * */ -class HttpRequest implements ArrayAccess { - /** - * @var array - */ - private array $data; - - /** - * @param array $data - */ - private function __construct(array $data) { - $this->data = $data; - } - - /** - * Creates a new HttpRequest instance, and ensures that the given request data validates the given schema. - * This is a simple function that only supports flat schemas (non-composed, the data must only be a k/v array pair.) - * @param array $request the request's data - * @param array $fails a reference to a failure array, that will contain the reported validation failures. - * @param array $schema the schema to satisfy. a schema is a simple array with a string key (which is the top-level field name), and a set of validators - * @return HttpRequest|null the built HttpRequest instance, or null if a field is missing, or if any of the schema validator failed - */ - public static function from(array $request, array &$fails, array $schema): ?HttpRequest { - $failure = false; - foreach ($schema as $fieldName => $fieldValidators) { - if (!isset($request[$fieldName])) { - $fails[] = FieldValidationFail::missing($fieldName); - $failure = true; - continue; - } - $failure |= Validation::validate($request[$fieldName], $fieldName, $fails, ...$fieldValidators); - } - - if ($failure) { - return null; - } - return new HttpRequest($request); - } - - public function offsetExists($offset): bool { - return isset($this->data[$offset]); - } - - /** - * @param $offset - * @return mixed - */ - public function offsetGet($offset) { - return $this->data[$offset]; - } - - /** - * @param $offset - * @param $value - * @throws Exception - */ - public function offsetSet($offset, $value) { - throw new Exception("requests are immutable objects."); - } - - /** - * @param $offset - * @throws Exception - */ - public function offsetUnset($offset) { - throw new Exception("requests are immutable objects."); - } -} diff --git a/src/Core/Http/HttpResponse.php b/src/Core/Http/HttpResponse.php deleted file mode 100644 index 6c6a743..0000000 --- a/src/Core/Http/HttpResponse.php +++ /dev/null @@ -1,65 +0,0 @@ - - */ - private array $headers; - private int $code; - - /** - * @param int $code - * @param array $headers - */ - public function __construct(int $code, array $headers) { - $this->code = $code; - $this->headers = $headers; - } - - public function getCode(): int { - return $this->code; - } - - /** - * @return array - */ - public function getHeaders(): array { - return $this->headers; - } - - /** - * @param int $code - * @return HttpResponse - */ - public static function fromCode(int $code): HttpResponse { - return new HttpResponse($code, []); - } - - /** - * @param string $url the url to redirect - * @param int $code only HTTP 3XX codes are accepted. - * @return HttpResponse a response that will redirect client to given url - */ - - public static function redirect(string $url, int $code = HttpCodes::FOUND): HttpResponse { - global $basePath; - return self::redirectAbsolute($basePath . $url, $code); - } - - /** - * @param string $url the url to redirect - * @param int $code only HTTP 3XX codes are accepted. - * @return HttpResponse a response that will redirect client to given url - */ - - public static function redirectAbsolute(string $url, int $code = HttpCodes::FOUND): HttpResponse { - if ($code < 300 || $code >= 400) { - throw new \InvalidArgumentException("given code is not a redirection http code"); - } - return new HttpResponse($code, ["Location" => $url]); - } - - -} diff --git a/src/Core/Http/JsonHttpResponse.php b/src/Core/Http/JsonHttpResponse.php deleted file mode 100644 index bb897f7..0000000 --- a/src/Core/Http/JsonHttpResponse.php +++ /dev/null @@ -1,28 +0,0 @@ -payload = $payload; - } - - public function getJson(): string { - $result = json_encode($this->payload); - if (!$result) { - throw new \RuntimeException("Given payload is not json encodable"); - } - - return $result; - } - -} diff --git a/src/Core/Model/AuthModel.php b/src/Core/Model/AuthModel.php deleted file mode 100644 index e1fc1bb..0000000 --- a/src/Core/Model/AuthModel.php +++ /dev/null @@ -1,78 +0,0 @@ -gateway = $gateway; - } - - /** - * @param string $username - * @param string $password - * @param string $email - * @return Account|null the registered account or null if the account already exists for the given email address - */ - public function register( - string $username, - string $password, - string $email - ): ?Account { - if ($this->gateway->exists($email)) { - return null; - } - - $hash = password_hash($password, PASSWORD_DEFAULT); - $token = $this->generateToken(); - $accountId = $this->gateway->insertAccount($username, $email, $token, $hash, self::DEFAULT_PROFILE_PICTURE); - return new Account($token, new User($email, $username, $accountId, self::DEFAULT_PROFILE_PICTURE, false)); - } - - /** - * Generate a random base 64 string - * @return string - */ - public static function generateToken(): string { - try { - return base64_encode(random_bytes(64)); - } catch (Exception $e) { - throw new \RuntimeException($e); - } - } - - /** - * @param string $email - * @param string $password - * @param ValidationFail[] $failures - * @return Account|null the authenticated account or null if failures occurred - */ - public function login(string $email, string $password, array &$failures): ?Account { - $hash = $this->gateway->getHash($email); - if ($hash == null or (!password_verify($password, $hash))) { - $failures[] = new ValidationFail("email", "Adresse email ou mot de passe invalide"); - return null; - } - return $this->gateway->getAccountFromMail($email); - } - - public function update(int $id, string $email, string $username, bool $isAdmin): void { - $token = $this->generateToken(); - $this->gateway->updateAccount($id, $username, $email, $token, $isAdmin); - } - -} diff --git a/src/Core/Model/TacticModel.php b/src/Core/Model/TacticModel.php deleted file mode 100644 index 920075b..0000000 --- a/src/Core/Model/TacticModel.php +++ /dev/null @@ -1,117 +0,0 @@ -tactics = $tactics; - } - - /** - * creates a new empty tactic, with given name - * @param string $name - * @param int $ownerId - * @param CourtType $type - * @return TacticInfo - */ - public function makeNew(string $name, int $ownerId, CourtType $type): TacticInfo { - $id = $this->tactics->insert($name, $ownerId, $type); - return $this->tactics->get($id); - } - - /** - * creates a new empty tactic, with a default name - * @param int $ownerId - * @param CourtType $type - * @return TacticInfo|null - */ - public function makeNewDefault(int $ownerId, CourtType $type): ?TacticInfo { - return $this->makeNew(self::TACTIC_DEFAULT_NAME, $ownerId, $type); - } - - /** - * Tries to retrieve information about a tactic - * @param int $id tactic identifier - * @return TacticInfo|null or null if the identifier did not match a tactic - */ - public function get(int $id): ?TacticInfo { - return $this->tactics->get($id); - } - - /** - * Return the nb last tactics - * - * @param integer $nb - * @param integer $ownerId - * @return array> - */ - public function getLast(int $nb, int $ownerId): array { - return $this->tactics->getLast($nb, $ownerId); - } - - - /** - * Return a list containing all the tactics of a given user - * NOTE: if given user id does not match any user, this function returns an empty array - * - * @param integer $user_id - * @return TacticInfo[] - */ - public function listAllOf(int $user_id): array { - return$this->tactics->listAllOf($user_id); - } - - /** - * Get all the tactics of the owner - * - * @param integer $ownerId - * @return array> - */ - public function getAll(int $ownerId): ?array { - return $this->tactics->getAll($ownerId); - } - /** - * Update the name of a tactic - * @param int $id the tactic identifier - * @param string $name the new name to set - * @return ValidationFail[] failures, if any - */ - public function updateName(int $id, string $name, int $authId): array { - - $tactic = $this->tactics->get($id); - - if ($tactic == null) { - return [ValidationFail::notFound("Could not find tactic")]; - } - - if ($tactic->getOwnerId() != $authId) { - return [ValidationFail::unauthorized()]; - } - - if (!$this->tactics->updateName($id, $name)) { - return [ValidationFail::error("Could not update name")]; - } - return []; - } - - public function updateContent(int $id, string $json): ?ValidationFail { - if (!$this->tactics->updateContent($id, $json)) { - return ValidationFail::error("Could not update content"); - } - return null; - } - -} diff --git a/src/Core/Model/TeamModel.php b/src/Core/Model/TeamModel.php deleted file mode 100644 index b6b7bdd..0000000 --- a/src/Core/Model/TeamModel.php +++ /dev/null @@ -1,164 +0,0 @@ -teams = $gateway; - $this->members = $members; - $this->users = $users; - } - - /** - * Create a team - * @param string $name - * @param string $picture - * @param string $mainColor - * @param string $secondColor - * @return int - */ - public function createTeam(string $name, string $picture, string $mainColor, string $secondColor): int { - return $this->teams->insert($name, $picture, $mainColor, $secondColor); - } - - /** - * add a member to a team - * @param string $mail - * @param int $teamId - * @param string $role - * @return int - */ - public function addMember(string $mail, int $teamId, string $role): int { - $user = $this->users->getAccountFromMail($mail); - if ($user == null) { - return -1; - } - if (!$this->members->isMemberOfTeam($teamId, $user->getUser()->getId())) { - $this->members->insert($teamId, $user->getUser()->getId(), $role); - return 1; - } - return -2; - } - - /** - * @param string $name - * @param int $id - * @return TeamInfo[] - */ - public function listByName(string $name, int $id): array { - return $this->teams->listByName($name, $id); - } - - /** - * @param int $idTeam - * @param int $idCurrentUser - * @return Team|null - */ - public function getTeam(int $idTeam, int $idCurrentUser): ?Team { - if (!$this->members->isMemberOfTeam($idTeam, $idCurrentUser)) { - return null; - } - $teamInfo = $this->teams->getTeamById($idTeam); - $members = $this->members->getMembersOfTeam($idTeam); - return new Team($teamInfo, $members); - } - - /** - * delete a member from given team identifier - * @param int $idMember - * @param int $teamId - * @return int - */ - public function deleteMember(int $idMember, int $teamId): int { - $this->members->remove($teamId, $idMember); - if (empty($this->members->getMembersOfTeam($teamId))) { - $this->teams->deleteTeam($teamId); - return -1; - } - return $teamId; - } - - /** - * Delete a team - * @param string $email - * @param int $idTeam - * @return int - */ - public function deleteTeam(string $email, int $idTeam): int { - if ($this->members->isCoach($email, $idTeam)) { - $this->teams->deleteTeam($idTeam); - return 0; - } - return -1; - } - - /** - * Verify if the account associated to an email is in a specific team indicated with its id - * @param int $idTeam - * @param string $email - * @return bool - */ - public function isCoach(int $idTeam, string $email): bool { - return $this->members->isCoach($email, $idTeam); - } - - /** - * Edit a team with its id, and replace the current attributes with the new ones - * @param int $idTeam - * @param string $newName - * @param string $newPicture - * @param string $newMainColor - * @param string $newSecondColor - * @return void - */ - public function editTeam(int $idTeam, string $newName, string $newPicture, string $newMainColor, string $newSecondColor) { - $this->teams->editTeam($idTeam, $newName, $newPicture, $newMainColor, $newSecondColor); - } - - /** - * Get all user's teams - * - * @param integer $user - * @return array> - */ - public function getAll(int $user): array { - return $this->teams->getAll($user); - } - - /** - * @param int $start - * @param int $n - * @return TeamInfo[] - */ - public function listAll(int $start, int $n) { - return $this->teams->listAll($start, $n); - } - - public function countTeam(): int { - return $this->teams->countTeam(); - } - - /** - * @param array $selectedTeams - * @return void - */ - public function deleteTeamSelected(array $selectedTeams) { - $this->teams->deleteTeamSelected($selectedTeams); - } - -} diff --git a/src/Core/Validation/ComposedValidator.php b/src/Core/Validation/ComposedValidator.php deleted file mode 100644 index 58f4910..0000000 --- a/src/Core/Validation/ComposedValidator.php +++ /dev/null @@ -1,26 +0,0 @@ -first = $first; - $this->then = $then; - } - - public function validate(string $name, $val): array { - $firstFailures = $this->first->validate($name, $val); - $thenFailures = []; - if (empty($firstFailures)) { - $thenFailures = $this->then->validate($name, $val); - } - return array_merge($firstFailures, $thenFailures); - } -} diff --git a/src/Core/Validation/DefaultValidators.php b/src/Core/Validation/DefaultValidators.php deleted file mode 100644 index c898170..0000000 --- a/src/Core/Validation/DefaultValidators.php +++ /dev/null @@ -1,146 +0,0 @@ - preg_match($regex, $str), - fn(string $name) => [new FieldValidationFail($name, $msg == null ? "le champ ne valide pas le pattern $regex" : $msg)] - ); - } - - public static function hex(?string $msg = null): Validator { - return self::regex('/#([0-9a-fA-F])/', $msg == null ? "le champ n'est pas un nombre hexadecimal valide" : $msg); - } - - public static function hexColor(?string $msg = null): Validator { - return self::regex('/#([0-9a-fA-F]{6})/', $msg == null ? "le champ n'est pas une couleur valide" : $msg); - } - - /** - * @return Validator a validator that validates strings that only contains numbers, letters, accents letters, `-` and `_`. - */ - public static function name(?string $msg = null): Validator { - return self::regex("/^[0-9a-zA-Zà-üÀ-Ü_-]*$/", $msg); - } - - /** - * @return Validator a validator that validates strings that only contains numbers, letters, accents letters, `-`, `_` and spaces. - */ - public static function nameWithSpaces(): Validator { - return self::regex("/^[0-9a-zA-Zà-üÀ-Ü _-]*$/"); - } - - public static function password(): Validator { - return self::lenBetween(6, 256); - } - - /** - * Validate string if its length is between given range - * @param int $min minimum accepted length, inclusive - * @param int $max maximum accepted length, exclusive - * @return Validator - */ - public static function lenBetween(int $min, int $max): Validator { - return new FunctionValidator( - function (string $fieldName, string $str) use ($min, $max) { - $len = strlen($str); - if ($len >= $max) { - return [new FieldValidationFail($fieldName, "trop long, maximum $max caractères.")]; - } - if ($len < $min) { - return [new FieldValidationFail($fieldName, "trop court, minimum $min caractères.")]; - } - return []; - } - ); - } - - public static function email(?string $msg = null): Validator { - return new SimpleFunctionValidator( - fn(string $str) => filter_var($str, FILTER_VALIDATE_EMAIL), - fn(string $name) => [new FieldValidationFail($name, $msg == null ? "addresse mail invalide" : $msg)] - ); - } - - - public static function isInteger(): Validator { - return self::regex("/^[-+]?[0-9]+$/", "field is not an integer"); - } - - public static function isUnsignedInteger(): Validator { - return self::regex("/^[0-9]+$/", "field is not an unsigned integer"); - } - - public static function isIntInRange(int $min, int $max): Validator { - return new SimpleFunctionValidator( - fn(string $val) => intval($val) >= $min && intval($val) <= $max, - fn(string $name) => [new FieldValidationFail($name, "The value is not in the range $min to $max ")] - ); - } - - /** - * @param mixed[] $values - * @return Validator - */ - public static function oneOf(array $values): Validator { - return new SimpleFunctionValidator( - fn(string $val) => in_array($val, $values), - fn(string $name) => [new FieldValidationFail($name, "The value must be one of '" . join(", ", $values) . "'")] - ); - } - - public static function bool(): Validator { - return self::oneOf([true, false]); - } - - public static function isURL(): Validator { - return new SimpleFunctionValidator( - fn($val) => filter_var($val, FILTER_VALIDATE_URL), - fn(string $name) => [new FieldValidationFail($name, "The value is not an URL")] - ); - } - - /** - * @return Validator - */ - public static function array(): Validator { - return new SimpleFunctionValidator( - fn($val) => is_array($val), - fn(string $name) => [new FieldValidationFail($name, "The value is not an array")] - ); - } - - /** - * @param Validator $validator - * @return Validator - */ - public static function forall(Validator $validator): Validator { - return new class ($validator) extends Validator { - private Validator $validator; - - /** - * @param Validator $validator - */ - public function __construct(Validator $validator) { - $this->validator = $validator; - } - - public function validate(string $name, $val): array { - $failures = []; - foreach ($val as $idx => $item) { - $failures = array_merge($failures, $this->validator->validate($name . "[$idx]", $item)); - } - - return $failures; - } - }; - } -} diff --git a/src/Core/Validation/FieldValidationFail.php b/src/Core/Validation/FieldValidationFail.php deleted file mode 100644 index e3a127d..0000000 --- a/src/Core/Validation/FieldValidationFail.php +++ /dev/null @@ -1,42 +0,0 @@ -fieldName = $fieldName; - } - - public function getFieldName(): string { - return $this->fieldName; - } - - public static function invalidChars(string $fieldName): FieldValidationFail { - return new FieldValidationFail($fieldName, "field contains illegal chars"); - } - - public static function empty(string $fieldName): FieldValidationFail { - return new FieldValidationFail($fieldName, "field is empty"); - } - - public static function missing(string $fieldName): FieldValidationFail { - return new FieldValidationFail($fieldName, "field is missing"); - } - - /** - * @return array - */ - public function jsonSerialize(): array { - return ["field" => $this->fieldName, "message" => $this->getMessage()]; - } -} diff --git a/src/Core/Validation/FunctionValidator.php b/src/Core/Validation/FunctionValidator.php deleted file mode 100644 index 1bd18d7..0000000 --- a/src/Core/Validation/FunctionValidator.php +++ /dev/null @@ -1,21 +0,0 @@ -validate_fn = $validate_fn; - } - - public function validate(string $name, $val): array { - return call_user_func_array($this->validate_fn, [$name, $val]); - } -} diff --git a/src/Core/Validation/SimpleFunctionValidator.php b/src/Core/Validation/SimpleFunctionValidator.php deleted file mode 100644 index f19462b..0000000 --- a/src/Core/Validation/SimpleFunctionValidator.php +++ /dev/null @@ -1,33 +0,0 @@ - bool`, to validate the given string - * @param callable(string): ValidationFail[] $errorsFactory a factory function with signature `(string) => array` to emit failures when the predicate fails - */ - public function __construct(callable $predicate, callable $errorsFactory) { - $this->predicate = $predicate; - $this->errorFactory = $errorsFactory; - } - - public function validate(string $name, $val): array { - if (!call_user_func_array($this->predicate, [$val])) { - return call_user_func_array($this->errorFactory, [$name]); - } - return []; - } -} diff --git a/src/Core/Validation/Validation.php b/src/Core/Validation/Validation.php deleted file mode 100644 index 5b13354..0000000 --- a/src/Core/Validation/Validation.php +++ /dev/null @@ -1,29 +0,0 @@ -validate($valName, $val); - if ($error != null) { - $failures = array_merge($failures, $error); - $had_errors = true; - } - } - return $had_errors; - } - -} diff --git a/src/Core/Validation/ValidationFail.php b/src/Core/Validation/ValidationFail.php deleted file mode 100644 index 9a74a03..0000000 --- a/src/Core/Validation/ValidationFail.php +++ /dev/null @@ -1,56 +0,0 @@ -message = $message; - $this->kind = $kind; - } - - public function getMessage(): string { - return $this->message; - } - - public function getKind(): string { - return $this->kind; - } - - /** - * @return array - */ - public function jsonSerialize(): array { - return ["error" => $this->kind, "message" => $this->message]; - } - - /** - * @param string $message - * @return ValidationFail validation fail for unknown resource access - */ - public static function notFound(string $message): ValidationFail { - return new ValidationFail("Not found", $message); - } - - /** - * @param string $message - * @return ValidationFail validation fail for unauthorized accesses - */ - public static function unauthorized(string $message = "Unauthorized"): ValidationFail { - return new ValidationFail("Unauthorized", $message); - } - - public static function error(string $message): ValidationFail { - return new ValidationFail("Error", $message); - } - -} diff --git a/src/Core/Validation/Validator.php b/src/Core/Validation/Validator.php deleted file mode 100644 index d1761da..0000000 --- a/src/Core/Validation/Validator.php +++ /dev/null @@ -1,23 +0,0 @@ - \ No newline at end of file diff --git a/front/assets/icon/arrow.svg b/src/assets/icon/arrow.svg similarity index 100% rename from front/assets/icon/arrow.svg rename to src/assets/icon/arrow.svg diff --git a/front/assets/icon/ball.svg b/src/assets/icon/ball.svg similarity index 100% rename from front/assets/icon/ball.svg rename to src/assets/icon/ball.svg diff --git a/front/assets/icon/remove.svg b/src/assets/icon/remove.svg similarity index 100% rename from front/assets/icon/remove.svg rename to src/assets/icon/remove.svg diff --git a/front/assets/logo.svg b/src/assets/logo.svg similarity index 100% rename from front/assets/logo.svg rename to src/assets/logo.svg diff --git a/front/assets/logo192.png b/src/assets/logo192.png similarity index 100% rename from front/assets/logo192.png rename to src/assets/logo192.png diff --git a/front/assets/logo512.png b/src/assets/logo512.png similarity index 100% rename from front/assets/logo512.png rename to src/assets/logo512.png diff --git a/src/assets/react.svg b/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/vite.svg b/src/assets/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/src/assets/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/front/components/Rack.tsx b/src/components/Rack.tsx similarity index 100% rename from front/components/Rack.tsx rename to src/components/Rack.tsx diff --git a/front/components/TitleInput.tsx b/src/components/TitleInput.tsx similarity index 100% rename from front/components/TitleInput.tsx rename to src/components/TitleInput.tsx diff --git a/front/components/actions/ArrowAction.tsx b/src/components/actions/ArrowAction.tsx similarity index 100% rename from front/components/actions/ArrowAction.tsx rename to src/components/actions/ArrowAction.tsx diff --git a/front/components/actions/BallAction.tsx b/src/components/actions/BallAction.tsx similarity index 100% rename from front/components/actions/BallAction.tsx rename to src/components/actions/BallAction.tsx diff --git a/front/components/arrows/BendableArrow.tsx b/src/components/arrows/BendableArrow.tsx similarity index 100% rename from front/components/arrows/BendableArrow.tsx rename to src/components/arrows/BendableArrow.tsx diff --git a/front/components/editor/BallPiece.tsx b/src/components/editor/BallPiece.tsx similarity index 100% rename from front/components/editor/BallPiece.tsx rename to src/components/editor/BallPiece.tsx diff --git a/front/components/editor/BasketCourt.tsx b/src/components/editor/BasketCourt.tsx similarity index 88% rename from front/components/editor/BasketCourt.tsx rename to src/components/editor/BasketCourt.tsx index 69bad37..2213525 100644 --- a/front/components/editor/BasketCourt.tsx +++ b/src/components/editor/BasketCourt.tsx @@ -1,14 +1,7 @@ -import { - ReactElement, - ReactNode, - RefObject, - useEffect, - useLayoutEffect, - useState, -} from "react" +import { ReactElement, ReactNode, RefObject } from "react" import { Action } from "../../model/tactic/Action" -import { CourtAction } from "../../views/editor/CourtAction" +import { CourtAction } from "./CourtAction.tsx" import { ComponentId, TacticComponent } from "../../model/tactic/Tactic" export interface BasketCourtProps { diff --git a/front/views/editor/CourtAction.tsx b/src/components/editor/CourtAction.tsx similarity index 100% rename from front/views/editor/CourtAction.tsx rename to src/components/editor/CourtAction.tsx diff --git a/front/components/editor/CourtBall.tsx b/src/components/editor/CourtBall.tsx similarity index 100% rename from front/components/editor/CourtBall.tsx rename to src/components/editor/CourtBall.tsx diff --git a/front/components/editor/CourtPlayer.tsx b/src/components/editor/CourtPlayer.tsx similarity index 100% rename from front/components/editor/CourtPlayer.tsx rename to src/components/editor/CourtPlayer.tsx diff --git a/front/components/editor/PlayerPiece.tsx b/src/components/editor/PlayerPiece.tsx similarity index 100% rename from front/components/editor/PlayerPiece.tsx rename to src/components/editor/PlayerPiece.tsx diff --git a/front/components/editor/SavingState.tsx b/src/components/editor/SavingState.tsx similarity index 100% rename from front/components/editor/SavingState.tsx rename to src/components/editor/SavingState.tsx diff --git a/front/editor/ActionsDomains.ts b/src/editor/ActionsDomains.ts similarity index 100% rename from front/editor/ActionsDomains.ts rename to src/editor/ActionsDomains.ts diff --git a/front/editor/PlayerDomains.ts b/src/editor/PlayerDomains.ts similarity index 100% rename from front/editor/PlayerDomains.ts rename to src/editor/PlayerDomains.ts diff --git a/front/editor/RackedItems.ts b/src/editor/RackedItems.ts similarity index 100% rename from front/editor/RackedItems.ts rename to src/editor/RackedItems.ts diff --git a/front/editor/TacticContentDomains.ts b/src/editor/StepContentDomains.ts similarity index 100% rename from front/editor/TacticContentDomains.ts rename to src/editor/StepContentDomains.ts diff --git a/src/editor/TacticContentDomains.ts b/src/editor/TacticContentDomains.ts new file mode 100644 index 0000000..5839bee --- /dev/null +++ b/src/editor/TacticContentDomains.ts @@ -0,0 +1,243 @@ +import { Pos, ratioWithinBase } from "../geo/Pos" +import { + BallState, + Player, + PlayerInfo, + PlayerTeam, +} from "../model/tactic/Player" +import { + Ball, + BALL_ID, + BALL_TYPE, + CourtObject, +} from "../model/tactic/CourtObjects" +import { + ComponentId, + TacticComponent, + TacticContent, +} from "../model/tactic/Tactic" +import { overlaps } from "../geo/Box" +import { RackedCourtObject, RackedPlayer } from "./RackedItems" +import { changePlayerBallState } from "./PlayerDomains" + +export function placePlayerAt( + refBounds: DOMRect, + courtBounds: DOMRect, + element: RackedPlayer, +): Player { + const { x, y } = ratioWithinBase(refBounds, courtBounds) + + return { + type: "player", + id: "player-" + element.key + "-" + element.team, + team: element.team, + role: element.key, + rightRatio: x, + bottomRatio: y, + ballState: BallState.NONE, + path: null, + actions: [], + } +} + +export function placeObjectAt( + refBounds: DOMRect, + courtBounds: DOMRect, + rackedObject: RackedCourtObject, + content: TacticContent, +): TacticContent { + const { x, y } = ratioWithinBase(refBounds, courtBounds) + + let courtObject: CourtObject + + switch (rackedObject.key) { + case BALL_TYPE: + const playerCollidedIdx = getComponentCollided( + refBounds, + content.components, + BALL_ID, + ) + if (playerCollidedIdx != -1) { + return dropBallOnComponent(playerCollidedIdx, content, true) + } + + courtObject = { + type: BALL_TYPE, + id: BALL_ID, + rightRatio: x, + bottomRatio: y, + actions: [], + } + break + + default: + throw new Error("unknown court object " + rackedObject.key) + } + + return { + ...content, + components: [...content.components, courtObject], + } +} + +export function dropBallOnComponent( + targetedComponentIdx: number, + content: TacticContent, + setAsOrigin: boolean, +): TacticContent { + const component = content.components[targetedComponentIdx] + + if (component.type === "player" || component.type === "phantom") { + const newState = + setAsOrigin || + component.ballState === BallState.PASSED_ORIGIN || + component.ballState === BallState.HOLDS_ORIGIN + ? BallState.HOLDS_ORIGIN + : BallState.HOLDS_BY_PASS + + content = changePlayerBallState(component, newState, content) + } + + return removeBall(content) +} + +export function removeBall(content: TacticContent): TacticContent { + const ballObjIdx = content.components.findIndex((o) => o.type == "ball") + + if (ballObjIdx == -1) { + return content + } + + return { + ...content, + components: content.components.toSpliced(ballObjIdx, 1), + } +} + +export function placeBallAt( + refBounds: DOMRect, + courtBounds: DOMRect, + content: TacticContent, +): TacticContent { + if (!overlaps(courtBounds, refBounds)) { + return removeBall(content) + } + const playerCollidedIdx = getComponentCollided( + refBounds, + content.components, + BALL_ID, + ) + + if (playerCollidedIdx != -1) { + return dropBallOnComponent(playerCollidedIdx, content, true) + } + + const ballIdx = content.components.findIndex((o) => o.type == "ball") + + const { x, y } = ratioWithinBase(refBounds, courtBounds) + + const ball: Ball = { + type: BALL_TYPE, + id: BALL_ID, + rightRatio: x, + bottomRatio: y, + actions: [], + } + + let components = content.components + + if (ballIdx != -1) { + components = components.toSpliced(ballIdx, 1, ball) + } else { + components = components.concat(ball) + } + + return { + ...content, + components, + } +} + +export function moveComponent( + newPos: Pos, + component: TacticComponent, + info: PlayerInfo, + courtBounds: DOMRect, + content: TacticContent, + removed: (content: TacticContent) => TacticContent, +): TacticContent { + const playerBounds = document + .getElementById(info.id)! + .getBoundingClientRect() + + // if the piece is no longer on the court, remove it + if (!overlaps(playerBounds, courtBounds)) { + return removed(content) + } + return updateComponent( + { + ...component, + rightRatio: newPos.x, + bottomRatio: newPos.y, + }, + content, + ) +} + +export function removeComponent( + componentId: ComponentId, + content: TacticContent, +): TacticContent { + return { + ...content, + components: content.components.filter((c) => c.id !== componentId), + } +} + +export function updateComponent( + component: TacticComponent, + content: TacticContent, +): TacticContent { + return { + ...content, + components: content.components.map((c) => + c.id === component.id ? component : c, + ), + } +} + +export function getComponentCollided( + bounds: DOMRect, + components: TacticComponent[], + ignore?: ComponentId, +): number | -1 { + for (let i = 0; i < components.length; i++) { + const component = components[i] + + if (component.id == ignore) continue + + const playerBounds = document + .getElementById(component.id)! + .getBoundingClientRect() + + if (overlaps(playerBounds, bounds)) { + return i + } + } + return -1 +} + +export function getRackPlayers( + team: PlayerTeam, + components: TacticComponent[], +): RackedPlayer[] { + return ["1", "2", "3", "4", "5"] + .filter( + (role) => + components.findIndex( + (c) => + c.type == "player" && c.team == team && c.role == role, + ) == -1, + ) + .map((key) => ({ team, key })) +} diff --git a/front/geo/Box.ts b/src/geo/Box.ts similarity index 100% rename from front/geo/Box.ts rename to src/geo/Box.ts diff --git a/front/geo/Pos.ts b/src/geo/Pos.ts similarity index 100% rename from front/geo/Pos.ts rename to src/geo/Pos.ts diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..880d452 --- /dev/null +++ b/src/index.css @@ -0,0 +1,25 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + +} + +body { + margin: 0; + display: flex; + place-items: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..7229199 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,11 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import './index.css' +import App from "./App.tsx"; + + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/front/model/Team.ts b/src/model/Team.ts similarity index 100% rename from front/model/Team.ts rename to src/model/Team.ts diff --git a/front/model/User.ts b/src/model/User.ts similarity index 100% rename from front/model/User.ts rename to src/model/User.ts diff --git a/front/model/tactic/Action.ts b/src/model/tactic/Action.ts similarity index 100% rename from front/model/tactic/Action.ts rename to src/model/tactic/Action.ts diff --git a/front/model/tactic/CourtObjects.ts b/src/model/tactic/CourtObjects.ts similarity index 100% rename from front/model/tactic/CourtObjects.ts rename to src/model/tactic/CourtObjects.ts diff --git a/front/model/tactic/Player.ts b/src/model/tactic/Player.ts similarity index 100% rename from front/model/tactic/Player.ts rename to src/model/tactic/Player.ts diff --git a/front/model/tactic/Tactic.ts b/src/model/tactic/Tactic.ts similarity index 100% rename from front/model/tactic/Tactic.ts rename to src/model/tactic/Tactic.ts diff --git a/src/model/tactic/TacticInfo.ts b/src/model/tactic/TacticInfo.ts new file mode 100644 index 0000000..dfe1190 --- /dev/null +++ b/src/model/tactic/TacticInfo.ts @@ -0,0 +1,39 @@ +import { Player, PlayerPhantom } from "./Player" +import { Action } from "./Action" +import { CourtObject } from "./CourtObjects" + +export interface Tactic { + id: number + name: string + content: TacticContent +} + +export interface TacticContent { + components: TacticComponent[] + //actions: Action[] +} + +export type TacticComponent = Player | CourtObject | PlayerPhantom +export type ComponentId = string + +export interface Component { + /** + * The component's type + */ + readonly type: T + /** + * The component's identifier + */ + readonly id: ComponentId + /** + * Percentage of the component's position to the bottom (0 means top, 1 means bottom, 0.5 means middle) + */ + readonly bottomRatio: number + + /** + * Percentage of the component's position to the right (0 means left, 1 means right, 0.5 means middle) + */ + readonly rightRatio: number + + readonly actions: Action[] +} diff --git a/src/pages/404.tsx b/src/pages/404.tsx new file mode 100644 index 0000000..558b442 --- /dev/null +++ b/src/pages/404.tsx @@ -0,0 +1,11 @@ +import {useLocation} from "react-router-dom"; +import {BASE} from "../Constants.ts"; +export default function NotFoundPage() { + + const target = useLocation() + + return
+

{target.pathname} NOT FOUND !

+ +
+} diff --git a/src/pages/CreateTeamPage.tsx b/src/pages/CreateTeamPage.tsx new file mode 100644 index 0000000..daefb83 --- /dev/null +++ b/src/pages/CreateTeamPage.tsx @@ -0,0 +1,4 @@ + +export default function CreateTeamPage() { + return

Create Team Page

+} \ No newline at end of file diff --git a/front/views/Editor.tsx b/src/pages/Editor.tsx similarity index 95% rename from front/views/Editor.tsx rename to src/pages/Editor.tsx index 598f94a..15c9bfb 100644 --- a/front/views/Editor.tsx +++ b/src/pages/Editor.tsx @@ -28,7 +28,7 @@ import SavingState, { } from "../components/editor/SavingState" import { BALL_TYPE } from "../model/tactic/CourtObjects" -import { CourtAction } from "./editor/CourtAction" +import { CourtAction } from "../components/editor/CourtAction" import { ActionPreview, BasketCourt } from "../components/editor/BasketCourt" import { overlaps } from "../geo/Box" import { @@ -83,6 +83,20 @@ export interface EditorViewProps { courtType: "PLAIN" | "HALF" } + +export interface EditorPageProps { + courtType: "PLAIN" | "HALF" +} + +export default function EditorPage({ courtType }: EditorPageProps) { + return +} + export interface EditorProps { id: number name: string @@ -90,7 +104,7 @@ export interface EditorProps { courtType: "PLAIN" | "HALF" } -export default function Editor({ id, name, courtType, content }: EditorProps) { +function Editor({ id, name, courtType, content }: EditorProps) { const isInGuestMode = id == -1 const storage_content = localStorage.getItem(GUEST_MODE_CONTENT_STORAGE_KEY) @@ -135,11 +149,11 @@ export default function Editor({ id, name, courtType, content }: EditorProps) { } function EditorView({ - tactic: { id, name, content: initialContent }, - onContentChange, - onNameChange, - courtType, -}: EditorViewProps) { + tactic: { id, name, content: initialContent }, + onContentChange, + onNameChange, + courtType, + }: EditorViewProps) { const isInGuestMode = id == -1 const [titleStyle, setTitleStyle] = useState({}) @@ -520,12 +534,12 @@ interface PlayerRackProps { } function PlayerRack({ - id, - objects, - setObjects, - courtRef, - setComponents, -}: PlayerRackProps) { + id, + objects, + setObjects, + courtRef, + setComponents, + }: PlayerRackProps) { const courtBounds = useCallback( () => courtRef.current!.getBoundingClientRect(), [courtRef], @@ -579,15 +593,15 @@ interface CourtPlayerArrowActionProps { } function CourtPlayerArrowAction({ - playerInfo, - player, - isInvalid, - - content, - setContent, - setPreviewAction, - courtRef, -}: CourtPlayerArrowActionProps) { + playerInfo, + player, + isInvalid, + + content, + setContent, + setPreviewAction, + courtRef, + }: CourtPlayerArrowActionProps) { const courtBounds = useCallback( () => courtRef.current!.getBoundingClientRect(), [courtRef], @@ -754,4 +768,4 @@ function useContentState( ) return [content, setContentSynced, savingState] -} +} \ No newline at end of file diff --git a/front/views/Home.tsx b/src/pages/Home.tsx similarity index 95% rename from front/views/Home.tsx rename to src/pages/Home.tsx index a9a4b0a..df27c11 100644 --- a/front/views/Home.tsx +++ b/src/pages/Home.tsx @@ -1,8 +1,8 @@ import "../style/home/home.css" // import AccountSvg from "../assets/account.svg?react" -import { Header } from "./template/Header" -import { BASE } from "../Constants" +import {Header} from "./template/Header" +import {BASE} from "../Constants" interface Tactic { id: number @@ -18,7 +18,14 @@ interface Team { second_color: string } -export default function Home({ + +export default function HomePage() { + + console.log("HOME PAGE LOADED") + return +} + +function Home({ lastTactics, allTactics, teams, diff --git a/front/views/NewTacticPanel.tsx b/src/pages/NewTacticPage.tsx similarity index 97% rename from front/views/NewTacticPanel.tsx rename to src/pages/NewTacticPage.tsx index d02f314..2d056d8 100644 --- a/front/views/NewTacticPanel.tsx +++ b/src/pages/NewTacticPage.tsx @@ -5,7 +5,7 @@ import plainCourt from "../assets/court/full_court.svg" import halfCourt from "../assets/court/half_court.svg" import { BASE } from "../Constants" -export default function NewTacticPanel() { +export default function NewTacticPage() { return (
diff --git a/front/views/TeamPanel.tsx b/src/pages/TeamPanel.tsx similarity index 72% rename from front/views/TeamPanel.tsx rename to src/pages/TeamPanel.tsx index 709d7f2..e0e2baa 100644 --- a/front/views/TeamPanel.tsx +++ b/src/pages/TeamPanel.tsx @@ -1,13 +1,25 @@ import "../style/team_panel.css" -import { BASE } from "../Constants" -import { Team, TeamInfo, Member } from "../model/Team" -import { User } from "../model/User" +import {BASE} from "../Constants" +import {Member, Team, TeamInfo} from "../model/Team" +import {useParams} from "react-router-dom"; -export default function TeamPanel({ - isCoach, - team, - currentUserId, -}: { +export default function TeamPanelPage() { + const {teamId} = useParams() + const teamInfo = { + id: parseInt(teamId!), + name: teamId!, + mainColor: "#FFFFFF", + secondColor: "#000000", + picture: "https://a.espncdn.com/combiner/i?img=/i/teamlogos/nba/500/lal.png" + } + return +} + +function TeamPanel({ + isCoach, + team, + currentUserId, + }: { isCoach: boolean team: Team currentUserId: number @@ -19,9 +31,9 @@ export default function TeamPanel({ IQBall - + - {isCoach && } + {isCoach && }

{team.name}

- +
@@ -46,19 +58,19 @@ function TeamDisplay({ team }: { team: TeamInfo }) {

Couleur secondaire

- - + +
) } -function ColorDisplay({ color }: { color: string }) { - return
+function ColorDisplay({color}: { color: string }) { + return
} -function CoachOptions({ id }: { id: number }) { +function CoachOptions({id}: { id: number }) { return (
diff --git a/front/style/actions/arrow_action.css b/src/style/actions/arrow_action.css similarity index 100% rename from front/style/actions/arrow_action.css rename to src/style/actions/arrow_action.css diff --git a/front/style/actions/remove_action.css b/src/style/actions/remove_action.css similarity index 100% rename from front/style/actions/remove_action.css rename to src/style/actions/remove_action.css diff --git a/front/style/ball.css b/src/style/ball.css similarity index 100% rename from front/style/ball.css rename to src/style/ball.css diff --git a/front/style/bendable_arrows.css b/src/style/bendable_arrows.css similarity index 100% rename from front/style/bendable_arrows.css rename to src/style/bendable_arrows.css diff --git a/front/style/editor.css b/src/style/editor.css similarity index 99% rename from front/style/editor.css rename to src/style/editor.css index 5ba7596..b6a8ea4 100644 --- a/front/style/editor.css +++ b/src/style/editor.css @@ -23,6 +23,7 @@ } #topbar-div { + width: 100%; display: flex; background-color: var(--main-color); margin-bottom: 3px; diff --git a/front/style/home/home.css b/src/style/home/home.css similarity index 84% rename from front/style/home/home.css rename to src/style/home/home.css index 455e3af..0c4ecf7 100644 --- a/front/style/home/home.css +++ b/src/style/home/home.css @@ -1,4 +1,4 @@ -@import url(../theme/dark.css); +@import url(../theme/default.css); @import url(personnal_space.css); @import url(side_menu.css); @import url(../template/header.css); @@ -21,12 +21,12 @@ body { flex-direction: row; margin: 0px; height: 100%; - background-color: var(--second-color); + background-color: var(--home-second-color); } .data { border: 1.5px solid var(--main-contrast-color); - background-color: var(--main-color); + background-color: var(--home-main-color); border-radius: 0.75cap; color: var(--main-contrast-color); } diff --git a/front/style/home/personnal_space.css b/src/style/home/personnal_space.css similarity index 93% rename from front/style/home/personnal_space.css rename to src/style/home/personnal_space.css index 173098e..8598a0e 100644 --- a/front/style/home/personnal_space.css +++ b/src/style/home/personnal_space.css @@ -13,7 +13,7 @@ #body-personal-space { width: 95%; /* background-color: #ccc2b7; */ - border: 3px var(--main-color) solid; + border: 3px var(--home-main-color) solid; border-radius: 0.5cap; align-self: center; } diff --git a/front/style/home/side_menu.css b/src/style/home/side_menu.css similarity index 81% rename from front/style/home/side_menu.css rename to src/style/home/side_menu.css index 3a23947..aeb50b4 100644 --- a/front/style/home/side_menu.css +++ b/src/style/home/side_menu.css @@ -1,7 +1,7 @@ -@import url(../theme/dark.css); +@import url(../theme/default.css); #side-menu { - background-color: var(--third-color); + background-color: var(--home-third-color); display: flex; flex-direction: column; align-items: center; @@ -17,7 +17,7 @@ width: 90%; } .titre-side-menu { - border-bottom: var(--main-color) solid 3px; + border-bottom: var(--home-main-color) solid 3px; width: 100%; margin-bottom: 3%; } @@ -28,7 +28,7 @@ color: var(--main-contrast-color); letter-spacing: 1px; text-transform: uppercase; - background-color: var(--main-color); + background-color: var(--home-main-color); padding: 3%; margin-bottom: 0px; margin-right: 3%; diff --git a/front/style/new_tactic_panel.css b/src/style/new_tactic_panel.css similarity index 100% rename from front/style/new_tactic_panel.css rename to src/style/new_tactic_panel.css diff --git a/front/style/player.css b/src/style/player.css similarity index 100% rename from front/style/player.css rename to src/style/player.css diff --git a/src/style/steps_tree.css b/src/style/steps_tree.css new file mode 100644 index 0000000..eadeaf6 --- /dev/null +++ b/src/style/steps_tree.css @@ -0,0 +1,87 @@ +.step-piece { + position: relative; + font-family: monospace; + pointer-events: all; + + background-color: var(--editor-tree-step-piece); + color: var(--selected-team-secondarycolor); + + border-radius: 100px; + + width: 20px; + height: 20px; + + display: flex; + + align-items: center; + justify-content: center; + + user-select: none; + cursor: pointer; + + border: 2px solid var(--editor-tree-background); +} + +.step-piece-selected { + border: 2px solid var(--selection-color-light); +} + +.step-piece-selected, +.step-piece:focus, +.step-piece:hover { + background-color: var(--editor-tree-step-piece-hovered); +} + +.step-piece-actions { + display: none; + position: absolute; + column-gap: 5px; + top: -140%; +} + +.step-piece-selected .step-piece-actions { + display: flex; +} + +.add-icon, +.remove-icon { + background-color: white; + border-radius: 100%; +} + +.add-icon { + fill: var(--add-icon-fill); +} + +.remove-icon { + fill: var(--remove-icon-fill); +} + +.step-children { + margin-top: 10vh; + display: flex; + flex-direction: row; + width: 100%; + height: 100%; +} + +.step-group { + position: relative; + + display: flex; + flex-direction: column; + align-items: center; + + width: 100%; + height: 100%; +} + +.steps-tree { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding-top: 10%; + + height: 100%; +} diff --git a/front/style/team_panel.css b/src/style/team_panel.css similarity index 100% rename from front/style/team_panel.css rename to src/style/team_panel.css diff --git a/front/style/template/header.css b/src/style/template/header.css similarity index 95% rename from front/style/template/header.css rename to src/style/template/header.css index 2ea8d2f..266b8c8 100644 --- a/front/style/template/header.css +++ b/src/style/template/header.css @@ -1,6 +1,6 @@ #header { text-align: center; - background-color: var(--main-color); + background-color: var(--home-main-color); margin: 0px; /* border : var(--accent-color) 1px solid; */ display: flex; diff --git a/front/style/theme/default.css b/src/style/theme/default.css similarity index 74% rename from front/style/theme/default.css rename to src/style/theme/default.css index 1885746..caa5162 100644 --- a/front/style/theme/default.css +++ b/src/style/theme/default.css @@ -21,4 +21,12 @@ --player-piece-ball-border-color: #000000; --text-main-font: "Roboto", sans-serif; + + --home-main-color: #191a21; + --home-second-color: #282a36; + --home-third-color: #303341; + --accent-color: #ffa238; + --main-contrast-color: #e6edf3; + --font-title: Helvetica; + --font-content: Helvetica; } diff --git a/front/style/title_input.css b/src/style/title_input.css similarity index 100% rename from front/style/title_input.css rename to src/style/title_input.css diff --git a/front/style/visualizer.css b/src/style/visualizer.css similarity index 100% rename from front/style/visualizer.css rename to src/style/visualizer.css diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/tsconfig.json b/tsconfig.json index d01f3cc..a7fc6fb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,27 +1,25 @@ { "compilerOptions": { - "target": "es2021", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], - "types": ["vite/client", "vite-plugin-svgr/client"], - "allowJs": true, + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "module": "esnext", - "moduleResolution": "node", + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, - "jsx": "react-jsx" + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true }, - "include": [ - "front" - ] + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] } diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..97ede7e --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "strict": true + }, + "include": ["vite.config.ts"] +} diff --git a/verify.sh b/verify.sh deleted file mode 100755 index 314b8bc..0000000 --- a/verify.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash - -## verify php and typescript types - -echo "running php typechecking" -vendor/bin/phpstan analyze && echo "php types are respected" - -echo "running typescript typechecking" -npm run tsc && echo "typescript types are respected" \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index 4ff1dc5..596d3e8 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,47 +1,17 @@ -import {defineConfig} from "vite"; -import react from '@vitejs/plugin-react'; -import fs from "fs"; -import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'; +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js"; import svgr from "vite-plugin-svgr"; - -function resolve_entries(dirname: string): [string, string][] { - - //exclude assets - if (dirname == "front/assets" || dirname == "front/style") { - return [] - } - - return fs.readdirSync(dirname).flatMap(file_name => { - if (fs.lstatSync(`${dirname}/${file_name}`).isFile()) { - return [[`${dirname}/${file_name}`, `${dirname}/${file_name}`]] - } else { - return resolve_entries(`${dirname}/${file_name}`) - } - }) -} - +// https://vitejs.dev/config/ export default defineConfig({ - root: 'front', - base: '/front', - envDir: '..', - build: { - target: 'es2021', - assetsDir: '', - outDir: "../dist", - manifest: true, - rollupOptions: { - input: Object.fromEntries(resolve_entries("front")), - preserveEntrySignatures: "allow-extension", - } - }, - plugins: [ - react(), - cssInjectedByJsPlugin({ - relativeCSSInjection: true, - }), - svgr({ - include: "**/*.svg?react" - }) - ] + plugins: [ + react(), + cssInjectedByJsPlugin({ + relativeCSSInjection: true, + }), + svgr({ + include: "**/*.svg?react" + }) + ] })