From 6577083b06f4a09d0148619c079b710a380d6db1 Mon Sep 17 00:00:00 2001 From: Ouijdane IMER Date: Tue, 23 Sep 2025 14:51:00 +0200 Subject: [PATCH] version_final_sans_test --- .vs/slnx.sqlite | Bin 90112 -> 90112 bytes GTF/project/backend/config/server.js | 866 +++++-- .../node_modules/.vite/deps/_metadata.json | 18 +- GTF/project/src/App.tsx | 124 +- GTF/project/src/components/Calendar.tsx | 19 +- GTF/project/src/components/TimeEntryModal.tsx | 545 +++-- GTF/project/src/components/UserProfile.tsx | 35 +- GTF/project/src/context/AuthContext.tsx | 104 +- GTF/project/src/types/TimeEntry.ts | 4 +- node_modules/.package-lock.json | 25 + node_modules/node-cron/LICENSE.md | 7 + node_modules/node-cron/README.md | 117 + .../node-cron/dist/cjs/create-id.d.ts | 1 + node_modules/node-cron/dist/cjs/create-id.js | 14 + .../node-cron/dist/cjs/create-id.js.map | 1 + node_modules/node-cron/dist/cjs/logger.d.ts | 7 + node_modules/node-cron/dist/cjs/logger.js | 57 + node_modules/node-cron/dist/cjs/logger.js.map | 1 + .../node-cron/dist/cjs/node-cron.d.ts | 18 + node_modules/node-cron/dist/cjs/node-cron.js | 71 + .../node-cron/dist/cjs/node-cron.js.map | 1 + .../asterisk-to-range-conversion.d.ts | 2 + .../asterisk-to-range-conversion.js | 21 + .../asterisk-to-range-conversion.js.map | 1 + .../dist/cjs/pattern/convertion/index.d.ts | 2 + .../dist/cjs/pattern/convertion/index.js | 42 + .../dist/cjs/pattern/convertion/index.js.map | 1 + .../convertion/month-names-conversion.d.ts | 2 + .../convertion/month-names-conversion.js | 21 + .../convertion/month-names-conversion.js.map | 1 + .../pattern/convertion/range-conversion.d.ts | 2 + .../pattern/convertion/range-conversion.js | 35 + .../convertion/range-conversion.js.map | 1 + .../convertion/week-day-names-conversion.d.ts | 2 + .../convertion/week-day-names-conversion.js | 20 + .../week-day-names-conversion.js.map | 1 + .../validation/pattern-validation.d.ts | 2 + .../pattern/validation/pattern-validation.js | 61 + .../validation/pattern-validation.js.map | 1 + .../dist/cjs/promise/tracked-promise.d.ts | 19 + .../dist/cjs/promise/tracked-promise.js | 55 + .../dist/cjs/promise/tracked-promise.js.map | 1 + .../node-cron/dist/cjs/scheduler/runner.d.ts | 42 + .../node-cron/dist/cjs/scheduler/runner.js | 192 ++ .../dist/cjs/scheduler/runner.js.map | 1 + .../node-cron/dist/cjs/task-registry.d.ts | 11 + .../node-cron/dist/cjs/task-registry.js | 35 + .../node-cron/dist/cjs/task-registry.js.map | 1 + .../background-scheduled-task.d.ts | 28 + .../background-scheduled-task.js | 220 ++ .../background-scheduled-task.js.map | 1 + .../background-scheduled-task/daemon.d.ts | 3 + .../tasks/background-scheduled-task/daemon.js | 142 ++ .../background-scheduled-task/daemon.js.map | 1 + .../background-scheduled-task/index.d.ts | 0 .../tasks/background-scheduled-task/index.js | 2 + .../background-scheduled-task/index.js.map | 1 + .../dist/cjs/tasks/inline-scheduled-task.d.ts | 30 + .../dist/cjs/tasks/inline-scheduled-task.js | 144 ++ .../cjs/tasks/inline-scheduled-task.js.map | 1 + .../dist/cjs/tasks/scheduled-task.d.ts | 37 + .../dist/cjs/tasks/scheduled-task.js | 3 + .../dist/cjs/tasks/scheduled-task.js.map | 1 + .../dist/cjs/tasks/state-machine.d.ts | 6 + .../node-cron/dist/cjs/tasks/state-machine.js | 25 + .../dist/cjs/tasks/state-machine.js.map | 1 + .../dist/cjs/time/localized-time.d.ts | 22 + .../node-cron/dist/cjs/time/localized-time.js | 81 + .../dist/cjs/time/localized-time.js.map | 1 + .../dist/cjs/time/matcher-walker.d.ts | 13 + .../node-cron/dist/cjs/time/matcher-walker.js | 100 + .../dist/cjs/time/matcher-walker.js.map | 1 + .../node-cron/dist/cjs/time/time-matcher.d.ts | 8 + .../node-cron/dist/cjs/time/time-matcher.js | 41 + .../dist/cjs/time/time-matcher.js.map | 1 + .../node-cron/dist/esm/create-id.d.ts | 1 + node_modules/node-cron/dist/esm/create-id.js | 14 + .../node-cron/dist/esm/create-id.js.map | 1 + node_modules/node-cron/dist/esm/logger.d.ts | 7 + node_modules/node-cron/dist/esm/logger.js | 57 + node_modules/node-cron/dist/esm/logger.js.map | 1 + .../node-cron/dist/esm/node-cron.d.ts | 18 + node_modules/node-cron/dist/esm/node-cron.js | 71 + .../node-cron/dist/esm/node-cron.js.map | 1 + .../asterisk-to-range-conversion.d.ts | 2 + .../asterisk-to-range-conversion.js | 21 + .../asterisk-to-range-conversion.js.map | 1 + .../dist/esm/pattern/convertion/index.d.ts | 2 + .../dist/esm/pattern/convertion/index.js | 42 + .../dist/esm/pattern/convertion/index.js.map | 1 + .../convertion/month-names-conversion.d.ts | 2 + .../convertion/month-names-conversion.js | 21 + .../convertion/month-names-conversion.js.map | 1 + .../pattern/convertion/range-conversion.d.ts | 2 + .../pattern/convertion/range-conversion.js | 35 + .../convertion/range-conversion.js.map | 1 + .../convertion/week-day-names-conversion.d.ts | 2 + .../convertion/week-day-names-conversion.js | 20 + .../week-day-names-conversion.js.map | 1 + .../validation/pattern-validation.d.ts | 2 + .../pattern/validation/pattern-validation.js | 61 + .../validation/pattern-validation.js.map | 1 + .../dist/esm/promise/tracked-promise.d.ts | 19 + .../dist/esm/promise/tracked-promise.js | 55 + .../dist/esm/promise/tracked-promise.js.map | 1 + .../node-cron/dist/esm/scheduler/runner.d.ts | 42 + .../node-cron/dist/esm/scheduler/runner.js | 192 ++ .../dist/esm/scheduler/runner.js.map | 1 + .../node-cron/dist/esm/task-registry.d.ts | 11 + .../node-cron/dist/esm/task-registry.js | 35 + .../node-cron/dist/esm/task-registry.js.map | 1 + .../background-scheduled-task.d.ts | 28 + .../background-scheduled-task.js | 220 ++ .../background-scheduled-task.js.map | 1 + .../background-scheduled-task/daemon.d.ts | 3 + .../tasks/background-scheduled-task/daemon.js | 109 + .../background-scheduled-task/daemon.js.map | 1 + .../background-scheduled-task/index.d.ts | 1 + .../tasks/background-scheduled-task/index.js | 3 + .../background-scheduled-task/index.js.map | 1 + .../dist/esm/tasks/inline-scheduled-task.d.ts | 30 + .../dist/esm/tasks/inline-scheduled-task.js | 144 ++ .../esm/tasks/inline-scheduled-task.js.map | 1 + .../dist/esm/tasks/scheduled-task.d.ts | 37 + .../dist/esm/tasks/scheduled-task.js | 3 + .../dist/esm/tasks/scheduled-task.js.map | 1 + .../dist/esm/tasks/state-machine.d.ts | 6 + .../node-cron/dist/esm/tasks/state-machine.js | 25 + .../dist/esm/tasks/state-machine.js.map | 1 + .../dist/esm/time/localized-time.d.ts | 22 + .../node-cron/dist/esm/time/localized-time.js | 81 + .../dist/esm/time/localized-time.js.map | 1 + .../dist/esm/time/matcher-walker.d.ts | 13 + .../node-cron/dist/esm/time/matcher-walker.js | 100 + .../dist/esm/time/matcher-walker.js.map | 1 + .../node-cron/dist/esm/time/time-matcher.d.ts | 8 + .../node-cron/dist/esm/time/time-matcher.js | 41 + .../dist/esm/time/time-matcher.js.map | 1 + node_modules/node-cron/package.json | 85 + node_modules/nodemailer/.gitattributes | 6 + node_modules/nodemailer/.ncurc.js | 11 + node_modules/nodemailer/.prettierrc.js | 8 + node_modules/nodemailer/CHANGELOG.md | 905 +++++++ node_modules/nodemailer/CODE_OF_CONDUCT.md | 76 + node_modules/nodemailer/LICENSE | 16 + node_modules/nodemailer/README.md | 86 + node_modules/nodemailer/SECURITY.txt | 22 + .../nodemailer/lib/addressparser/index.js | 327 +++ node_modules/nodemailer/lib/base64/index.js | 139 ++ node_modules/nodemailer/lib/dkim/index.js | 251 ++ .../nodemailer/lib/dkim/message-parser.js | 155 ++ .../nodemailer/lib/dkim/relaxed-body.js | 154 ++ node_modules/nodemailer/lib/dkim/sign.js | 117 + node_modules/nodemailer/lib/fetch/cookies.js | 281 +++ node_modules/nodemailer/lib/fetch/index.js | 274 +++ .../nodemailer/lib/json-transport/index.js | 82 + .../nodemailer/lib/mail-composer/index.js | 601 +++++ node_modules/nodemailer/lib/mailer/index.js | 434 ++++ .../nodemailer/lib/mailer/mail-message.js | 315 +++ .../nodemailer/lib/mime-funcs/index.js | 625 +++++ .../nodemailer/lib/mime-funcs/mime-types.js | 2104 +++++++++++++++++ .../nodemailer/lib/mime-node/index.js | 1314 ++++++++++ .../nodemailer/lib/mime-node/last-newline.js | 33 + .../nodemailer/lib/mime-node/le-unix.js | 43 + .../nodemailer/lib/mime-node/le-windows.js | 52 + node_modules/nodemailer/lib/nodemailer.js | 155 ++ node_modules/nodemailer/lib/punycode/index.js | 460 ++++ node_modules/nodemailer/lib/qp/index.js | 219 ++ .../lib/sendmail-transport/index.js | 210 ++ .../nodemailer/lib/ses-transport/index.js | 234 ++ node_modules/nodemailer/lib/shared/index.js | 710 ++++++ .../lib/smtp-connection/data-stream.js | 108 + .../lib/smtp-connection/http-proxy-client.js | 143 ++ .../nodemailer/lib/smtp-connection/index.js | 1836 ++++++++++++++ .../nodemailer/lib/smtp-pool/index.js | 652 +++++ .../nodemailer/lib/smtp-pool/pool-resource.js | 253 ++ .../nodemailer/lib/smtp-transport/index.js | 416 ++++ .../nodemailer/lib/stream-transport/index.js | 135 ++ .../nodemailer/lib/well-known/index.js | 47 + .../nodemailer/lib/well-known/services.json | 611 +++++ node_modules/nodemailer/lib/xoauth2/index.js | 427 ++++ node_modules/nodemailer/package.json | 43 + package-lock.json | 31 + package.json | 6 + 184 files changed, 19853 insertions(+), 512 deletions(-) create mode 100644 node_modules/.package-lock.json create mode 100644 node_modules/node-cron/LICENSE.md create mode 100644 node_modules/node-cron/README.md create mode 100644 node_modules/node-cron/dist/cjs/create-id.d.ts create mode 100644 node_modules/node-cron/dist/cjs/create-id.js create mode 100644 node_modules/node-cron/dist/cjs/create-id.js.map create mode 100644 node_modules/node-cron/dist/cjs/logger.d.ts create mode 100644 node_modules/node-cron/dist/cjs/logger.js create mode 100644 node_modules/node-cron/dist/cjs/logger.js.map create mode 100644 node_modules/node-cron/dist/cjs/node-cron.d.ts create mode 100644 node_modules/node-cron/dist/cjs/node-cron.js create mode 100644 node_modules/node-cron/dist/cjs/node-cron.js.map create mode 100644 node_modules/node-cron/dist/cjs/pattern/convertion/asterisk-to-range-conversion.d.ts create mode 100644 node_modules/node-cron/dist/cjs/pattern/convertion/asterisk-to-range-conversion.js create mode 100644 node_modules/node-cron/dist/cjs/pattern/convertion/asterisk-to-range-conversion.js.map create mode 100644 node_modules/node-cron/dist/cjs/pattern/convertion/index.d.ts create mode 100644 node_modules/node-cron/dist/cjs/pattern/convertion/index.js create mode 100644 node_modules/node-cron/dist/cjs/pattern/convertion/index.js.map create mode 100644 node_modules/node-cron/dist/cjs/pattern/convertion/month-names-conversion.d.ts create mode 100644 node_modules/node-cron/dist/cjs/pattern/convertion/month-names-conversion.js create mode 100644 node_modules/node-cron/dist/cjs/pattern/convertion/month-names-conversion.js.map create mode 100644 node_modules/node-cron/dist/cjs/pattern/convertion/range-conversion.d.ts create mode 100644 node_modules/node-cron/dist/cjs/pattern/convertion/range-conversion.js create mode 100644 node_modules/node-cron/dist/cjs/pattern/convertion/range-conversion.js.map create mode 100644 node_modules/node-cron/dist/cjs/pattern/convertion/week-day-names-conversion.d.ts create mode 100644 node_modules/node-cron/dist/cjs/pattern/convertion/week-day-names-conversion.js create mode 100644 node_modules/node-cron/dist/cjs/pattern/convertion/week-day-names-conversion.js.map create mode 100644 node_modules/node-cron/dist/cjs/pattern/validation/pattern-validation.d.ts create mode 100644 node_modules/node-cron/dist/cjs/pattern/validation/pattern-validation.js create mode 100644 node_modules/node-cron/dist/cjs/pattern/validation/pattern-validation.js.map create mode 100644 node_modules/node-cron/dist/cjs/promise/tracked-promise.d.ts create mode 100644 node_modules/node-cron/dist/cjs/promise/tracked-promise.js create mode 100644 node_modules/node-cron/dist/cjs/promise/tracked-promise.js.map create mode 100644 node_modules/node-cron/dist/cjs/scheduler/runner.d.ts create mode 100644 node_modules/node-cron/dist/cjs/scheduler/runner.js create mode 100644 node_modules/node-cron/dist/cjs/scheduler/runner.js.map create mode 100644 node_modules/node-cron/dist/cjs/task-registry.d.ts create mode 100644 node_modules/node-cron/dist/cjs/task-registry.js create mode 100644 node_modules/node-cron/dist/cjs/task-registry.js.map create mode 100644 node_modules/node-cron/dist/cjs/tasks/background-scheduled-task/background-scheduled-task.d.ts create mode 100644 node_modules/node-cron/dist/cjs/tasks/background-scheduled-task/background-scheduled-task.js create mode 100644 node_modules/node-cron/dist/cjs/tasks/background-scheduled-task/background-scheduled-task.js.map create mode 100644 node_modules/node-cron/dist/cjs/tasks/background-scheduled-task/daemon.d.ts create mode 100644 node_modules/node-cron/dist/cjs/tasks/background-scheduled-task/daemon.js create mode 100644 node_modules/node-cron/dist/cjs/tasks/background-scheduled-task/daemon.js.map create mode 100644 node_modules/node-cron/dist/cjs/tasks/background-scheduled-task/index.d.ts create mode 100644 node_modules/node-cron/dist/cjs/tasks/background-scheduled-task/index.js create mode 100644 node_modules/node-cron/dist/cjs/tasks/background-scheduled-task/index.js.map create mode 100644 node_modules/node-cron/dist/cjs/tasks/inline-scheduled-task.d.ts create mode 100644 node_modules/node-cron/dist/cjs/tasks/inline-scheduled-task.js create mode 100644 node_modules/node-cron/dist/cjs/tasks/inline-scheduled-task.js.map create mode 100644 node_modules/node-cron/dist/cjs/tasks/scheduled-task.d.ts create mode 100644 node_modules/node-cron/dist/cjs/tasks/scheduled-task.js create mode 100644 node_modules/node-cron/dist/cjs/tasks/scheduled-task.js.map create mode 100644 node_modules/node-cron/dist/cjs/tasks/state-machine.d.ts create mode 100644 node_modules/node-cron/dist/cjs/tasks/state-machine.js create mode 100644 node_modules/node-cron/dist/cjs/tasks/state-machine.js.map create mode 100644 node_modules/node-cron/dist/cjs/time/localized-time.d.ts create mode 100644 node_modules/node-cron/dist/cjs/time/localized-time.js create mode 100644 node_modules/node-cron/dist/cjs/time/localized-time.js.map create mode 100644 node_modules/node-cron/dist/cjs/time/matcher-walker.d.ts create mode 100644 node_modules/node-cron/dist/cjs/time/matcher-walker.js create mode 100644 node_modules/node-cron/dist/cjs/time/matcher-walker.js.map create mode 100644 node_modules/node-cron/dist/cjs/time/time-matcher.d.ts create mode 100644 node_modules/node-cron/dist/cjs/time/time-matcher.js create mode 100644 node_modules/node-cron/dist/cjs/time/time-matcher.js.map create mode 100644 node_modules/node-cron/dist/esm/create-id.d.ts create mode 100644 node_modules/node-cron/dist/esm/create-id.js create mode 100644 node_modules/node-cron/dist/esm/create-id.js.map create mode 100644 node_modules/node-cron/dist/esm/logger.d.ts create mode 100644 node_modules/node-cron/dist/esm/logger.js create mode 100644 node_modules/node-cron/dist/esm/logger.js.map create mode 100644 node_modules/node-cron/dist/esm/node-cron.d.ts create mode 100644 node_modules/node-cron/dist/esm/node-cron.js create mode 100644 node_modules/node-cron/dist/esm/node-cron.js.map create mode 100644 node_modules/node-cron/dist/esm/pattern/convertion/asterisk-to-range-conversion.d.ts create mode 100644 node_modules/node-cron/dist/esm/pattern/convertion/asterisk-to-range-conversion.js create mode 100644 node_modules/node-cron/dist/esm/pattern/convertion/asterisk-to-range-conversion.js.map create mode 100644 node_modules/node-cron/dist/esm/pattern/convertion/index.d.ts create mode 100644 node_modules/node-cron/dist/esm/pattern/convertion/index.js create mode 100644 node_modules/node-cron/dist/esm/pattern/convertion/index.js.map create mode 100644 node_modules/node-cron/dist/esm/pattern/convertion/month-names-conversion.d.ts create mode 100644 node_modules/node-cron/dist/esm/pattern/convertion/month-names-conversion.js create mode 100644 node_modules/node-cron/dist/esm/pattern/convertion/month-names-conversion.js.map create mode 100644 node_modules/node-cron/dist/esm/pattern/convertion/range-conversion.d.ts create mode 100644 node_modules/node-cron/dist/esm/pattern/convertion/range-conversion.js create mode 100644 node_modules/node-cron/dist/esm/pattern/convertion/range-conversion.js.map create mode 100644 node_modules/node-cron/dist/esm/pattern/convertion/week-day-names-conversion.d.ts create mode 100644 node_modules/node-cron/dist/esm/pattern/convertion/week-day-names-conversion.js create mode 100644 node_modules/node-cron/dist/esm/pattern/convertion/week-day-names-conversion.js.map create mode 100644 node_modules/node-cron/dist/esm/pattern/validation/pattern-validation.d.ts create mode 100644 node_modules/node-cron/dist/esm/pattern/validation/pattern-validation.js create mode 100644 node_modules/node-cron/dist/esm/pattern/validation/pattern-validation.js.map create mode 100644 node_modules/node-cron/dist/esm/promise/tracked-promise.d.ts create mode 100644 node_modules/node-cron/dist/esm/promise/tracked-promise.js create mode 100644 node_modules/node-cron/dist/esm/promise/tracked-promise.js.map create mode 100644 node_modules/node-cron/dist/esm/scheduler/runner.d.ts create mode 100644 node_modules/node-cron/dist/esm/scheduler/runner.js create mode 100644 node_modules/node-cron/dist/esm/scheduler/runner.js.map create mode 100644 node_modules/node-cron/dist/esm/task-registry.d.ts create mode 100644 node_modules/node-cron/dist/esm/task-registry.js create mode 100644 node_modules/node-cron/dist/esm/task-registry.js.map create mode 100644 node_modules/node-cron/dist/esm/tasks/background-scheduled-task/background-scheduled-task.d.ts create mode 100644 node_modules/node-cron/dist/esm/tasks/background-scheduled-task/background-scheduled-task.js create mode 100644 node_modules/node-cron/dist/esm/tasks/background-scheduled-task/background-scheduled-task.js.map create mode 100644 node_modules/node-cron/dist/esm/tasks/background-scheduled-task/daemon.d.ts create mode 100644 node_modules/node-cron/dist/esm/tasks/background-scheduled-task/daemon.js create mode 100644 node_modules/node-cron/dist/esm/tasks/background-scheduled-task/daemon.js.map create mode 100644 node_modules/node-cron/dist/esm/tasks/background-scheduled-task/index.d.ts create mode 100644 node_modules/node-cron/dist/esm/tasks/background-scheduled-task/index.js create mode 100644 node_modules/node-cron/dist/esm/tasks/background-scheduled-task/index.js.map create mode 100644 node_modules/node-cron/dist/esm/tasks/inline-scheduled-task.d.ts create mode 100644 node_modules/node-cron/dist/esm/tasks/inline-scheduled-task.js create mode 100644 node_modules/node-cron/dist/esm/tasks/inline-scheduled-task.js.map create mode 100644 node_modules/node-cron/dist/esm/tasks/scheduled-task.d.ts create mode 100644 node_modules/node-cron/dist/esm/tasks/scheduled-task.js create mode 100644 node_modules/node-cron/dist/esm/tasks/scheduled-task.js.map create mode 100644 node_modules/node-cron/dist/esm/tasks/state-machine.d.ts create mode 100644 node_modules/node-cron/dist/esm/tasks/state-machine.js create mode 100644 node_modules/node-cron/dist/esm/tasks/state-machine.js.map create mode 100644 node_modules/node-cron/dist/esm/time/localized-time.d.ts create mode 100644 node_modules/node-cron/dist/esm/time/localized-time.js create mode 100644 node_modules/node-cron/dist/esm/time/localized-time.js.map create mode 100644 node_modules/node-cron/dist/esm/time/matcher-walker.d.ts create mode 100644 node_modules/node-cron/dist/esm/time/matcher-walker.js create mode 100644 node_modules/node-cron/dist/esm/time/matcher-walker.js.map create mode 100644 node_modules/node-cron/dist/esm/time/time-matcher.d.ts create mode 100644 node_modules/node-cron/dist/esm/time/time-matcher.js create mode 100644 node_modules/node-cron/dist/esm/time/time-matcher.js.map create mode 100644 node_modules/node-cron/package.json create mode 100644 node_modules/nodemailer/.gitattributes create mode 100644 node_modules/nodemailer/.ncurc.js create mode 100644 node_modules/nodemailer/.prettierrc.js create mode 100644 node_modules/nodemailer/CHANGELOG.md create mode 100644 node_modules/nodemailer/CODE_OF_CONDUCT.md create mode 100644 node_modules/nodemailer/LICENSE create mode 100644 node_modules/nodemailer/README.md create mode 100644 node_modules/nodemailer/SECURITY.txt create mode 100644 node_modules/nodemailer/lib/addressparser/index.js create mode 100644 node_modules/nodemailer/lib/base64/index.js create mode 100644 node_modules/nodemailer/lib/dkim/index.js create mode 100644 node_modules/nodemailer/lib/dkim/message-parser.js create mode 100644 node_modules/nodemailer/lib/dkim/relaxed-body.js create mode 100644 node_modules/nodemailer/lib/dkim/sign.js create mode 100644 node_modules/nodemailer/lib/fetch/cookies.js create mode 100644 node_modules/nodemailer/lib/fetch/index.js create mode 100644 node_modules/nodemailer/lib/json-transport/index.js create mode 100644 node_modules/nodemailer/lib/mail-composer/index.js create mode 100644 node_modules/nodemailer/lib/mailer/index.js create mode 100644 node_modules/nodemailer/lib/mailer/mail-message.js create mode 100644 node_modules/nodemailer/lib/mime-funcs/index.js create mode 100644 node_modules/nodemailer/lib/mime-funcs/mime-types.js create mode 100644 node_modules/nodemailer/lib/mime-node/index.js create mode 100644 node_modules/nodemailer/lib/mime-node/last-newline.js create mode 100644 node_modules/nodemailer/lib/mime-node/le-unix.js create mode 100644 node_modules/nodemailer/lib/mime-node/le-windows.js create mode 100644 node_modules/nodemailer/lib/nodemailer.js create mode 100644 node_modules/nodemailer/lib/punycode/index.js create mode 100644 node_modules/nodemailer/lib/qp/index.js create mode 100644 node_modules/nodemailer/lib/sendmail-transport/index.js create mode 100644 node_modules/nodemailer/lib/ses-transport/index.js create mode 100644 node_modules/nodemailer/lib/shared/index.js create mode 100644 node_modules/nodemailer/lib/smtp-connection/data-stream.js create mode 100644 node_modules/nodemailer/lib/smtp-connection/http-proxy-client.js create mode 100644 node_modules/nodemailer/lib/smtp-connection/index.js create mode 100644 node_modules/nodemailer/lib/smtp-pool/index.js create mode 100644 node_modules/nodemailer/lib/smtp-pool/pool-resource.js create mode 100644 node_modules/nodemailer/lib/smtp-transport/index.js create mode 100644 node_modules/nodemailer/lib/stream-transport/index.js create mode 100644 node_modules/nodemailer/lib/well-known/index.js create mode 100644 node_modules/nodemailer/lib/well-known/services.json create mode 100644 node_modules/nodemailer/lib/xoauth2/index.js create mode 100644 node_modules/nodemailer/package.json create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite index a0a60e290636d9c312310e2497a62162c575cc0d..a033adcc6e013033c78c69044d9c64bf30ea4873 100644 GIT binary patch delta 3785 zcmZveZEPb)8OL{THye9*XU5LPcI=H~JI?DR>v&^3wsk_{2-j;K~DGVA}rBi}-9aGybk5Q6(Ry`DqRdT}h^ z&fj?W!~k>Z(_HI!W8a&|ua|Q9^}L~(`J$nvli8G3SWGQxDKopin9Y?kx{+;Ni%d-z z>!oxqlg?_%b#qBe8^uK}ms&J5LtiYGQkkM@=JTy@MBdlb%#5B{N}DAuoyn!Nbhe~x zx$I&|OBJ)FOr~JwmvkJinA6kgd{#>(GZ`(tWawJHWG1ytZpkq6nN%{RC$&;GW8|{y zNv&ASq_uQuDXHa`jG|@~OU2|;4(GGD=x2FmI&j{&WNsBcZ0^)=z7-ktu=)>^yr-m# zPc!HZ^cwmldLDfiZJ~20fxPf-_&WSG{4RVFUV$Hki_in!0k44{foH%jco^hB4Dj;n z@@@Gg`6>C6vLU}$o|V1Qo6;YppGn`5o{+Yr^HNgsi1)-h;w|xM+}ecL9M=UV<8}+@ zc50iZ(Kv558s+LnJ+WD@Rb2=FdG_~_ebtu~7}f0(&R%$6?d-}0+pI1K%p~g;E|j;- zGu6h`zZ0yBr5r>{0HE!V0IbFYEjdlQadqyDCFv2O0*;H7fKBm?61fY`ji z=$)&t){2En*N~Apfr;QDAE<4Vt6d)Tq?Ouop<-5xg`Fk=B*w#~M6Y}%@4t6r$ARO^jG)gbvgvjS5B6UK{f6|7Vo$H=6WWVu>2 z_Yy|EJ;gsEFijOlGOYeMQ-X1UiBbF3cCBhwJHmv=1!fk9A#KIeO63hwnlsZM!#hRX z+uL3CV3e%r78+)~p%vHh?uM*#&+V%t5i3Ew54+}0+qJJMV*=yDvl6&RN8|TZHEgwZ zyI@={Y?$<*$ae3dqpf$X2Aa;ds*=Gjp;{}NYg@JA)rwhnoW7921g-LM9gaC1v`Spr z!ponj)~{|SwyPVG9U?qpxykM&oEC_-I~EX_)}2*<)3J>Dt?h2qk4BTD?RcX;fmtA( zvAqS{x~>tiVe23^3gyb9co*%Un{{WAAw^&mTuFV$ux;;AqeIrp+qHTFZ~HORRBwMP z?fGidLtE*5tEj(~jxz|Y{k0C+rN&mpaYcZ@Xt+SLUMW`_$7JD`$(eBp>v%))WpO6@ zB)mN<*ioq0%|^Rezi5r$wur&mombuc)i}Q`sZ*COzCF z+sWGng_~o07@Hur9;|A9PH#_iP&yB|2y5Z9i8(SymhWWdNiR3elAGpWa@NfSSc=9O z5~c@-(=r!kZ3Iq{#U1QTk}{4+HvzdA{%zAOKE%zk4|OALoD4t0Sd0vBqbo|*b!ZiF zaXyw1YK$b`d1S(^SJr&ZQDW}ok1BFOk_cu_hy?EDOputm*)l?U=kS3nMF9~Yy>ZM0 z8GawP(6(405o%Q*p5OZO<4SUPkW*O92!)L6!@^K+{$7&*rl&W5M7q(w=x~_R*xt(x zNcWB|v`p%??yyA6NB36j4d*7|ysqAGtryoWmUMWRLGPe9(ZBG!NM3U zs*_YFsK%*|Q;iV~MX5%pjuDl^SevXoN^PAbJuD*QCt%_U`4;sdNjCS|5|{juSuG7b4K z>)=S4q6<4fQYHbfvNn>G2@fA5UpADK_%J_fRpY!e_LOlt$9`VL=$tm0LQxvRVpEem zr6MBlV=10OW3-M=K*`|%zrfne(keCMvh|!Ar9(TRC95(Yq{)(}Qi!J34K682jcs(j zpF9o45!z=5Qx3hv0PUgUpidq@&Bw7hPa$9H;PHSg4!e1UB>)qZFE#NCYpAax(R+MF zqNlGS5z*$gK_)^!ud#h769H}c5zIuH_JQCG|6z#|_1P~Gu`eIaeSAKgt1lmJ(92IU e_GZw15d8dtV`*>C03BGBI^MK5|I!@&)Bgd1yfG&L delta 3190 zcmZuzTWB0r7@q0O&g^B*`Dd@Qo5|jiY|=EFt?@y{qNvqMY%Nt=@u4MdlWDp!nO!o| z8Wpm|D{2LGK`W>rC|VU-4f@~(s^SF^1u2L^p`fh_-l(Om(g)8ulbM~}_2pyczyJR^ z|2K2sSm401z>_*JJ=MRFm)^Sb6$s%Y9Q=Ym$46G8g_m&aTviKB+@D($;5IxMZKZN= zv`*(rtxNKgE5GD5l`p@2iKy-@N6H-h4&TFPa2OteIk*)zz#y!_KjSa)Yxr?o!#nV$ zSVd>hH|QvO3f+r#p$#a9Ldu`YapeQ$1?7HaPT8hhu4oD$`7!cYOjQeKIw zUxk}`S>j53NZMXoFm5pI#d|8YRSKRsyDPSGE|43OxG|!W8JikC7Y<1ik^O* zqu*f}i`y3KdusCr)fZfSaa7_a{K0Ro&s69ACT1YLR^sx+W=nmxX8KhDeZ(nkV|Csz zXR3=G%Kl-A8zFA{Yql|NnENX;M6J4s6D5h$_@HFp(=gi7+)(S-=48{e(gr0iOJXck zoy?vhJ>b+-GiQvY%CyxEkt#}@P6AIm)9^ep1&J#%`9h;^8m6z7Y+m9`jIJr=rCD+K>*Qhki*`S*un{?Fluqt#0Yy`5aE+!2_prA~))m zO)f&W8}ZVyf4W^|7Z4k+7fx4thwCXb5$D96Yq!^4nDXem8ge^{s4`o#U0IZO%qsgWR}|(ru8gka@Mic6{=ozA90s%*oia=yszQnP(_T3)jPYGmH%4V$^tLV~Otc+6N*hH&p7&U6EsgGHu92uP z!u#2(X$^-2jb{{9qG=Ya>inc>*;R9z(&&&PWO!YdUrVJJPc_AGZ zGPDq~&>BtLx&J9D>fHGxnO&>89Iq$nI69U&o)p%%CGG;p=&*a2HYy8op6sbgoqBc? zy35D3{37ZuU!l`zZ(F2w%~WDAOvkmlMj_gtvpGR3Ufq_~k*OU!%lIM(7vVgdf#2XX zoP;0XID8FXz{l_byi2a(>+mwX2#4TlI0z5J0k{`zFkvrjgPY)bxEij2OQ8(IP=MXA z6K;n-NC1W)`MO^uzX;FcWE78IDj?hZ&X_4lx{LIKZ&Tu)r|S zFh^0#GVEulQw;Sn%rHy`npC9GmtcyboMf0_7-tw`7-gt31csO)VyG~TFbp#cF$_`^ zWQG#M07Fp}I9?#o`nMk5SyoPQI1PWm1@tQ%M8}m2u#9d-pWrHb2WH_EHqk3^JGu%z zgATyAun}*C_h1ZP2CqUI4+2NHBZ$x86X?8h3Ju{;@mu&1vJpmi;s*!~Jft*ZVu^3B zGg-V+IVq0u-Aqs%W6~}zD6C716Fzs34GN-`v+@KC1QRiKDP>ZTqq;D=isD*?$>NMx6Lel}ieTUOxF)Xm$z0lyW9(ddi9?Qt#5hl>Lspq<4|&MY(~*pc zY3uLhcvD6_P00$IR+~oTNJ<>%`JPX8n9X>VS>zBKh5J=zD)(C~_Y@#VPOYtT%W6Uh N^wc1%{M76}`5)ZTZq)z) diff --git a/GTF/project/backend/config/server.js b/GTF/project/backend/config/server.js index 55da0418..9bc478b8 100644 --- a/GTF/project/backend/config/server.js +++ b/GTF/project/backend/config/server.js @@ -1,4 +1,4 @@ -const express = require('express'); +const express = require('express'); const cors = require('cors'); const sql = require('mssql'); require('dotenv').config(); @@ -6,7 +6,7 @@ require('dotenv').config(); const app = express(); const PORT = 3001; -// Configuration base de données +// Configuration base de données const dbConfig = { server: process.env.DB_SERVER, database: process.env.DB_DATABASE, @@ -26,20 +26,34 @@ app.use(cors({ })); app.use(express.json()); -// Log de toutes les requêtes +// Log de toutes les requêtes app.use((req, res, next) => { console.log(`${new Date().toISOString()} - ${req.method} ${req.path} depuis ${req.get('origin') || 'inconnu'}`); + if (req.query.user_email || req.query.email) { + console.log(` - Filtrage pour utilisateur: ${req.query.user_email || req.query.email}`); + } next(); }); -// Variable pour stocker la connexion +// Variable pour stocker la connexion et l'état de la migration let pool = null; +let systemStatus = { + hasFormateurEmailColumn: false, + hasFormateurView: false, + canAccessFormateurView: false, + hasFormateurLocal: false, + operatingMode: 'unknown' +}; -// Fonction pour se connecter à la base +// Fonction pour se connecter à la base async function connectDatabase() { try { pool = await sql.connect(dbConfig); - console.log('Base de données connectée'); + console.log('Base de données connectée'); + + // Diagnostic automatique de la structure et permissions + await checkSystemStatus(); + return true; } catch (error) { console.error('Erreur de connexion :', error.message); @@ -47,116 +61,345 @@ async function connectDatabase() { } } -// Route de test -app.get('/api/test', (req, res) => { - res.json({ - message: 'Le serveur fonctionne !', - timestamp: new Date().toISOString() - }); -}); - -// Route pour tester la base de données -app.get('/api/db-test', async (req, res) => { +// Fonction pour vérifier l'état complet du système +async function checkSystemStatus() { try { - if (!pool) { - return res.status(500).json({ error: 'Base non connectée' }); + // 1. Vérifier si la colonne formateur_email_fk existe + const columnCheck = await pool.request().query(` + SELECT COUNT(*) as count + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_NAME = 'declarations' + AND COLUMN_NAME = 'formateur_email_fk' + `); + systemStatus.hasFormateurEmailColumn = columnCheck.recordset[0].count > 0; + + // 2. Vérifier si la vue Formateurs existe + const viewCheck = await pool.request().query(` + SELECT COUNT(*) as count + FROM INFORMATION_SCHEMA.VIEWS + WHERE TABLE_NAME = 'Formateurs' + `); + systemStatus.hasFormateurView = viewCheck.recordset[0].count > 0; + + // 3. Tester l'accès à la vue Formateurs si elle existe + if (systemStatus.hasFormateurView) { + try { + await pool.request().query(`SELECT TOP 1 userPrincipalName FROM [dbo].[Formateurs]`); + systemStatus.canAccessFormateurView = true; + console.log('✅ Accès à la vue Formateurs: OK'); + } catch (error) { + systemStatus.canAccessFormateurView = false; + console.log('⌠Accès à la vue Formateurs: ERREUR -', error.message); + if (error.message.includes('HP-TO-O365')) { + console.log('💡 Problème de permissions sur la base HP-TO-O365'); + } + } + } + + // 4. Vérifier si la table formateurs_local existe et est accessible + try { + await pool.request().query(`SELECT TOP 1 * FROM formateurs_local`); + systemStatus.hasFormateurLocal = true; + console.log('✅ Table formateurs_local: OK'); + } catch (error) { + systemStatus.hasFormateurLocal = false; + console.log('⌠Table formateurs_local: non accessible'); + } + + // 5. Déterminer le mode de fonctionnement optimal + if (systemStatus.hasFormateurEmailColumn && systemStatus.canAccessFormateurView) { + systemStatus.operatingMode = 'new_with_view'; + } else if (systemStatus.hasFormateurEmailColumn && systemStatus.hasFormateurLocal) { + systemStatus.operatingMode = 'new_with_local'; + } else if (systemStatus.hasFormateurEmailColumn) { + systemStatus.operatingMode = 'new_email_only'; + } else { + systemStatus.operatingMode = 'legacy_hash'; + } + + console.log('📊 État du système:'); + console.log(` - Colonne formateur_email_fk: ${systemStatus.hasFormateurEmailColumn ? '✅' : 'âŒ'}`); + console.log(` - Vue Formateurs: ${systemStatus.hasFormateurView ? '✅' : 'âŒ'}`); + console.log(` - Accès vue Formateurs: ${systemStatus.canAccessFormateurView ? '✅' : 'âŒ'}`); + console.log(` - Table formateurs_local: ${systemStatus.hasFormateurLocal ? '✅' : 'âŒ'}`); + console.log(` - Mode de fonctionnement: ${systemStatus.operatingMode}`); + + } catch (error) { + console.error('Erreur lors du diagnostic:', error.message); + systemStatus.operatingMode = 'legacy_hash'; + } +} + +// Route de diagnostic complet +app.get('/api/diagnostic', async (req, res) => { + try { + await checkSystemStatus(); + + let recommendations = []; + + switch (systemStatus.operatingMode) { + case 'new_with_view': + recommendations.push('✅ Système optimal - toutes les fonctionnalités disponibles'); + break; + case 'new_with_local': + recommendations.push('âš ï¸ Fonctionne avec la table locale - pas d\'accès à la vue distante'); + recommendations.push('💡 Vérifier les permissions sur HP-TO-O365 pour utiliser la vue'); + break; + case 'new_email_only': + recommendations.push('âš ï¸ Mode dégradé - sauvegarde par email mais pas de détails formateurs'); + recommendations.push('💡 Restaurer l\'accès à la vue Formateurs ou table formateurs_local'); + break; + case 'legacy_hash': + recommendations.push('🔄 Mode compatibilité - utilise l\'ancien système de hash'); + recommendations.push('💡 Appliquer la migration avec POST /api/migrate'); + break; } - const result = await pool.request().query('SELECT COUNT(*) as total FROM FormateurSqy'); res.json({ - message: 'Base OK', - formateurs: result.recordset[0].total + systemStatus, + recommendations, + currentMode: systemStatus.operatingMode }); + } catch (error) { res.status(500).json({ error: error.message }); } }); -// Route pour lister les formateurs -app.get('/api/formateurs', async (req, res) => { +// Route pour appliquer la migration +app.post('/api/migrate', async (req, res) => { try { - const result = await pool.request().query(` - SELECT TOP 10 - NUMERO, - NOM_ENS as nom, - PRENOM_ENS as prenom, - EMAIL1 as email - FROM FormateurSqy - WHERE EMAIL1 IS NOT NULL - ORDER BY NOM_ENS - `); + const steps = []; + + // Étape 1: Ajouter la colonne si nécessaire + if (!systemStatus.hasFormateurEmailColumn) { + try { + await pool.request().query(` + ALTER TABLE [dbo].[declarations] + ADD [formateur_email_fk] [nvarchar](255) NULL + `); + steps.push('✅ Colonne formateur_email_fk ajoutée'); + } catch (error) { + if (!error.message.includes('already exists')) { + throw error; + } + steps.push('â„¹ï¸ Colonne formateur_email_fk déjà existante'); + } + } + + // Étape 2: Créer un index + try { + await pool.request().query(` + CREATE NONCLUSTERED INDEX [IX_declarations_formateur_email_fk] + ON [dbo].[declarations] ([formateur_email_fk]) + `); + steps.push('✅ Index créé'); + } catch (error) { + if (error.message.includes('already exists')) { + steps.push('â„¹ï¸ Index déjà existant'); + } else { + steps.push(`âš ï¸ Erreur index: ${error.message}`); + } + } + + // Vérifier à nouveau l'état + await checkSystemStatus(); res.json({ success: true, - data: result.recordset + steps, + newStatus: systemStatus, + message: `Migration appliquée - Mode: ${systemStatus.operatingMode}` }); + } catch (error) { res.status(500).json({ success: false, - error: error.message + error: error.message, + message: 'Erreur lors de la migration' }); } }); -// Route pour sauvegarder une déclaration +// Route de test +app.get('/api/test', (req, res) => { + res.json({ + message: 'Le serveur utilisateur fonctionne !', + timestamp: new Date().toISOString(), + systemStatus + }); +}); + +// Fonction pour générer un hash reproductible depuis un email (mode legacy) +function generateHashFromEmail(email) { + let hash = 0; + for (let i = 0; i < email.length; i++) { + const char = email.charCodeAt(i); + hash = ((hash << 5) - hash) + char; + hash = hash & hash; // Convert to 32-bit integer + } + return Math.abs(hash) % 10000 + 1000; +} + +// Route pour les formateurs (adaptée au mode de fonctionnement) +app.get('/api/formateurs', async (req, res) => { + try { + const { email, search } = req.query; + + switch (systemStatus.operatingMode) { + case 'new_with_view': + // Utiliser la vue Formateurs + let query = ` + SELECT + userPrincipalName as email, + displayName as nom_complet, + givenname as prenom, + surname as nom, + Campus, + departement, + Jobtitle + FROM [dbo].[Formateurs] + `; + + const request = pool.request(); + + if (email) { + query += ` WHERE userPrincipalName = @email`; + request.input('email', sql.NVarChar, email); + } else if (search) { + query += ` WHERE displayName LIKE @search OR userPrincipalName LIKE @search`; + request.input('search', sql.NVarChar, `%${search}%`); + } + + query += ` ORDER BY displayName`; + + const result = await request.query(query); + + res.json({ + success: true, + data: result.recordset, + source: 'vue_formateurs' + }); + break; + + case 'new_with_local': + // Utiliser la table formateurs_local + let localQuery = ` + SELECT + userPrincipalName as email, + displayName as nom_complet, + givenname as prenom, + surname as nom, + Campus, + departement, + Jobtitle + FROM formateurs_local + `; + + const localRequest = pool.request(); + + if (email) { + localQuery += ` WHERE userPrincipalName = @email`; + localRequest.input('email', sql.NVarChar, email); + } else if (search) { + localQuery += ` WHERE displayName LIKE @search OR userPrincipalName LIKE @search`; + localRequest.input('search', sql.NVarChar, `%${search}%`); + } + + localQuery += ` ORDER BY displayName`; + + const localResult = await localRequest.query(localQuery); + + res.json({ + success: true, + data: localResult.recordset, + source: 'table_locale' + }); + break; + + case 'new_email_only': + case 'legacy_hash': + default: + // Mode dégradé - réponse basique + if (email) { + res.json({ + success: true, + data: [{ + email: email, + nom_complet: email.split('@')[0] || 'Utilisateur', + prenom: 'Utilisateur', + nom: email.split('@')[0] || 'Inconnu', + source: 'fallback' + }] + }); + } else { + res.json({ + success: true, + data: [], + source: 'fallback' + }); + } + break; + } + + } catch (error) { + console.error('Erreur recherche formateurs:', error); + // Fallback en cas d'erreur + if (email) { + res.json({ + success: true, + data: [{ + email: email, + nom_complet: email.split('@')[0] || 'Utilisateur', + prenom: 'Utilisateur', + nom: email.split('@')[0] || 'Inconnu', + source: 'error_fallback' + }] + }); + } else { + res.status(500).json({ + success: false, + error: error.message + }); + } + } +}); + +// Conversion simple en string "HH:mm:ss" +const toSQLTime = (timeStr) => { + if (!timeStr) return null; + const [hours, minutes] = timeStr.split(':').map(Number); + const d = new Date(Date.UTC(2000, 0, 1, hours, minutes, 0)); + return d; +}; + +// Route pour sauvegarder une déclaration (ADAPTÉE AU MODE) app.post('/api/save_declaration', async (req, res) => { try { - const { date, activityType, hours, description, user } = req.body; + const { date, activityType, hours, description, user, startTime, endTime } = req.body; + console.log('Données reçues:', { date, activityType, hours, description, user, startTime, endTime }); - console.log('Données reçues:', { date, activityType, hours, description, user }); + const heureDebutSQL = toSQLTime(startTime); + const heureFinSQL = toSQLTime(endTime); - // Validation simple - if (!date || !activityType || !hours) { + // Validation + if (!date || !activityType || !hours || !startTime || !endTime) { return res.status(400).json({ success: false, - error: 'Données manquantes' + error: 'Données manquantes' }); } - let formateurNumero; - - if (user && user.email) { - // Chercher le formateur par email - const formateurResult = await pool.request() - .input('email', sql.VarChar, user.email) - .query('SELECT NUMERO FROM FormateurSqy WHERE EMAIL1 = @email'); - - if (formateurResult.recordset.length > 0) { - // Formateur trouvé - formateurNumero = formateurResult.recordset[0].NUMERO; - console.log('Formateur trouvé par email:', formateurNumero); - } else { - // Créer un nouveau formateur - const maxNumeroResult = await pool.request() - .query('SELECT ISNULL(MAX(NUMERO), 0) + 1 as nextNumero FROM FormateurSqy'); - - const nextNumero = maxNumeroResult.recordset[0].nextNumero; - - await pool.request() - .input('numero', sql.Int, nextNumero) - .input('nom', sql.VarChar, user.nom || 'Inconnu') - .input('prenom', sql.VarChar, user.prenom || 'Inconnu') - .input('email', sql.VarChar, user.email) - .input('department', sql.VarChar, user.department || 'Non défini') - .input('role', sql.VarChar, user.role || 'Employé') - .query(` - INSERT INTO FormateurSqy ( - NUMERO, NOM_ENS, PRENOM_ENS, EMAIL1, [Ecole - Pole], Contrat - ) VALUES ( - @numero, @nom, @prenom, @email, @department, @role - ) - `); - - formateurNumero = nextNumero; - console.log('Nouveau formateur créé:', formateurNumero); - } - } else { - // Fallback : utiliser un formateur par défaut - formateurNumero = 999; - console.log('Utilisation du formateur par défaut:', formateurNumero); + if (!user || !user.email) { + return res.status(400).json({ + success: false, + error: 'Email utilisateur requis' + }); } - // Récupérer l'ID du type de demande + const userEmail = user.email; + + // Récupération du type de demande const typeResult = await pool.request() .input('activityType', sql.VarChar, activityType) .query('SELECT id FROM types_demandes WHERE libelle = @activityType'); @@ -164,56 +407,140 @@ app.post('/api/save_declaration', async (req, res) => { if (typeResult.recordset.length === 0) { return res.status(400).json({ success: false, - error: `Type d'activité invalide: ${activityType}` + error: `Type d'activité invalide: ${activityType}` }); } const typeDemandeId = typeResult.recordset[0].id; - // Vérifier si une déclaration existe déjà pour cette date - const existingResult = await pool.request() - .input('formateurNumero', sql.Int, formateurNumero) - .input('date', sql.Date, date) - .query('SELECT id FROM declarations WHERE formateur_numero = @formateurNumero AND date = @date'); + // Logique selon le mode de fonctionnement + if (systemStatus.operatingMode.startsWith('new_')) { + // NOUVEAU SYSTÈME - par email + console.log(`💾 Sauvegarde avec nouveau système (email) - Mode: ${systemStatus.operatingMode}`); - if (existingResult.recordset.length > 0) { - // Mise à jour - await pool.request() - .input('utilisateurId', sql.Int, formateurNumero) - .input('typeDemandeId', sql.Int, typeDemandeId) - .input('hours', sql.Float, hours) - .input('description', sql.NVarChar, description || null) - .input('formateurNumero', sql.Int, formateurNumero) + // Vérifier si une déclaration existe déjà + const existingResult = await pool.request() + .input('formateurEmail', sql.NVarChar, userEmail) .input('date', sql.Date, date) - .query(` - UPDATE declarations - SET utilisateur_id = @utilisateurId, type_demande_id = @typeDemandeId, duree = @hours, description = @description - WHERE formateur_numero = @formateurNumero AND date = @date - `); + .query('SELECT id FROM declarations WHERE formateur_email_fk = @formateurEmail AND date = @date'); + + const utilisateurId = 1; // À adapter selon votre logique + + if (existingResult.recordset.length > 0) { + // UPDATE + await pool.request() + .input('formateurEmail', sql.NVarChar, userEmail) + .input('typeDemandeId', sql.Int, typeDemandeId) + .input('hours', sql.Float, hours) + .input('description', sql.NVarChar, description || null) + .input('date', sql.Date, date) + .input('heure_debut', sql.Time, heureDebutSQL) + .input('heure_fin', sql.Time, heureFinSQL) + .query(` + UPDATE declarations + SET type_demande_id = @typeDemandeId, + duree = @hours, + description = @description, + heure_debut = @heure_debut, + heure_fin = @heure_fin + WHERE formateur_email_fk = @formateurEmail AND date = @date + `); + console.log('✅ Déclaration mise à jour'); + } else { + // INSERT + await pool.request() + .input('utilisateurId', sql.Int, utilisateurId) + .input('formateurEmail', sql.NVarChar, userEmail) + .input('typeDemandeId', sql.Int, typeDemandeId) + .input('date', sql.Date, date) + .input('hours', sql.Float, hours) + .input('description', sql.NVarChar, description || null) + .input('heure_debut', sql.Time, heureDebutSQL) + .input('heure_fin', sql.Time, heureFinSQL) + .query(` + INSERT INTO declarations ( + utilisateur_id, formateur_email_fk, type_demande_id, + date, duree, description, heure_debut, heure_fin + ) + VALUES ( + @utilisateurId, @formateurEmail, @typeDemandeId, + @date, @hours, @description, @heure_debut, @heure_fin + ) + `); + console.log('✅ Nouvelle déclaration créée'); + } + + res.json({ + success: true, + message: `Déclaration sauvegardée avec succès (${systemStatus.operatingMode})`, + formateurEmail: userEmail + }); - console.log('Déclaration mise à jour'); } else { - // Création - await pool.request() - .input('utilisateurId', sql.Int, formateurNumero) + // ANCIEN SYSTÈME - par hash + console.log('💾 Sauvegarde avec ancien système (hash)'); + + const formateurNumero = generateHashFromEmail(userEmail); + + // Vérifier si une déclaration existe déjà + const existingResult = await pool.request() .input('formateurNumero', sql.Int, formateurNumero) - .input('typeDemandeId', sql.Int, typeDemandeId) .input('date', sql.Date, date) - .input('hours', sql.Float, hours) - .input('description', sql.NVarChar, description || null) - .query(` - INSERT INTO declarations (utilisateur_id, formateur_numero, type_demande_id, date, duree, description) - VALUES (@utilisateurId, @formateurNumero, @typeDemandeId, @date, @hours, @description) - `); + .query('SELECT id FROM declarations WHERE formateur_numero = @formateurNumero AND date = @date'); - console.log('Nouvelle déclaration créée'); + if (existingResult.recordset.length > 0) { + // UPDATE + await pool.request() + .input('utilisateurId', sql.Int, formateurNumero) + .input('typeDemandeId', sql.Int, typeDemandeId) + .input('hours', sql.Float, hours) + .input('description', sql.NVarChar, description || null) + .input('formateurNumero', sql.Int, formateurNumero) + .input('date', sql.Date, date) + .input('heure_debut', sql.Time, heureDebutSQL) + .input('heure_fin', sql.Time, heureFinSQL) + .query(` + UPDATE declarations + SET utilisateur_id = @utilisateurId, + type_demande_id = @typeDemandeId, + duree = @hours, + description = @description, + heure_debut = @heure_debut, + heure_fin = @heure_fin + WHERE formateur_numero = @formateurNumero AND date = @date + `); + console.log('✅ Déclaration mise à jour (legacy)'); + } else { + // INSERT + await pool.request() + .input('utilisateurId', sql.Int, formateurNumero) + .input('formateurNumero', sql.Int, formateurNumero) + .input('typeDemandeId', sql.Int, typeDemandeId) + .input('date', sql.Date, date) + .input('hours', sql.Float, hours) + .input('description', sql.NVarChar, description || null) + .input('heure_debut', sql.Time, heureDebutSQL) + .input('heure_fin', sql.Time, heureFinSQL) + .query(` + INSERT INTO declarations ( + utilisateur_id, formateur_numero, type_demande_id, + date, duree, description, heure_debut, heure_fin + ) + VALUES ( + @utilisateurId, @formateurNumero, @typeDemandeId, + @date, @hours, @description, @heure_debut, @heure_fin + ) + `); + console.log('✅ Nouvelle déclaration créée (legacy)'); + } + + res.json({ + success: true, + message: 'Déclaration sauvegardée avec succès (legacy)', + formateurNumero: formateurNumero + }); } - res.json({ - success: true, - message: 'Déclaration sauvegardée avec succès' - }); - } catch (error) { console.error('Erreur lors de la sauvegarde:', error); res.status(500).json({ @@ -223,60 +550,307 @@ app.post('/api/save_declaration', async (req, res) => { } }); -// Route pour récupérer les déclarations -app.get('/api/get_declarations', async (req, res) => { +// Route pour récupérer par email (ADAPTÉE AU MODE) +app.get('/api/get_declarations_by_email', async (req, res) => { try { - const result = await pool.request().query(` - SELECT - d.id, - d.formateur_numero as utilisateur_id, - td.id as type_demande_id, - d.date, - d.duree, - d.description, - d.formateur_numero, - td.libelle as activityType - FROM declarations d - INNER JOIN types_demandes td ON d.type_demande_id = td.id - ORDER BY d.date DESC - `); + const { email } = req.query; + console.log('DEBUG - Email reçu:', email); - res.json(result.recordset); + if (!email) { + return res.status(400).json({ error: 'Email requis' }); + } + + switch (systemStatus.operatingMode) { + case 'new_with_view': + // Avec vue Formateurs + const resultWithView = await pool.request() + .input('email', sql.NVarChar, email) + .query(` + SELECT + d.id, + d.utilisateur_id, + d.formateur_email_fk as formateur_email, + f.displayName as formateur_nom, + f.Campus, + f.departement, + td.id as type_demande_id, + td.libelle as activityType, + d.date, + d.duree, + d.heure_debut, + d.heure_fin, + d.description, + d.status + FROM declarations d + INNER JOIN types_demandes td ON d.type_demande_id = td.id + LEFT JOIN [dbo].[Formateurs] f ON d.formateur_email_fk = f.userPrincipalName + WHERE d.formateur_email_fk = @email + ORDER BY d.date DESC + `); + res.json(resultWithView.recordset); + break; + + case 'new_with_local': + // Avec table formateurs_local + const resultWithLocal = await pool.request() + .input('email', sql.NVarChar, email) + .query(` + SELECT + d.id, + d.utilisateur_id, + d.formateur_email_fk as formateur_email, + f.displayName as formateur_nom, + f.Campus, + f.departement, + td.id as type_demande_id, + td.libelle as activityType, + d.date, + d.duree, + d.heure_debut, + d.heure_fin, + d.description, + d.status + FROM declarations d + INNER JOIN types_demandes td ON d.type_demande_id = td.id + LEFT JOIN formateurs_local f ON d.formateur_email_fk = f.userPrincipalName + WHERE d.formateur_email_fk = @email + ORDER BY d.date DESC + `); + res.json(resultWithLocal.recordset); + break; + + case 'new_email_only': + // Sans jointure formateur + const resultEmailOnly = await pool.request() + .input('email', sql.NVarChar, email) + .query(` + SELECT + d.id, + d.utilisateur_id, + d.formateur_email_fk as formateur_email, + td.id as type_demande_id, + td.libelle as activityType, + d.date, + d.duree, + d.heure_debut, + d.heure_fin, + d.description, + d.status + FROM declarations d + INNER JOIN types_demandes td ON d.type_demande_id = td.id + WHERE d.formateur_email_fk = @email + ORDER BY d.date DESC + `); + res.json(resultEmailOnly.recordset); + break; + + case 'legacy_hash': + default: + // Ancien système avec hash + const formateurNumero = generateHashFromEmail(email); + console.log(`Email ${email} -> Numéro ${formateurNumero}`); + + const resultLegacy = await pool.request() + .input('formateur_numero', sql.Int, formateurNumero) + .query(` + SELECT + d.id, + d.formateur_numero as utilisateur_id, + td.id as type_demande_id, + d.date, + d.duree, + d.heure_debut, + d.heure_fin, + d.description, + td.libelle as activityType, + d.status + FROM declarations d + INNER JOIN types_demandes td ON d.type_demande_id = td.id + WHERE d.formateur_numero = @formateur_numero + ORDER BY d.date DESC + `); + res.json(resultLegacy.recordset); + break; + } + + console.log(`✅ Déclarations récupérées pour ${email} (mode: ${systemStatus.operatingMode})`); } catch (error) { - console.error('Erreur lors de la récupération:', error); + console.error('Erreur lors de la récupération par email:', error.message); res.status(500).json({ error: error.message }); } }); -// Démarrage du serveur +// Route pour récupérer toutes les déclarations +app.get('/api/get_declarations', async (req, res) => { + try { + const { user_email, admin } = req.query; + + // Utiliser la même logique que pour get_declarations_by_email + if (user_email && !admin) { + // Rediriger vers get_declarations_by_email + req.query = { email: user_email }; + return app._router.handle(req, res); + } + + // Mode admin - récupérer toutes les déclarations + let result; + switch (systemStatus.operatingMode) { + case 'new_with_view': + result = await pool.request().query(` + SELECT + d.id, + d.utilisateur_id, + d.formateur_email_fk as formateur_email, + f.displayName as formateur_nom, + f.Campus, + f.departement, + td.id as type_demande_id, + td.libelle as activityType, + d.date, + d.duree, + d.heure_debut, + d.heure_fin, + d.description, + d.status + FROM declarations d + INNER JOIN types_demandes td ON d.type_demande_id = td.id + LEFT JOIN [dbo].[Formateurs] f ON d.formateur_email_fk = f.userPrincipalName + ORDER BY d.date DESC + `); + break; + + case 'new_with_local': + result = await pool.request().query(` + SELECT + d.id, + d.utilisateur_id, + d.formateur_email_fk as formateur_email, + f.displayName as formateur_nom, + f.Campus, + f.departement, + td.id as type_demande_id, + td.libelle as activityType, + d.date, + d.duree, + d.heure_debut, + d.heure_fin, + d.description, + d.status + FROM declarations d + INNER JOIN types_demandes td ON d.type_demande_id = td.id + LEFT JOIN formateurs_local f ON d.formateur_email_fk = f.userPrincipalName + ORDER BY d.date DESC + `); + break; + + case 'new_email_only': + result = await pool.request().query(` + SELECT + d.id, + d.utilisateur_id, + d.formateur_email_fk as formateur_email, + td.id as type_demande_id, + td.libelle as activityType, + d.date, + d.duree, + d.heure_debut, + d.heure_fin, + d.description, + d.status + FROM declarations d + INNER JOIN types_demandes td ON d.type_demande_id = td.id + ORDER BY d.date DESC + `); + break; + + default: + result = await pool.request().query(` + SELECT + d.id, + d.formateur_numero as utilisateur_id, + td.id as type_demande_id, + d.date, + d.duree, + d.heure_debut, + d.heure_fin, + d.description, + d.formateur_numero, + td.libelle as activityType, + d.status + FROM declarations d + INNER JOIN types_demandes td ON d.type_demande_id = td.id + ORDER BY d.date DESC + `); + break; + } + + res.json(result.recordset); + + } catch (error) { + console.error('Erreur lors de la récupération:', error); + res.status(500).json({ error: error.message }); + } +}); + +// Route de création de formateur (pour compatibilité) +app.post('/api/create_formateur', (req, res) => { + res.json({ + success: true, + message: 'Route de compatibilité - utilisez /api/formateurs' + }); +}); + +// Démarrage du serveur async function startServer() { const dbConnected = await connectDatabase(); if (!dbConnected) { - console.log('Impossible de démarrer sans base de données'); + console.log('Impossible de démarrer sans base de données'); return; } app.listen(PORT, () => { - console.log(`Serveur démarré sur http://localhost:${PORT}`); + console.log(`🚀 Serveur UTILISATEUR démarré sur http://localhost:${PORT}`); + console.log(`📊 Mode de fonctionnement: ${systemStatus.operatingMode}`); + console.log(''); console.log('Routes disponibles :'); + console.log('- GET /api/diagnostic (vérifier l\'état)'); + console.log('- POST /api/migrate (appliquer la migration)'); console.log('- GET /api/test'); - console.log('- GET /api/db-test'); - console.log('- GET /api/formateurs'); + console.log('- GET /api/formateurs?email=test@example.com'); console.log('- POST /api/save_declaration'); console.log('- GET /api/get_declarations'); + console.log('- GET /api/get_declarations_by_email?email=test@example.com'); + console.log(''); + + switch (systemStatus.operatingMode) { + case 'new_with_view': + console.log('✅ Système optimal - utilise la vue Formateurs'); + break; + case 'new_with_local': + console.log('âš ï¸ Mode dégradé - utilise la table formateurs_local'); + console.log('💡 Conseil: Vérifier les permissions sur HP-TO-O365'); + break; + case 'new_email_only': + console.log('âš ï¸ Mode minimal - sauvegarde par email sans détails formateurs'); + break; + case 'legacy_hash': + console.log('🔄 Mode compatibilité - utilise l\'ancien système de hash'); + console.log('💡 Conseil: Appliquer la migration avec POST /api/migrate'); + break; + } }); } -// Arrêt propre +// Arrêt propre process.on('SIGINT', async () => { - console.log('Arrêt du serveur...'); + console.log('Arrêt du serveur utilisateur...'); if (pool) { await pool.close(); } process.exit(0); }); -// Démarrer -startServer(); \ No newline at end of file +// Démarrer +startServer() \ No newline at end of file diff --git a/GTF/project/node_modules/.vite/deps/_metadata.json b/GTF/project/node_modules/.vite/deps/_metadata.json index bbf56146..51ba413a 100644 --- a/GTF/project/node_modules/.vite/deps/_metadata.json +++ b/GTF/project/node_modules/.vite/deps/_metadata.json @@ -1,43 +1,43 @@ { - "hash": "1e9c2bee", - "configHash": "451161ff", + "hash": "5aba48b5", + "configHash": "2088881b", "lockfileHash": "b1ec30a4", - "browserHash": "57e609ed", + "browserHash": "ac0e96b1", "optimized": { "react": { "src": "../../react/index.js", "file": "react.js", - "fileHash": "4348729b", + "fileHash": "9a2d9b21", "needsInterop": true }, "react/jsx-dev-runtime": { "src": "../../react/jsx-dev-runtime.js", "file": "react_jsx-dev-runtime.js", - "fileHash": "9694e297", + "fileHash": "d21e6b25", "needsInterop": true }, "react/jsx-runtime": { "src": "../../react/jsx-runtime.js", "file": "react_jsx-runtime.js", - "fileHash": "4fa96245", + "fileHash": "286c86a9", "needsInterop": true }, "@azure/msal-browser": { "src": "../../@azure/msal-browser/dist/index.mjs", "file": "@azure_msal-browser.js", - "fileHash": "8be31f82", + "fileHash": "90396c8f", "needsInterop": false }, "react-dom/client": { "src": "../../react-dom/client.js", "file": "react-dom_client.js", - "fileHash": "8aa9b52d", + "fileHash": "a16fb88b", "needsInterop": true }, "react-router-dom": { "src": "../../../../node_modules/react-router-dom/dist/index.mjs", "file": "react-router-dom.js", - "fileHash": "f4ac1124", + "fileHash": "a93f0753", "needsInterop": false } }, diff --git a/GTF/project/src/App.tsx b/GTF/project/src/App.tsx index 33527dc9..f7e9f7ab 100644 --- a/GTF/project/src/App.tsx +++ b/GTF/project/src/App.tsx @@ -13,41 +13,57 @@ function App() { const [showProfile, setShowProfile] = useState(false); const [loading, setLoading] = useState(true); - // Fonction pour charger les déclarations depuis la base - const loadDeclarations = async () => { - try { - console.log('Chargement des déclarations...'); - const response = await fetch('http://localhost:3001/api/get_declarations'); + // Fonction pour charger les déclarations filtrées par utilisateur + const loadDeclarations = async () => { + try { + console.log('Chargement des déclarations...'); + + // Vérifier si l'utilisateur est connecté + if (!user?.email) { + console.log('Aucun utilisateur connecté'); + setTimeEntries([]); + setLoading(false); + return; + } - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } + // CORRECTION : Utiliser l'email au lieu de l'ID + const response = await fetch(`http://localhost:3001/api/get_declarations_by_email?email=${encodeURIComponent(user.email)}`); - const data = await response.json(); - console.log('Déclarations reçues:', data); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } - // Convertir les données du serveur au format TimeEntry - const entries: TimeEntry[] = data.map((d: any) => ({ - date: d.date.split('T')[0], // Convertir la date SQL en format YYYY-MM-DD - activityType: d.activityType, - hours: d.duree, - description: d.description || '', - createdAt: new Date(d.date) - })); + const data = await response.json(); + console.log('Déclarations reçues pour l\'email', user.email, ':', data); - setTimeEntries(entries); - console.log('Déclarations converties:', entries); - } catch (error) { - console.error('Erreur lors du chargement des déclarations:', error); - } finally { + // Convertir les données du serveur au format TimeEntry + const entries: TimeEntry[] = data.map((d: any) => ({ + date: d.date.split('T')[0], // Convertir la date SQL en format YYYY-MM-DD + activityType: d.activityType, + hours: d.duree, + description: d.description || '', + createdAt: new Date(d.date) + })); + + setTimeEntries(entries); + console.log('Déclarations converties pour l\'utilisateur:', entries); + } catch (error) { + console.error('Erreur lors du chargement des déclarations:', error); + setTimeEntries([]); + } finally { + setLoading(false); + } +}; + + // Recharger quand l'utilisateur change + useEffect(() => { + if (user?.email) { + loadDeclarations(); + } else { + setTimeEntries([]); setLoading(false); } - }; - - // Charger les déclarations au démarrage - useEffect(() => { - loadDeclarations(); - }, []); + }, [user?.email]); const handleDateClick = (date: string) => setSelectedDate(date); const handleCloseModal = () => setSelectedDate(null); @@ -82,6 +98,17 @@ function App() { .slice(0, 3); }; + // Vérification si utilisateur connecté + if (!user) { + return ( +
+
+
Chargement de la session...
+
+
+ ); + } + return (
{/* Header */} @@ -94,7 +121,9 @@ function App() {

GTF

-

Formateurs - Suivi mensuel

+

+ Formateurs - Suivi mensuel • {user.nom} {user.prenom} +

@@ -136,15 +165,11 @@ function App() { {/* Mobile Profile */} {showProfile && (
- {user ? ( - - ) : ( -
Chargement du profil...
- )} +
)} @@ -161,6 +186,7 @@ function App() { )} @@ -169,15 +195,11 @@ function App() {
{/* Desktop Profile */}
- {user ? ( - - ) : ( -
Chargement du profil...
- )} +
{/* Quick Actions */} @@ -201,6 +223,7 @@ function App() {
  • • Lundi à vendredi uniquement
  • • Description optionnelle
  • +
@@ -249,7 +272,9 @@ function App() { {!loading && timeEntries.length === 0 && (

Aucune déclaration

-

Commencez par déclarer vos premières heures

+

+ Commencez par déclarer vos premières heures pour {user.prenom} +

diff --git a/GTF/project/src/components/Calendar.tsx b/GTF/project/src/components/Calendar.tsx index 623f4930..2ee34089 100644 --- a/GTF/project/src/components/Calendar.tsx +++ b/GTF/project/src/components/Calendar.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react'; -import { ChevronLeft, ChevronRight, Clock, Plus } from 'lucide-react'; +import { ChevronLeft, ChevronRight, Clock } from 'lucide-react'; import { TimeEntry } from '../types/TimeEntry'; interface Declaration { @@ -14,6 +14,7 @@ interface Declaration { interface CalendarProps { onDateClick: (date: string) => void; getEntryForDate: (date: string) => TimeEntry | undefined; + currentUserId?: number; } const activityTypeColors = { @@ -45,14 +46,14 @@ const Calendar: React.FC = ({ onDateClick, getEntryForDate }) => .catch((err) => console.error("Erreur fetch:", err)); }, []); - // Fonction de sauvegarde d'une déclaration (à utiliser selon besoin) + // Fonction de sauvegarde d'une déclaration const saveDeclaration = async (newDeclaration: Declaration) => { const res = await fetch("http://localhost:3001/api/get_declarations", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(newDeclaration), }); - // Traiter le retour selon besoin + }; const getMonthData = () => { @@ -174,22 +175,20 @@ const Calendar: React.FC = ({ onDateClick, getEntryForDate }) => return ( - - {/* Error Messages */} - {errors.length > 0 && ( -
-
- -
-

Erreurs de validation

-
    - {errors.map((error, index) => ( -
  • {error}
  • - ))} -
-
-
-
- )} - {/* Form */} -
- {/* Type d'activité */} -
- -
- {(['preparation', 'correction'] as const).map((type) => { - const color = getActivityTypeColor(type); - const isSelected = activityType === type; - return ( -