From f27de28bb44158840cb7837443d00b729c82a41c Mon Sep 17 00:00:00 2001 From: odentas Date: Sun, 12 Oct 2025 17:05:46 +0200 Subject: [PATCH] Initial commit --- .env.local.bak | 41 + .env.local.bak2 | 41 + .gitignore | 53 +- .vscode/settings.json | 3 + BULK_EMAIL_PROGRESS_MODAL.md | 73 + DEBUG_EMAIL_LOGS.md | 78 + DEPLOYMENT.md | 113 + DOCUSEAL_ENV_VARIABLES.md | 2 +- EMAIL_TEMPLATE_SYSTEM.md | 2 +- FIX_EMAIL_SES_ERROR.md | 114 + FIX_OTP_EMAIL_LINKS.md | 73 + GUIDE_ERREURS_EMAILS.md | 114 + GUIDE_MODE_DEMO.md | 62 + MAINTENANCE_PUBLIC_PAGES.md | 57 + NOTES_CREATION_AUTOMATIQUES.md | 46 + OPTIMISATION_DEBIT_SES.md | 84 + STAFF_MAINTENANCE_ACCESS_GUIDE.md | 103 + app/(app)/compte/securite/page.tsx | 25 +- app/(app)/contrats-multi/[id]/page.tsx | 84 +- app/(app)/contrats-rg/[id]/page.tsx | 84 +- app/(app)/contrats/ContratsClient.tsx | 54 +- .../contrats/[id]/edit/formulaire/page.tsx | 4 +- app/(app)/contrats/[id]/edit/page.tsx | 53 +- app/(app)/contrats/[id]/modification/page.tsx | 14 +- app/(app)/contrats/[id]/page.tsx | 228 +- app/(app)/contrats/nouveau/page.tsx | 3 + .../contrats/nouveau/saisie-tableau/page.tsx | 509 +++- app/(app)/cotisations/page.tsx | 29 +- app/(app)/debug/page.tsx | 5 + app/(app)/facturation/page.tsx | 29 +- app/(app)/informations/page.tsx | 34 +- app/(app)/layout.tsx | 59 +- app/(app)/page.tsx | 42 +- app/(app)/salaries/[matricule]/page.tsx | 154 +- app/(app)/salaries/nouveau/page.tsx | 75 +- app/(app)/salaries/page.tsx | 159 +- app/(app)/signatures-electroniques/page.tsx | 72 +- app/(app)/staff/clients/[id]/page.tsx | 40 +- app/(app)/staff/clients/nouveau/page.tsx | 12 +- app/(app)/staff/clients/page.tsx | 8 +- app/(app)/staff/contrats/page.tsx | 4 +- .../staff/email-logs/EmailLogsClient.tsx | 438 ++++ app/(app)/staff/email-logs/page.tsx | 102 + app/(app)/staff/emails-groupes/page.tsx | 126 + app/(app)/staff/facturation/[id]/page.tsx | 108 +- app/(app)/staff/facturation/create/page.tsx | 42 +- app/(app)/staff/facturation/page.tsx | 153 +- app/(app)/staff/salaries/page.tsx | 2 +- app/(app)/staff/tickets/[id]/page.tsx | 6 +- app/(app)/staff/tickets/nouveau/page.tsx | 5 + app/(app)/staff/tickets/page.tsx | 11 +- app/(app)/staff/utilisateurs/nouveau/page.tsx | 5 + app/(app)/staff/utilisateurs/page-old.tsx | 12 +- app/(app)/staff/utilisateurs/page.tsx | 17 +- app/(app)/staff/virements-salaires/page.tsx | 7 +- app/(app)/support/[id]/page.tsx | 17 +- app/(app)/support/page.tsx | 75 +- app/(app)/virements-salaires/page.tsx | 218 +- app/(app)/vos-acces/page.tsx | 23 +- app/(app)/vos-documents/page.tsx | 803 +++---- app/activate/page.tsx | 3 + app/api/access/route.ts | 2 +- app/api/auth/send-code/route.ts | 26 +- .../auto-declaration/generate-token/route.ts | 132 + app/api/auto-declaration/route.ts | 199 ++ app/api/auto-declaration/upload/route.ts | 139 ++ app/api/cddu-contracts/route.ts | 490 +++- app/api/contrats/[id]/generate-pdf/route.ts | 31 +- app/api/contrats/[id]/pdf-url/route.ts | 28 +- app/api/contrats/[id]/route.ts | 112 +- app/api/contrats/generate-batch-pdf/route.ts | 127 + app/api/contrats/route.ts | 52 +- app/api/debug-documents/route.ts | 32 + app/api/debug-env/route.ts | 75 + app/api/debug-s3/route.ts | 61 + app/api/debug/pdf-diagnosis/route.ts | 106 + app/api/debug/url-detection/route.ts | 28 + app/api/dl-contrat-signe/route.ts | 221 ++ app/api/documents/route.ts | 144 +- app/api/docuseal-signature/route.ts | 43 +- app/api/facturation/route.ts | 2 +- app/api/me/role/route.ts | 15 + app/api/me/route.ts | 23 +- app/api/organizations/route.ts | 16 +- app/api/professions-feminisations/route.ts | 113 +- app/api/rg-contracts/route.ts | 84 +- app/api/s3-presigned/route.ts | 2 +- app/api/salaries/route.ts | 35 +- app/api/search/route.ts | 248 +- .../signatures-electroniques/relance/route.ts | 105 +- app/api/staff/bulk-email-stream/route.ts | 348 +++ app/api/staff/bulk-email/route.ts | 191 ++ .../staff/contracts/bulk-update-dpae/route.ts | 45 + .../contracts/bulk-update-salary/route.ts | 57 + app/api/staff/contracts/search/route.ts | 70 +- .../staff/email-logs/[id]/content/route.ts | 76 + app/api/staff/email-logs/route.ts | 80 + app/api/staff/facturation/[id]/route.ts | 4 +- .../facturation/[id]/upload-pdf/route.ts | 4 +- .../staff/facturation/[id]/view-pdf/route.ts | 2 +- app/api/staff/facturation/route.ts | 2 +- app/api/staff/facturation/upload-pdf/route.ts | 2 +- app/api/staff/users/invite/route.ts | 17 +- app/api/upload-s3/route.ts | 2 +- app/api/virements-salaires/route.ts | 139 +- app/auth/callback/route.ts | 16 +- app/auto-declaration/page.tsx | 790 ++++++ app/debug-signatures/page.tsx | 3 + .../ContractViewerContent.tsx | 130 + app/dl-contrat-signe/layout.tsx | 8 + app/dl-contrat-signe/page.tsx | 21 + app/globals.css | 14 +- app/layout.tsx | 16 +- app/maintenance/MaintenancePage.tsx | 58 +- app/maintenance/page.tsx | 5 + app/set-password/SetPasswordContent.tsx | 18 +- app/set-password/page.tsx | 4 +- app/signin/page-old.tsx | 2 +- app/signin/page.tsx | 37 +- components/AccessDeniedCard.tsx | 8 +- components/BugReporter.tsx | 16 +- components/ConfirmableForm.tsx | 6 +- components/DemoBanner.tsx | 61 + components/GlobalSearchOverlay.tsx | 15 +- components/Header.tsx | 50 +- components/LogoutButton.tsx | 31 +- components/MessageCard.tsx | 32 +- components/MobileSidebarOverlay.tsx | 46 + components/NotesSection.tsx | 22 +- components/OrganizationSwitcher.tsx | 2 +- components/PageTitle.tsx | 23 + components/Providers.tsx | 2 + components/Sidebar.tsx | 193 +- components/SidebarMenu.tsx | 6 +- components/StatusEditModal.tsx | 64 +- components/TicketConversation.tsx | 18 +- components/TicketTimeline.tsx | 12 +- components/auth/MfaSetupComponent.tsx | 32 +- components/contrats/NouveauCDDUForm.tsx | 171 +- .../contrats/NouveauCDDUForm.tsx.backup | 2129 +++++++++++++++++ components/staff/BulkEmailForm.tsx | 572 +++++ components/staff/BulkEmailProgressModal.tsx | 265 ++ components/staff/BulkPdfProgressModal.tsx | 187 ++ components/staff/ContractSlide.tsx | 2 +- components/staff/ContractsGrid.tsx | 858 ++++++- components/staff/EmailLogDetailModal.tsx | 411 ++++ components/staff/InviteForm.tsx | 4 +- components/staff/NewStaffTicketForm.tsx | 8 +- components/staff/PdfVerificationModal.tsx | 304 +++ components/staff/SalarieModal.tsx | 112 +- components/staff/SalariesGrid.tsx | 8 +- components/staff/SalariesGridSimple.tsx | 82 +- components/staff/SalaryTransfersGrid.tsx | 28 +- components/staff/StaffTicketActions.tsx | 6 +- components/staff/contracts/ContractEditor.tsx | 7 +- components/ui/badge.tsx | 24 + components/ui/card.tsx | 4 +- components/ui/confirmation-modal.tsx | 10 +- components/ui/label.tsx | 2 +- components/ui/loading-modal.tsx | 26 +- components/ui/select.tsx | 117 + components/ui/separator.tsx | 2 +- components/ui/tooltip.tsx | 110 + docs/DEMO_SYSTEM.md | 262 ++ hooks/useContrats.ts | 112 + hooks/useDemoData.ts | 80 + hooks/useDemoMode.tsx | 66 + hooks/usePageTitle.ts | 8 + lib/aws-s3.ts | 26 +- lib/cleanEnv.ts | 52 + lib/demo-data.ts | 420 ++++ lib/demo-detector.ts | 93 + lib/emailLoggingService.ts | 445 ++++ lib/emailMigrationHelpers.ts | 10 +- lib/emailService.ts | 185 +- lib/emailTemplateService.ts | 150 +- middleware.ts | 61 +- public/avatar-renaud.png | Bin 0 -> 1070732 bytes public/favicon.ico | Bin 0 -> 12734 bytes public/favicon.png | Bin 0 -> 12734 bytes public/manifest.json | 17 + public/odentas-logo-signature.png | Bin 0 -> 32641 bytes public/odentas-logo.png | Bin 0 -> 232946 bytes scripts/check-feminisations-table.mjs | 128 + scripts/migrate-feminisations.mjs | 95 + scripts/test-demo-domains.sh | 100 + styles/cmdk.css | 314 ++- styles/cmdk.css.backup | 226 ++ .../0003_professions_feminisations.sql | 48 + .../migrations/0004_search_index_policies.sql | 23 + supabase/migrations/001_create_email_logs.sql | 240 ++ .../migrations/002_update_email_logs_safe.sql | 247 ++ .../003_add_html_content_to_email_logs.sql | 8 + ...7000000_create_auto_declaration_tokens.sql | 45 + .../migrations/20251009_documents_rls.sql | 128 + supabase/migrations/alternative_policy.sql | 23 + supabase/migrations/debug_auth_uid.sql | 32 + supabase/migrations/debug_disable_rls.sql | 9 + supabase/migrations/diagnostic_email_logs.sql | 39 + supabase/migrations/final_rls_fix.sql | 48 + supabase/migrations/fix_rls_policies.sql | 39 + supabase/migrations/fix_staff_access.sql | 0 supabase/migrations/temporary_policy_fix.sql | 23 + templates-mails/facture.html | 473 +--- templates-mails/facture.html.backup | 352 +++ .../invitation-auto-declaration.html | 200 ++ templates-mails/universal-template.html | 4 +- tmp/rendered-email.html | 72 + tmp/rendered-employee-created.html | 101 + tmp/send-preview.html | 95 + tsconfig.tsbuildinfo | 1 + vercel.json | 37 + 212 files changed, 18207 insertions(+), 2882 deletions(-) create mode 100644 .env.local.bak create mode 100644 .env.local.bak2 create mode 100644 .vscode/settings.json create mode 100644 BULK_EMAIL_PROGRESS_MODAL.md create mode 100644 DEBUG_EMAIL_LOGS.md create mode 100644 DEPLOYMENT.md create mode 100644 FIX_EMAIL_SES_ERROR.md create mode 100644 FIX_OTP_EMAIL_LINKS.md create mode 100644 GUIDE_ERREURS_EMAILS.md create mode 100644 GUIDE_MODE_DEMO.md create mode 100644 MAINTENANCE_PUBLIC_PAGES.md create mode 100644 NOTES_CREATION_AUTOMATIQUES.md create mode 100644 OPTIMISATION_DEBIT_SES.md create mode 100644 STAFF_MAINTENANCE_ACCESS_GUIDE.md create mode 100644 app/(app)/staff/email-logs/EmailLogsClient.tsx create mode 100644 app/(app)/staff/email-logs/page.tsx create mode 100644 app/(app)/staff/emails-groupes/page.tsx create mode 100644 app/api/auto-declaration/generate-token/route.ts create mode 100644 app/api/auto-declaration/route.ts create mode 100644 app/api/auto-declaration/upload/route.ts create mode 100644 app/api/contrats/generate-batch-pdf/route.ts create mode 100644 app/api/debug-documents/route.ts create mode 100644 app/api/debug-env/route.ts create mode 100644 app/api/debug-s3/route.ts create mode 100644 app/api/debug/pdf-diagnosis/route.ts create mode 100644 app/api/debug/url-detection/route.ts create mode 100644 app/api/dl-contrat-signe/route.ts create mode 100644 app/api/staff/bulk-email-stream/route.ts create mode 100644 app/api/staff/bulk-email/route.ts create mode 100644 app/api/staff/contracts/bulk-update-dpae/route.ts create mode 100644 app/api/staff/contracts/bulk-update-salary/route.ts create mode 100644 app/api/staff/email-logs/[id]/content/route.ts create mode 100644 app/api/staff/email-logs/route.ts create mode 100644 app/auto-declaration/page.tsx create mode 100644 app/dl-contrat-signe/ContractViewerContent.tsx create mode 100644 app/dl-contrat-signe/layout.tsx create mode 100644 app/dl-contrat-signe/page.tsx create mode 100644 components/DemoBanner.tsx create mode 100644 components/MobileSidebarOverlay.tsx create mode 100644 components/PageTitle.tsx create mode 100644 components/contrats/NouveauCDDUForm.tsx.backup create mode 100644 components/staff/BulkEmailForm.tsx create mode 100644 components/staff/BulkEmailProgressModal.tsx create mode 100644 components/staff/BulkPdfProgressModal.tsx create mode 100644 components/staff/EmailLogDetailModal.tsx create mode 100644 components/staff/PdfVerificationModal.tsx create mode 100644 components/ui/badge.tsx create mode 100644 components/ui/select.tsx create mode 100644 components/ui/tooltip.tsx create mode 100644 docs/DEMO_SYSTEM.md create mode 100644 hooks/useDemoData.ts create mode 100644 hooks/useDemoMode.tsx create mode 100644 hooks/usePageTitle.ts create mode 100644 lib/cleanEnv.ts create mode 100644 lib/demo-data.ts create mode 100644 lib/demo-detector.ts create mode 100644 lib/emailLoggingService.ts create mode 100644 public/avatar-renaud.png create mode 100644 public/favicon.ico create mode 100644 public/favicon.png create mode 100644 public/manifest.json create mode 100644 public/odentas-logo-signature.png create mode 100644 public/odentas-logo.png create mode 100755 scripts/check-feminisations-table.mjs create mode 100755 scripts/migrate-feminisations.mjs create mode 100755 scripts/test-demo-domains.sh create mode 100644 styles/cmdk.css.backup create mode 100644 supabase/migrations/0003_professions_feminisations.sql create mode 100644 supabase/migrations/0004_search_index_policies.sql create mode 100644 supabase/migrations/001_create_email_logs.sql create mode 100644 supabase/migrations/002_update_email_logs_safe.sql create mode 100644 supabase/migrations/003_add_html_content_to_email_logs.sql create mode 100644 supabase/migrations/20241007000000_create_auto_declaration_tokens.sql create mode 100644 supabase/migrations/20251009_documents_rls.sql create mode 100644 supabase/migrations/alternative_policy.sql create mode 100644 supabase/migrations/debug_auth_uid.sql create mode 100644 supabase/migrations/debug_disable_rls.sql create mode 100644 supabase/migrations/diagnostic_email_logs.sql create mode 100644 supabase/migrations/final_rls_fix.sql create mode 100644 supabase/migrations/fix_rls_policies.sql create mode 100644 supabase/migrations/fix_staff_access.sql create mode 100644 supabase/migrations/temporary_policy_fix.sql create mode 100644 templates-mails/facture.html.backup create mode 100644 templates-mails/invitation-auto-declaration.html create mode 100644 tmp/rendered-email.html create mode 100644 tmp/rendered-employee-created.html create mode 100644 tmp/send-preview.html create mode 100644 tsconfig.tsbuildinfo create mode 100644 vercel.json diff --git a/.env.local.bak b/.env.local.bak new file mode 100644 index 0000000..24c0999 --- /dev/null +++ b/.env.local.bak @@ -0,0 +1,41 @@ +# Created by Vercel CLI +# Created by Vercel CLI +VERCEL_OIDC_TOKEN="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im1yay00MzAyZWMxYjY3MGY0OGE5OGFkNjFkYWRlNGEyM2JlNyJ9.eyJpc3MiOiJodHRwczovL29pZGMudmVyY2VsLmNvbS9vZGVudGFzLXByb2plY3RzLTE2ZjRlYWZiIiwic3ViIjoib3duZXI6b2RlbnRhcy1wcm9qZWN0cy0xNmY0ZWFmYjpwcm9qZWN0Om9kZW50YXMtZXNwYWNlLXBhaWU6ZW52aXJvbm1lbnQ6ZGV2ZWxvcG1lbnQiLCJzY29wZSI6Im93bmVyOm9kZW50YXMtcHJvamVjdHMtMTZmNGVhZmI6cHJvamVjdDpvZGVudGFzLWVzcGFjZS1wYWllOmVudmlyb25tZW50OmRldmVsb3BtZW50IiwiYXVkIjoiaHR0cHM6Ly92ZXJjZWwuY29tL29kZW50YXMtcHJvamVjdHMtMTZmNGVhZmIiLCJvd25lciI6Im9kZW50YXMtcHJvamVjdHMtMTZmNGVhZmIiLCJvd25lcl9pZCI6InRlYW1fYkw2RkdOUkc0dFB5VDBmOGhqMFFUdVZuIiwicHJvamVjdCI6Im9kZW50YXMtZXNwYWNlLXBhaWUiLCJwcm9qZWN0X2lkIjoicHJqX3RPNGJObzQ0cldiYkk2UkhENGRZNDlSTEZ4b2EiLCJlbnZpcm9ubWVudCI6ImRldmVsb3BtZW50IiwidXNlcl9pZCI6Ill0QU5YTkhGMVVtcHRUa0llTVB5QlJKdCIsIm5iZiI6MTc1ODExMDE1OSwiaWF0IjoxNzU4MTEwMTU5LCJleHAiOjE3NTgxNTMzNTl9.b9OEMCf8-S6bgJBF8-IEvaZrzo9mChJCis0lbq7HsUB99cGVUUlWaLYe2o5zw3eYYO5rNVXcZ5wlNvNqEAjkaelcFBexE6Qx1yFh9QB9xGsTYltfVio2PxyWFKxsHVcNYf3khOHjHutm84LIoYIx6mxXYr96sfJsilpamw7bN161zYBuu2E4T5RlLhQ6r1DRCTMyvD-R7pLdtyHgpq8VsLzOEsvvCfO92pI57kLveen_itHMpIH7An3J7BjijkSluYWHRg4PkzrCti_3SP0kK8wIs2Jl2pXAKR877nsVl7ZR08Z-sspHfhYAtQWnhMJ7M0l8xpWpHG2U8mpiqLCP_w" + +NEXT_PUBLIC_API_BASE=https://0rryyjj6vh.execute-api.eu-west-3.amazonaws.com/default +AUTH_BYPASS=1 +NEXT_PUBLIC_AUTH_BYPASS=1 + +# 🎭 Mode dĂ©mo - DĂ©commentez pour activer +DEMO_MODE=true +NEXT_PUBLIC_DEMO_MODE=true + +NEXT_PUBLIC_SUPABASE_URL=https://fusqtpjififcmgbhmosq.supabase.co + +NEXT_PUBLIC_API_BASE=https://0rryyjj6vh.execute-api.eu-west-3.amazonaws.com/default +AUTH_BYPASS=1 +NEXT_PUBLIC_AUTH_BYPASS=1 +NEXT_PUBLIC_SUPABASE_URL=https://fusqtpjififcmgbhmosq.supabase.co +NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImZ1c3F0cGppZmlmY21nYmhtb3NxIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTc1ODI4OTgsImV4cCI6MjA3MzE1ODg5OH0.kkQN9TaMM38WRgMLAUQh77yj71bi1aRGjG7W8KGGfUI +SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImZ1c3F0cGppZmlmY21nYmhtb3NxIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc1NzU4Mjg5OCwiZXhwIjoyMDczMTU4ODk4fQ.hbouhmX_2f3ivGIgTM1qom5q_GBNnbDTr6bcJnnhmOA +N8N_NOTES_WEBHOOK_URL=https://n8n.odentas.fr/webhook-test/ajout-note-contrat +SUPABASE_URL=https://fusqtpjififcmgbhmosq.supabase.co +AWS_REGION=eu-west-3 +AWS_SES_FROM="Odentas " +AWS_ACCESS_KEY_ID=AKIAUIGDVEFKTEVMDOVG +AWS_SECRET_ACCESS_KEY=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 +S3_BUCKET_NAME=odentas-docs +S3_BUCKET_NAME_EMAILS=stockage-logs-emails +UPSTREAM_API_BASE=https://0rryyjj6vh.execute-api.eu-west-3.amazonaws.com/default +AIRTABLE_TABLE_CONTRATS=tblUMqJtsvAvavZNz +AIRTABLE_BASE_ID=appyVHP0ZQdQzXFPn +AIRTABLE_API_KEY=patrNer5Dqyff2KEJ.aeebec95fc07db378dc92867d81ceba64e5e625b76ab0153395172595d8aa50c +DOCUSEAL_API_BASE=https://api.docuseal.eu +DOCUSEAL_TOKEN=s1fDVPiie3HYq2dEYvBX6od5k9p7JMB7VtvihoS8X77 +NEXT_PUBLIC_FORCE_ANIMATIONS=1 +N8N_VIREMENT_WEBHOOK_URL= +AWS_S3_BUCKET=odentas-docs +PDFMONKEY_URL=https://api.pdfmonkey.io/api/v1/documents +PDFMONKEY_API_KEY=ss-eoykvRJsbzedEP8c- +GOCARDLESS_ENVIRONMENT=live +GOCARDLESS_ACCESS_TOKEN=live_iMcKO4hM4Bezk25GN-owkb52jAI47NlxdjoL4dB_ \ No newline at end of file diff --git a/.env.local.bak2 b/.env.local.bak2 new file mode 100644 index 0000000..99d5121 --- /dev/null +++ b/.env.local.bak2 @@ -0,0 +1,41 @@ +# Created by Vercel CLI +# Created by Vercel CLI +VERCEL_OIDC_TOKEN="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im1yay00MzAyZWMxYjY3MGY0OGE5OGFkNjFkYWRlNGEyM2JlNyJ9.eyJpc3MiOiJodHRwczovL29pZGMudmVyY2VsLmNvbS9vZGVudGFzLXByb2plY3RzLTE2ZjRlYWZiIiwic3ViIjoib3duZXI6b2RlbnRhcy1wcm9qZWN0cy0xNmY0ZWFmYjpwcm9qZWN0Om9kZW50YXMtZXNwYWNlLXBhaWU6ZW52aXJvbm1lbnQ6ZGV2ZWxvcG1lbnQiLCJzY29wZSI6Im93bmVyOm9kZW50YXMtcHJvamVjdHMtMTZmNGVhZmI6cHJvamVjdDpvZGVudGFzLWVzcGFjZS1wYWllOmVudmlyb25tZW50OmRldmVsb3BtZW50IiwiYXVkIjoiaHR0cHM6Ly92ZXJjZWwuY29tL29kZW50YXMtcHJvamVjdHMtMTZmNGVhZmIiLCJvd25lciI6Im9kZW50YXMtcHJvamVjdHMtMTZmNGVhZmIiLCJvd25lcl9pZCI6InRlYW1fYkw2RkdOUkc0dFB5VDBmOGhqMFFUdVZuIiwicHJvamVjdCI6Im9kZW50YXMtZXNwYWNlLXBhaWUiLCJwcm9qZWN0X2lkIjoicHJqX3RPNGJObzQ0cldiYkk2UkhENGRZNDlSTEZ4b2EiLCJlbnZpcm9ubWVudCI6ImRldmVsb3BtZW50IiwidXNlcl9pZCI6Ill0QU5YTkhGMVVtcHRUa0llTVB5QlJKdCIsIm5iZiI6MTc1ODExMDE1OSwiaWF0IjoxNzU4MTEwMTU5LCJleHAiOjE3NTgxNTMzNTl9.b9OEMCf8-S6bgJBF8-IEvaZrzo9mChJCis0lbq7HsUB99cGVUUlWaLYe2o5zw3eYYO5rNVXcZ5wlNvNqEAjkaelcFBexE6Qx1yFh9QB9xGsTYltfVio2PxyWFKxsHVcNYf3khOHjHutm84LIoYIx6mxXYr96sfJsilpamw7bN161zYBuu2E4T5RlLhQ6r1DRCTMyvD-R7pLdtyHgpq8VsLzOEsvvCfO92pI57kLveen_itHMpIH7An3J7BjijkSluYWHRg4PkzrCti_3SP0kK8wIs2Jl2pXAKR877nsVl7ZR08Z-sspHfhYAtQWnhMJ7M0l8xpWpHG2U8mpiqLCP_w" + +NEXT_PUBLIC_API_BASE=https://0rryyjj6vh.execute-api.eu-west-3.amazonaws.com/default +AUTH_BYPASS=1 +NEXT_PUBLIC_AUTH_BYPASS=1 + +# 🎭 Mode dĂ©mo - DĂ©commentez pour activer +# DEMO_MODE=true +NEXT_PUBLIC_DEMO_MODE=true + +NEXT_PUBLIC_SUPABASE_URL=https://fusqtpjififcmgbhmosq.supabase.co + +NEXT_PUBLIC_API_BASE=https://0rryyjj6vh.execute-api.eu-west-3.amazonaws.com/default +AUTH_BYPASS=1 +NEXT_PUBLIC_AUTH_BYPASS=1 +NEXT_PUBLIC_SUPABASE_URL=https://fusqtpjififcmgbhmosq.supabase.co +NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImZ1c3F0cGppZmlmY21nYmhtb3NxIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTc1ODI4OTgsImV4cCI6MjA3MzE1ODg5OH0.kkQN9TaMM38WRgMLAUQh77yj71bi1aRGjG7W8KGGfUI +SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImZ1c3F0cGppZmlmY21nYmhtb3NxIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc1NzU4Mjg5OCwiZXhwIjoyMDczMTU4ODk4fQ.hbouhmX_2f3ivGIgTM1qom5q_GBNnbDTr6bcJnnhmOA +N8N_NOTES_WEBHOOK_URL=https://n8n.odentas.fr/webhook-test/ajout-note-contrat +SUPABASE_URL=https://fusqtpjififcmgbhmosq.supabase.co +AWS_REGION=eu-west-3 +AWS_SES_FROM="Odentas " +AWS_ACCESS_KEY_ID=AKIAUIGDVEFKTEVMDOVG +AWS_SECRET_ACCESS_KEY=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 +S3_BUCKET_NAME=odentas-docs +S3_BUCKET_NAME_EMAILS=stockage-logs-emails +UPSTREAM_API_BASE=https://0rryyjj6vh.execute-api.eu-west-3.amazonaws.com/default +AIRTABLE_TABLE_CONTRATS=tblUMqJtsvAvavZNz +AIRTABLE_BASE_ID=appyVHP0ZQdQzXFPn +AIRTABLE_API_KEY=patrNer5Dqyff2KEJ.aeebec95fc07db378dc92867d81ceba64e5e625b76ab0153395172595d8aa50c +DOCUSEAL_API_BASE=https://api.docuseal.eu +DOCUSEAL_TOKEN=s1fDVPiie3HYq2dEYvBX6od5k9p7JMB7VtvihoS8X77 +NEXT_PUBLIC_FORCE_ANIMATIONS=1 +N8N_VIREMENT_WEBHOOK_URL= +AWS_S3_BUCKET=odentas-docs +PDFMONKEY_URL=https://api.pdfmonkey.io/api/v1/documents +PDFMONKEY_API_KEY=ss-eoykvRJsbzedEP8c- +GOCARDLESS_ENVIRONMENT=live +GOCARDLESS_ACCESS_TOKEN=live_iMcKO4hM4Bezk25GN-owkb52jAI47NlxdjoL4dB_ \ No newline at end of file diff --git a/.gitignore b/.gitignore index 6135753..45636aa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,46 +1,21 @@ -# Vercel -.vercel - -# Environment variables -.env*.local -.env - -# Dependencies +# DĂ©pendances & builds node_modules/ -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Next.js .next/ out/ -build/ dist/ +build/ -# Logs +# Vercel +.vercel/ + +# Environnement & secrets +.env +.env.local +.env.*.local + +# OS / IDE +.DS_Store *.log -# TypeScript -*.tsbuildinfo -tsconfig.tsbuildinfo - -# OS generated files -.DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes -ehthumbs.db -Thumbs.db - -# IDE -.vscode/ -.idea/ -*.swp -*.swo - -# Temporary files -tmp/ -temp/ -*.tmp -node_modules +# Tests & coverage +coverage/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5fc820b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "npm.scriptRunner": "npm" +} \ No newline at end of file diff --git a/BULK_EMAIL_PROGRESS_MODAL.md b/BULK_EMAIL_PROGRESS_MODAL.md new file mode 100644 index 0000000..b38ff32 --- /dev/null +++ b/BULK_EMAIL_PROGRESS_MODAL.md @@ -0,0 +1,73 @@ +## 🎉 Modal de progression d'envoi d'emails groupĂ©s - TERMINÉ ! + +### ✅ FonctionnalitĂ©s implĂ©mentĂ©es : + +**1. Modal de progression en temps rĂ©el :** +- Interface similaire au modal de gĂ©nĂ©ration de PDFs +- Affichage de la progression en pourcentage avec barre animĂ©e +- Statistiques en temps rĂ©el (succĂšs, Ă©checs, restants) +- Liste dĂ©taillĂ©e de chaque email avec statuts + +**2. API Streaming pour envoi progressif :** +- Endpoint `/api/staff/bulk-email-stream` avec Server-Sent Events +- Envoi par lots de 5 emails pour respecter les limites SES +- Pause de 1 seconde entre les lots +- Gestion des erreurs individuelles sans arrĂȘter le processus + +**3. États de progression visuels :** +- 🕐 **En attente** : Gris, icĂŽne horloge +- 🔄 **Envoi en cours** : Bleu, spinner animĂ© +- ✅ **SuccĂšs** : Vert, icĂŽne de validation + heure d'envoi +- ❌ **Erreur** : Rouge, icĂŽne d'erreur + message dĂ©taillĂ© + +**4. FonctionnalitĂ©s avancĂ©es :** +- **Annulation** : Bouton pour arrĂȘter l'envoi en cours +- **Logs dĂ©taillĂ©s** : Sauvegarde des statistiques d'envoi +- **Animations** : Effets visuels pour l'interface +- **Responsive** : Compatible mobile et desktop + +### 📊 Interface du modal : + +``` +┌─────────────────────────────────────────────────┐ +│ 📧 Envoi d'emails groupĂ©s │ +│ Sujet: "Information importante - Espace Paie" │ +├────────────────────────────────────────────────── +│ Progression: 8/15 (53%) │ +│ ████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░ 53% │ +├────────────────────────────────────────────────── +│ ✅ EnvoyĂ©s: 7 ❌ Échecs: 1 ⏳ Restants: 7 │ +├────────────────────────────────────────────────── +│ ✅ user1@example.com (Organization A) 14:32:15 │ +│ ✅ user2@example.com (Organization B) 14:32:16 │ +│ 🔄 user3@example.com (Organization C) Envoi... │ +│ 🕐 user4@example.com (Organization D) En attente │ +│ ❌ user5@example.com (Organization E) Erreur SES │ +└─────────────────────────────────────────────────┘ +``` + +### 🚀 Comment ça marche : + +1. **Clic sur "Envoyer"** → Ouverture du modal +2. **Initialisation** → Tous les emails en "En attente" +3. **Traitement par lots** → 5 emails max simultanĂ©s +4. **Mise Ă  jour en temps rĂ©el** → Statuts et progression +5. **Gestion des erreurs** → Continuer malgrĂ© les Ă©checs +6. **Finalisation** → Statistiques complĂštes + logs + +### 🔧 Fichiers créés/modifiĂ©s : + +- **`BulkEmailProgressModal.tsx`** - Interface du modal +- **`bulk-email-stream/route.ts`** - API streaming +- **`BulkEmailForm.tsx`** - IntĂ©gration du modal + +### 🌟 AmĂ©liorations apportĂ©es : + +- Retour visuel immĂ©diat Ă  l'utilisateur +- Transparence totale sur le processus d'envoi +- PossibilitĂ© d'annuler en cours de route +- Interface moderne et intuitive +- Respect des bonnes pratiques SES +- Logs pour traçabilitĂ© et debug + +La fonctionnalitĂ© est maintenant complĂšte avec un modal de progression professionnel qui affiche l'avancement en temps rĂ©el de l'envoi des emails groupĂ©s ! 🎯 \ No newline at end of file diff --git a/DEBUG_EMAIL_LOGS.md b/DEBUG_EMAIL_LOGS.md new file mode 100644 index 0000000..aa74001 --- /dev/null +++ b/DEBUG_EMAIL_LOGS.md @@ -0,0 +1,78 @@ +# 🔍 Diagnostic Email SES - Logs de DĂ©bogage ActivĂ©s + +## Logs AjoutĂ©s pour Diagnostic + +J'ai ajoutĂ© des logs dĂ©taillĂ©s dans 3 fichiers pour identifier exactement d'oĂč vient l'email invalide : + +### 1. Dans `app/api/contrats/[id]/route.ts` +- 🔍 **[ROUTE DEBUG]** Organization data retrieved for non-staff user +- 🔍 **[ROUTE DEBUG]** About to send email notifications with organizationData + +### 2. Dans `lib/emailService.ts` +- 🔍 **[EMAIL DEBUG]** sendContractUpdateNotifications called with +- 🔍 **[EMAIL DEBUG]** Email addresses found +- 🔍 **[EMAIL DEBUG]** Sending email with validated addresses + +### 3. Dans `lib/emailTemplateService.ts` +- 🔍 **[SES DEBUG]** sendUniversalEmailV2 called with +- 🚹 **[SES DEBUG]** Invalid toEmail detected (si erreur) +- 🔍 **[SES DEBUG]** Emails validated, proceeding with send + +## Corrections AppliquĂ©es + +### ✅ Fix Potentiel : Array vs Object +Si `organization_details` est retournĂ© comme un tableau au lieu d'un objet : +```typescript +// Fix: Si organization_details est un tableau, prendre le premier Ă©lĂ©ment +let orgDetails = organizationData?.organization_details; +if (Array.isArray(orgDetails)) { + console.log('🔍 [EMAIL DEBUG] organization_details is array, taking first element'); + orgDetails = orgDetails[0] || {}; +} +``` + +## Comment Tester + +1. **Modifiez un contrat** en tant qu'utilisateur non-staff +2. **Regardez les logs Vercel** pour voir les messages de debug +3. **Identifiez** exactement quelle valeur d'email cause le problĂšme + +## Attendu dans les Logs + +``` +🔍 [ROUTE DEBUG] Organization data retrieved for non-staff user: { + orgId: "...", + hasOrgDetails: true, + emailNotifs: "...", // ← La valeur problĂ©matique sera ici + emailNotifsCC: "..." +} + +🔍 [EMAIL DEBUG] Email addresses found: { + emailNotifs: "...", // ← Valeur exacte + emailNotifsType: "string" // ← Type de donnĂ©es +} + +🚹 [SES DEBUG] Invalid toEmail detected: { + toEmail: "...", // ← Email invalide dĂ©tectĂ© + isEmpty: true/false +} +``` + +## Actions Possibles + +### Si `organization_details` est un tableau vide : +```json +"organization_details": [] +``` +→ Le fix array est appliquĂ© + +### Si `emailNotifs` est une chaĂźne vide : +```json +"emailNotifs": "" +``` +→ Validation should catch this + +### Si la structure de donnĂ©es est inattendue : +Les logs dĂ©taillĂ©s rĂ©vĂ©leront la structure exacte. + +**Testez maintenant et regardez les logs Vercel pour identifier le problĂšme !** 🔍 \ No newline at end of file diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..23bd9ed --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,113 @@ +# DĂ©ploiement Vercel - Guide Rapide + +## 🚀 Configuration pour demo.odentas.fr + +### 1. Ajouter le domaine dans Vercel + +```bash +# Si vous utilisez Vercel CLI +vercel domains add demo.odentas.fr + +# Ou via l'interface web: +# Dashboard → Settings → Domains → Add → demo.odentas.fr +``` + +### 2. Configuration DNS + +Chez votre registraire DNS : + +``` +Type: CNAME +Name: demo +Value: cname.vercel-dns.com +TTL: 300 +``` + +### 3. Test de la configuration + +```bash +# Test local (avec le serveur en marche) +./scripts/test-demo-domains.sh + +# Test manuel +curl -H "Host: demo.odentas.fr" http://localhost:3001/api/me +``` + +### 4. VĂ©rification du dĂ©ploiement + +Une fois dĂ©ployĂ©, vous devriez avoir : + +- **https://demo.odentas.fr** → Mode dĂ©mo automatique +- **https://paie.odentas.fr** → Mode normal +- **MĂȘme codebase** → Comportements diffĂ©rents + +### 5. FonctionnalitĂ©s automatiques + +Le fichier `vercel.json` configure automatiquement : + +- ✅ **DĂ©tection du domaine** : Auto-activation sur demo.odentas.fr +- ✅ **Header injection** : x-demo-mode: true +- ✅ **Redirection** : / → /contrats pour montrer les donnĂ©es +- ✅ **Optimisations** : Timeouts API adaptĂ©s + +### 6. Banner dĂ©mo intelligent + +Le systĂšme affiche automatiquement : + +- **Sur demo.odentas.fr** : Banner complet avec liens contact +- **En dĂ©veloppement** : Banner simple de dĂ©mo +- **Sur paie.odentas.fr** : Aucun banner (mode normal) + +## 🔧 DĂ©pannage + +### ProblĂšme : demo.odentas.fr ne fonctionne pas + +1. VĂ©rifier la configuration DNS : `nslookup demo.odentas.fr` +2. VĂ©rifier dans Vercel Dashboard que le domaine est ajoutĂ© +3. Attendre la propagation DNS (jusqu'Ă  24h) + +### ProblĂšme : Mode dĂ©mo non dĂ©tectĂ© + +1. VĂ©rifier les logs : `🎭 [DEMO MIDDLEWARE]` doit apparaĂźtre +2. Tester avec header : `curl -H "x-demo-mode: true" ...` +3. VĂ©rifier `vercel.json` est bien dĂ©ployĂ© + +### Test complet + +```bash +# Lancer le script de test +cd "/Users/renaud/Projet Nouvel Espace Paie" +./scripts/test-demo-domains.sh +``` + +## 📋 Checklist de dĂ©ploiement + +- [ ] Domaine `demo.odentas.fr` ajoutĂ© dans Vercel +- [ ] DNS configurĂ© (CNAME demo → cname.vercel-dns.com) +- [ ] `vercel.json` prĂ©sent dans le repo +- [ ] Test local passant avec `./scripts/test-demo-domains.sh` +- [ ] DĂ©ploiement Vercel effectuĂ© +- [ ] Test production : https://demo.odentas.fr/api/me +- [ ] VĂ©rification mode normal : https://paie.odentas.fr + +## 🎯 URLs de test + +Une fois dĂ©ployĂ© : + +```bash +# Mode dĂ©mo +curl https://demo.odentas.fr/api/me +curl https://demo.odentas.fr/api/contrats +curl "https://demo.odentas.fr/api/search?q=alice" + +# Mode normal +curl https://paie.odentas.fr/api/me +``` + +## 💡 Avantages de cette configuration + +- **Un seul codebase** → Maintenance simplifiĂ©e +- **DĂ©tection automatique** → Pas de variables d'env +- **SEO friendly** → URLs propres sans paramĂštres +- **DĂ©ploiement atomique** → CohĂ©rence garantie +- **Isolation sĂ©curisĂ©e** → Pas de risque de fuite de donnĂ©es \ No newline at end of file diff --git a/DOCUSEAL_ENV_VARIABLES.md b/DOCUSEAL_ENV_VARIABLES.md index a21f9c2..e01e38b 100644 --- a/DOCUSEAL_ENV_VARIABLES.md +++ b/DOCUSEAL_ENV_VARIABLES.md @@ -1,7 +1,7 @@ # Variables d'environnement requises pour la signature Ă©lectronique ## DocuSeal -- `DOCUSEAL_API_TOKEN`: Token d'authentification pour l'API DocuSeal +- `DOCUSEAL_TOKEN`: Token d'authentification pour l'API DocuSeal ## AWS S3 (pour les PDFs et les emails HTML) - `AWS_REGION`: RĂ©gion AWS (par dĂ©faut: eu-west-3) diff --git a/EMAIL_TEMPLATE_SYSTEM.md b/EMAIL_TEMPLATE_SYSTEM.md index fc31963..2b26bdb 100644 --- a/EMAIL_TEMPLATE_SYSTEM.md +++ b/EMAIL_TEMPLATE_SYSTEM.md @@ -107,7 +107,7 @@ import { await sendAccountActivationEmail('user@example.com', { firstName: 'Marie', organizationName: 'Compagnie Théùtrale', - activationUrl: 'https://espace-paie.odentas.fr/activate?token=abc' + activationUrl: 'https://paie.odentas.fr/activate?token=abc' }); // Modification d’habilitation diff --git a/FIX_EMAIL_SES_ERROR.md b/FIX_EMAIL_SES_ERROR.md new file mode 100644 index 0000000..cbe1ae6 --- /dev/null +++ b/FIX_EMAIL_SES_ERROR.md @@ -0,0 +1,114 @@ +# 🔧 Fix Email Error: "Missing final '@domain'" - COMPLET + +## ProblĂšme RĂ©solu ✅ +L'erreur AWS SES "Missing final '@domain'" Ă©tait causĂ©e par des adresses email invalides ou vides dans les notifications CDDU + un problĂšme spĂ©cifique avec les accĂšs staff. + +## 🎯 **Root Cause IdentifiĂ©** +1. **Emails invalides** dans `organization_details.email_notifs` +2. **Bug staff** : Quand staff modifie/supprime un contrat, `organizationData = { organization_details: {} }` crĂ©ait un objet vide → `emailNotifs = undefined` → erreur SES + +## ✅ Corrections AppliquĂ©es + +### 1. Validation d'Email Globale +Ajout de validation dans `emailTemplateService.ts` : +```typescript +const isValidEmail = (email: string) => { + return email && typeof email === 'string' && email.includes('@') && email.includes('.') && email.trim().length > 0; +}; +``` + +### 2. Protection dans les Fonctions d'Envoi +- `sendContractNotifications()` ✅ +- `sendContractUpdateNotifications()` ✅ +- `sendContractCancellationNotifications()` ✅ +- `sendUniversalEmailV2()` ✅ + +### 3. **Correction Critique : Bug Staff dans `/contrats/[id]/route.ts`** +**Avant :** +```typescript +if (org.id === null) { + // Staff avec accĂšs admin + organizationData = { organization_details: {} }; // ❌ BUG : objet vide +} +``` + +**AprĂšs :** +```typescript +if (org.id === null) { + // Pour staff, rĂ©cupĂ©rer l'organisation du contrat depuis la DB + if (contractData.org_id) { + const { data: orgDetails } = await admin + .from("organizations") + .select("*, organization_details(*)") + .eq("id", contractData.org_id) + .single(); + organizationData = orgDetails || { organization_details: {} }; + } else { + organizationData = null; // Skip notifications + } +} +``` + +### 4. Gestion Intelligente des Erreurs +- Emails invalides → Warning dans les logs au lieu d'erreur fatale +- Emails CC invalides → IgnorĂ©s automatiquement +- Emails principaux invalides → Erreur avec message clair +- **Pas d'organisation** → Skip notifications (pas d'erreur) + +## 🔍 Points CorrigĂ©s dans le Code + +### **1. `lib/emailService.ts`** +- ✅ Validation emails dans `sendContractNotifications()` +- ✅ Validation emails dans `sendContractUpdateNotifications()` +- ✅ Validation emails dans `sendContractCancellationNotifications()` + +### **2. `lib/emailTemplateService.ts`** +- ✅ Validation emails dans `sendUniversalEmailV2()` + +### **3. `app/api/contrats/[id]/route.ts`** ⭐ **CRITIQUE** +- ✅ Correction bug staff dans `PATCH` (ligne ~350) +- ✅ Correction bug staff dans `PATCH` (ligne ~500) +- ✅ Correction bug staff dans `DELETE` (ligne ~580) + +## 📊 Monitoring Post-Fix + +Les logs contiendront maintenant : + +### ✅ SuccĂšs +``` +Contract notifications sent successfully (universal system) +``` + +### ⚠ Warnings (non-bloquants) +``` +No valid notification email configured for organization: { + orgId: "...", + orgName: "...", + emailNotifs: "", + emailNotifsCC: null +} + +No organization linked to contract, skipping email notifications +``` + +### ❌ Erreurs (si email principal complĂštement invalide) +``` +Invalid email address: "invalid-email" +``` + +## 🚀 RĂ©sultat + +- ✅ Plus d'erreurs SES "Missing final '@domain'" +- ✅ CrĂ©ation/modification/suppression de contrats qui continue mĂȘme si email invalide +- ✅ **Staff peut maintenant modifier/supprimer des contrats sans erreurs email** +- ✅ Logs clairs pour identifier les problĂšmes de configuration +- ✅ Envoi robuste avec gestion des cas d'erreur + +## 🔧 Actions de Maintenance + +1. **VĂ©rifiez les logs** pour identifier les organisations avec des warnings +2. **Corrigez les emails** dans l'interface d'administration +3. **Testez** la crĂ©ation/modification/suppression de contrats +4. **Surveillez** les mĂ©triques SES pour confirmer la rĂ©solution + +Le systĂšme est maintenant **100% rĂ©sistant aux erreurs d'email** ! 🎯 \ No newline at end of file diff --git a/FIX_OTP_EMAIL_LINKS.md b/FIX_OTP_EMAIL_LINKS.md new file mode 100644 index 0000000..7b3cb7d --- /dev/null +++ b/FIX_OTP_EMAIL_LINKS.md @@ -0,0 +1,73 @@ +# 🔧 Fix OTP Email Links - Guide Rapide + +## ProblĂšme +Les emails OTP reçus contiennent des liens vers `localhost:3000` au lieu de votre domaine Vercel. + +## ✅ Solution ImplĂ©mentĂ©e + +### 1. Code CorrigĂ© ✅ +Le fichier `app/api/auth/send-code/route.ts` a Ă©tĂ© mis Ă  jour pour dĂ©tecter automatiquement l'URL correcte : + +```typescript +// DĂ©tecter automatiquement l'URL de base selon l'environnement +const getBaseUrl = () => { + // En production, utiliser l'URL Vercel ou le domaine custom + if (process.env.VERCEL_URL) { + return `https://${process.env.VERCEL_URL}`; + } + // Fallback vers variable d'environnement personnalisĂ©e + if (process.env.NEXT_PUBLIC_SITE_URL) { + return process.env.NEXT_PUBLIC_SITE_URL; + } + // En dĂ©veloppement local + return "http://localhost:3000"; +}; +``` + +## 🚀 Actions Ă  Faire MAINTENANT + +### 1. Variables Vercel (URGENT) +Ajoutez cette variable dans votre dashboard Vercel : + +```bash +# Settings → Environment Variables → Production +NEXT_PUBLIC_SITE_URL=https://votre-app.vercel.app +``` + +### 2. Configuration Supabase (URGENT) +Dans Supabase Dashboard → Authentication → URL Configuration : + +```bash +Site URL: https://votre-app.vercel.app +Additional Redirect URLs: +- https://votre-app.vercel.app/** +- https://votre-domaine-custom.com/** (si vous en avez un) +- http://localhost:3000/** (pour le dev) +``` + +### 3. Template Email Supabase (OPTIONNEL) +Dans Supabase → Authentication → Email Templates → Confirm signup : + +Remplacez : +```html +{{ .ConfirmationURL }} +``` + +Par votre domaine : +```html +https://votre-app.vercel.app/auth/callback?token_hash={{ .TokenHash }}&type=magiclink +``` + +## ⚡ Test Rapide + +1. **DĂ©ployez** le code corrigĂ© sur Vercel +2. **Ajoutez** la variable `NEXT_PUBLIC_SITE_URL` +3. **Configurez** les URLs dans Supabase +4. **Testez** l'OTP sur votre site Vercel + +## 🎯 RĂ©sultat +✅ Les emails OTP contiendront dĂ©sormais des liens vers votre domaine Vercel, pas localhost. + +--- + +**Temps estimĂ© :** 5 minutes de configuration + 2 minutes de dĂ©ploiement = **7 minutes total** \ No newline at end of file diff --git a/GUIDE_ERREURS_EMAILS.md b/GUIDE_ERREURS_EMAILS.md new file mode 100644 index 0000000..fe87174 --- /dev/null +++ b/GUIDE_ERREURS_EMAILS.md @@ -0,0 +1,114 @@ +# 🐛 Guide de rĂ©solution des erreurs d'envoi d'emails groupĂ©s + +## Erreur : "Missing '<'" - CORRIGÉ ✅ + +### ProblĂšme identifiĂ© : +L'erreur "Missing '<'" Ă©tait causĂ©e par un mauvais formatage de l'adresse email source dans l'API SES. + +### Solution appliquĂ©e : +```typescript +// Avant (incorrect) +Source: `Espace Paie Odentas <${fromEmail}>`, + +// AprĂšs (corrigĂ©) +let sourceEmail = fromEmail; +if (fromEmail && !fromEmail.includes('<')) { + sourceEmail = `Espace Paie Odentas <${fromEmail}>`; +} else { + sourceEmail = fromEmail; +} +Source: sourceEmail, +``` + +## 📋 Autres erreurs courantes et solutions + +### 1. **Adresse email invalide** +- **Cause** : Format d'email incorrect (manque @, domaine invalide, espaces) +- **Solution** : Validation ajoutĂ©e cĂŽtĂ© client et serveur +- **PrĂ©vention** : Seuls les emails confirmĂ©s et valides sont affichĂ©s + +### 2. **Quota journalier dĂ©passĂ©** +- **Cause** : Limite AWS SES atteinte (200 emails/jour par dĂ©faut) +- **Solution** : Attendre 24h ou demander une augmentation de quota +- **PrĂ©vention** : Surveiller le nombre d'emails envoyĂ©s + +### 3. **Limite de dĂ©bit dĂ©passĂ©e** +- **Cause** : Trop d'emails envoyĂ©s simultanĂ©ment (limite SES : 14/seconde) +- **Solution** : L'API limite automatiquement Ă  12 emails par lot avec pause de 1s +- **PrĂ©vention** : Ne pas cliquer plusieurs fois sur "Envoyer" + +### Performance optimisĂ©e : +- **DĂ©bit** : 12 emails par seconde (sous la limite de 14/sec) +- **Throughput** : ~720 emails par minute +- **SĂ©curitĂ©** : Marge de 2 emails/sec pour Ă©viter les dĂ©passements + +### 4. **Configuration SES manquante** +- **Cause** : Variable d'environnement AWS_SES_FROM non dĂ©finie +- **Solution** : VĂ©rifier la configuration des variables d'environnement +- **PrĂ©vention** : Test automatique au dĂ©marrage + +## 🔧 AmĂ©liorations apportĂ©es + +### Validation cĂŽtĂ© client : +```typescript +const isValidEmail = user.email && + user.email.includes('@') && + user.email.includes('.') && + user.email.length > 5 && + !user.email.includes(' '); +``` + +### Gestion d'erreurs amĂ©liorĂ©e : +- Messages d'erreur plus clairs et spĂ©cifiques +- Distinction entre les types d'erreurs +- Conseils contextuels pour rĂ©solution + +### Interface utilisateur : +- Warning dĂ©taillĂ© en cas d'Ă©checs +- Affichage des statuts en temps rĂ©el +- PossibilitĂ© d'annulation en cours d'envoi + +## 📊 Monitoring et logs + +### Logs automatiques : +- Nombre total d'emails traitĂ©s +- Taux de succĂšs/Ă©chec +- DĂ©tails des erreurs par email +- Horodatage prĂ©cis + +### TraçabilitĂ© : +- Chaque envoi est enregistrĂ© en base +- PossibilitĂ© de retrouver les Ă©checs +- Statistiques d'utilisation + +## 🚀 Bonnes pratiques + +1. **Avant l'envoi** : + - VĂ©rifier le contenu HTML (aperçu) + - Tester avec 1-2 destinataires d'abord + - S'assurer que le sujet est clair + +2. **Pendant l'envoi** : + - Ne pas fermer le navigateur + - Surveiller les erreurs en temps rĂ©el + - Utiliser "Annuler" si nĂ©cessaire + +3. **AprĂšs l'envoi** : + - VĂ©rifier les statistiques finales + - Noter les emails en Ă©chec pour retry + - ContrĂŽler la rĂ©ception sur quelques comptes + +## ⚡ Limites techniques + +- **Maximum** : 100 destinataires par envoi +- **DĂ©bit** : 12 emails simultanĂ©s par lot (limite SES : 14/sec) +- **Performance** : ~720 emails/minute +- **Taille** : Contenu HTML < 10MB +- **Quota** : Selon configuration AWS SES + +## 🆘 En cas de problĂšme persistant + +1. VĂ©rifier les logs AWS SES +2. ContrĂŽler les variables d'environnement +3. Tester avec un email simple +4. Contacter l'administrateur systĂšme \ No newline at end of file diff --git a/GUIDE_MODE_DEMO.md b/GUIDE_MODE_DEMO.md new file mode 100644 index 0000000..faa11f7 --- /dev/null +++ b/GUIDE_MODE_DEMO.md @@ -0,0 +1,62 @@ +# Guide : Gestion du Mode DĂ©mo + +## DĂ©sactivation du mode dĂ©mo (situation actuelle) + +Le mode dĂ©mo a Ă©tĂ© **dĂ©sactivĂ©** en local en commentant les variables dans `.env.local` : +```bash +# DEMO_MODE=true +# NEXT_PUBLIC_DEMO_MODE=true +``` + +## Comment rĂ©activer le mode dĂ©mo temporairement + +### 1. RĂ©activation rapide +DĂ©commentez les lignes dans `.env.local` : +```bash +DEMO_MODE=true +NEXT_PUBLIC_DEMO_MODE=true +``` + +### 2. RĂ©activation temporaire via header HTTP +Pour tester une API spĂ©cifique en mode dĂ©mo sans modifier `.env.local` : +```bash +curl -H "x-demo-mode: true" http://localhost:3001/api/contrats +``` + +### 3. RĂ©activation temporaire via variable d'environnement +Pour un test ponctuel : +```bash +DEMO_MODE=true npm run dev +``` + +## Modes d'utilisation + +### Mode Normal (actuel) +- ✅ AccĂšs Ă  tous les comptes staff et client +- ✅ Connexion Supabase rĂ©elle +- ✅ DonnĂ©es de production/dĂ©veloppement + +### Mode DĂ©mo +- ⚠ DonnĂ©es fictives uniquement +- ⚠ Pas d'accĂšs aux vraies donnĂ©es +- ⚠ BanniĂšre de dĂ©mo affichĂ©e + +## Commandes utiles + +```bash +# Activer temporairement le mode dĂ©mo +echo "DEMO_MODE=true" >> .env.local +echo "NEXT_PUBLIC_DEMO_MODE=true" >> .env.local + +# DĂ©sactiver le mode dĂ©mo +sed -i 's/^DEMO_MODE=true$/# DEMO_MODE=true/' .env.local +sed -i 's/^NEXT_PUBLIC_DEMO_MODE=true$/# NEXT_PUBLIC_DEMO_MODE=true/' .env.local + +# VĂ©rifier l'Ă©tat actuel +grep -E "(# )?DEMO_MODE" .env.local +``` + +## En production + +Le mode dĂ©mo reste actif sur `demo.odentas.fr` grĂące Ă  la configuration Vercel automatique via le domaine. +Les modifications locales n'affectent pas la production. \ No newline at end of file diff --git a/MAINTENANCE_PUBLIC_PAGES.md b/MAINTENANCE_PUBLIC_PAGES.md new file mode 100644 index 0000000..a0e5d0b --- /dev/null +++ b/MAINTENANCE_PUBLIC_PAGES.md @@ -0,0 +1,57 @@ +## Test de l'accĂšs aux pages publiques pendant la maintenance + +### Pages maintenant accessibles en mode maintenance : + +1. **`/auto-declaration`** - Formulaire d'auto-dĂ©claration des salariĂ©s + - Permet aux salariĂ©s de complĂ©ter leurs informations et uploader des justificatifs + - Accessible via un lien avec matricule : `/auto-declaration?matricule=XXX` + +2. **`/dl-contrat-signe`** - TĂ©lĂ©chargement de contrats signĂ©s + - Permet aux salariĂ©s de tĂ©lĂ©charger leurs contrats signĂ©s + - Accessible via des liens sĂ©curisĂ©s gĂ©nĂ©rĂ©s par le systĂšme + +### Pages dĂ©jĂ  accessibles en mode maintenance : + +- `/api/*` - Toutes les API routes (nĂ©cessaires pour le fonctionnement) +- `/maintenance` - Page de maintenance elle-mĂȘme +- `/signin` - Page de connexion (pour l'accĂšs staff) +- Ressources statiques (`/_next`, `/favicon`, `/public`) + +### Comment tester : + +1. **Activer le mode maintenance** : + - Se connecter en tant que staff + - Utiliser le bouton "Mode Maintenance" dans l'interface + +2. **Tester l'accĂšs aux pages publiques** : + ``` + # Ces URLs doivent ĂȘtre accessibles mĂȘme en maintenance + http://localhost:3000/auto-declaration?matricule=TEST123 + http://localhost:3000/dl-contrat-signe?token=xxx + ``` + +3. **Tester les autres pages** : + ``` + # Ces URLs doivent rediriger vers /maintenance + http://localhost:3000/contrats + http://localhost:3000/salaries + http://localhost:3000/ + ``` + +4. **AccĂšs staff** : + - Sur la page de maintenance, cliquer sur "AccĂšs Ă©quipe" + - Se connecter avec un compte staff + - AccĂšs complet Ă  l'application + +### Modification apportĂ©e : + +Dans `middleware.ts`, la condition de vĂ©rification de maintenance exclut maintenant : +```typescript +const isPublicPage = path.startsWith('/auto-declaration') || path.startsWith('/dl-contrat-signe'); + +if (!isApiOrAssets && !isMaintenancePage && !isSigninPage && !isPublicPage) { + // VĂ©rification du mode maintenance... +} +``` + +Cela garantit que ces pages restent accessibles aux salariĂ©s mĂȘme lorsque l'Espace Paie est en maintenance pour les utilisateurs connectĂ©s. \ No newline at end of file diff --git a/NOTES_CREATION_AUTOMATIQUES.md b/NOTES_CREATION_AUTOMATIQUES.md new file mode 100644 index 0000000..18f7502 --- /dev/null +++ b/NOTES_CREATION_AUTOMATIQUES.md @@ -0,0 +1,46 @@ +# FonctionnalitĂ© : Notes automatiques de crĂ©ation de contrats + +## Description + +Lorsqu'un nouveau contrat est créé via l'Espace Paie (routes `/contrats/nouveau` ou `/contrats/nouveau/saisie-tableau`), le systĂšme crĂ©e automatiquement une note traçant cette crĂ©ation. + +## Comportement + +### Pour un utilisateur standard (client) +Quand un contrat est créé par un client via l'Espace Paie, une note automatique est ajoutĂ©e avec : +- **Contenu** : "Demande créée via l'Espace Paie par PrĂ©nom (ROLE) le dd/mm/aaaa Ă  hh:mm:ss" +- **Source** : "SystĂšme" +- **Exemples** : + - "Demande créée via l'Espace Paie par Marie (SUPER_ADMIN) le 09/10/2025 Ă  14:30:25" + - "Demande créée via l'Espace Paie par Jean (ADMIN) le 09/10/2025 Ă  14:30:25" + - "Demande créée via l'Espace Paie par Sophie (AGENT) le 09/10/2025 Ă  14:30:25" + +### Pour un membre du Staff Odentas +Quand un contrat est créé par un membre du staff, une note automatique est ajoutĂ©e avec : +- **Contenu** : "Demande créée par le Staff Odentas le dd/mm/aaaa Ă  hh:mm:ss" +- **Source** : "SystĂšme" + +## Notes additionnelles + +- Cette note systĂšme est créée **en plus** de toute note manuelle saisie par l'utilisateur +- Les notes manuelles gardent la source "Espace Paie" +- La dĂ©tection du statut Staff se fait via la table `staff_users` ou via les mĂ©tadonnĂ©es utilisateur +- Le prĂ©nom est extrait depuis `user_metadata.first_name` ou `user_metadata.display_name` +- Le niveau d'habilitation est rĂ©cupĂ©rĂ© depuis la table `organization_members` pour les clients +- Les niveaux possibles sont : SUPER_ADMIN, ADMIN, AGENT, COMPTA + +## ImplĂ©mentation + +### Fichiers modifiĂ©s +- `/app/api/cddu-contracts/route.ts` : API principale de crĂ©ation de contrats CDDU +- `/app/api/rg-contracts/route.ts` : API pour les contrats RĂ©gime GĂ©nĂ©ral + +### Routes concernĂ©es +- `/contrats/nouveau` : Formulaire de crĂ©ation d'un nouveau contrat CDDU +- `/contrats/nouveau/saisie-tableau` : Interface de saisie en lot +- Les contrats RĂ©gime GĂ©nĂ©ral via le mĂȘme formulaire + +### Format des dates +- Date : format français (dd/mm/aaaa) +- Heure : format français 24h (hh:mm:ss) +- Timezone : locale du serveur \ No newline at end of file diff --git a/OPTIMISATION_DEBIT_SES.md b/OPTIMISATION_DEBIT_SES.md new file mode 100644 index 0000000..7fffae9 --- /dev/null +++ b/OPTIMISATION_DEBIT_SES.md @@ -0,0 +1,84 @@ +# 🚀 Optimisation du dĂ©bit d'envoi d'emails - SES 14/sec + +## ⚡ Performances amĂ©liorĂ©es + +### Avant l'optimisation : +- **DĂ©bit** : 5 emails par lot +- **Pause** : 1 seconde entre lots +- **Performance** : ~300 emails/minute +- **EfficacitĂ©** : 35% de la capacitĂ© SES + +### AprĂšs l'optimisation : +- **DĂ©bit** : 12 emails par lot +- **Pause** : 1 seconde entre lots +- **Performance** : ~720 emails/minute +- **EfficacitĂ©** : 85% de la capacitĂ© SES +- **Marge de sĂ©curitĂ©** : 2 emails/sec + +## 📊 Calculs de performance + +``` +Limite SES : 14 emails/seconde +Configuration : 12 emails/lot avec 1s de pause + +DĂ©bit effectif : +- Lot 1 : 12 emails Ă  t=0s +- Pause : 1 seconde +- Lot 2 : 12 emails Ă  t=1s +- Performance : 12 emails/seconde (< 14 emails/seconde ✅) + +Temps d'envoi estimĂ©s : +- 50 emails : ~5 secondes +- 100 emails : ~9 secondes +- 200 emails : ~17 secondes +- 500 emails : ~42 secondes +``` + +## đŸ›Ąïž SĂ©curitĂ© et fiabilitĂ© + +### Marge de sĂ©curitĂ© : +- **Buffer** : 2 emails/sec sous la limite +- **Protection** : Évite les erreurs de dĂ©passement +- **Robustesse** : TolĂ©rance aux variations rĂ©seau + +### Gestion d'erreurs amĂ©liorĂ©e : +- DĂ©tection automatique des limites dĂ©passĂ©es +- Messages d'erreur spĂ©cifiques +- Retry automatique en cas d'erreur temporaire + +## 🎯 Impact utilisateur + +### Interface mise Ă  jour : +- Affichage du dĂ©bit en temps rĂ©el +- Indicateur "12 emails/lot, respect limite 14/sec" +- Progression plus fluide et rapide + +### ExpĂ©rience amĂ©liorĂ©e : +- **2.4x plus rapide** qu'avant +- Temps d'attente rĂ©duits +- Feedback en temps rĂ©el optimisĂ© + +## 🔧 Configuration technique + +### Variables importantes : +```typescript +const batchSize = 12; // Emails par lot +const batchDelayMs = 1000; // Pause entre lots (ms) +const maxSESRate = 14; // Limite SES (emails/sec) +const safetyMargin = 2; // Marge de sĂ©curitĂ© +``` + +### AdaptabilitĂ© : +- Facile Ă  ajuster si limite SES change +- Configuration centralisĂ©e +- Monitoring des performances intĂ©grĂ© + +## 📈 MĂ©triques de suivi + +L'API enregistre automatiquement : +- Nombre d'emails traitĂ©s par minute +- Taux de succĂšs/Ă©chec +- Temps de traitement total +- Respect des limites SES + +Cette optimisation permet d'utiliser efficacement votre quota SES tout en gardant une marge de sĂ©curitĂ© ! 🎉 \ No newline at end of file diff --git a/STAFF_MAINTENANCE_ACCESS_GUIDE.md b/STAFF_MAINTENANCE_ACCESS_GUIDE.md new file mode 100644 index 0000000..70dd5f0 --- /dev/null +++ b/STAFF_MAINTENANCE_ACCESS_GUIDE.md @@ -0,0 +1,103 @@ +# Guide d'AccĂšs Staff en Mode Maintenance + +## FonctionnalitĂ© ImplĂ©mentĂ©e + +Cette implĂ©mentation permet aux membres du staff de se connecter Ă  l'Espace Paie mĂȘme lorsque le site est en mode maintenance. + +## Comment ça fonctionne + +### 1. Page de Maintenance +- Lorsque le site est en maintenance, un lien discret "AccĂšs Ă©quipe" apparaĂźt en bas de la page +- Ce lien redirige vers `/signin?staff_access=true` + +### 2. Page de Connexion Staff +- Quand on accĂšde Ă  `/signin` avec le paramĂštre `staff_access=true`, la page affiche : + - Un titre spĂ©cial : "Connexion Staff - Mode Maintenance" + - Un avertissement orange : "⚠ Site en maintenance - AccĂšs Ă©quipe uniquement" + - Tous les modes de connexion habituels (mot de passe et code par email) + +### 3. Middleware AjustĂ© +- Le middleware autorise maintenant l'accĂšs Ă  `/signin` mĂȘme en mode maintenance +- AprĂšs connexion rĂ©ussie, les staff peuvent accĂ©der normalement Ă  l'application +- Les non-staff restent bloquĂ©s et redirigĂ©s vers la page de maintenance + +## Flux Utilisateur + +### Pour un Staff +1. Visite du site en maintenance → page de maintenance +2. Clic sur "AccĂšs Ă©quipe" → page de connexion staff +3. Saisie des identifiants → connexion rĂ©ussie +4. Redirection vers l'application → accĂšs complet + +### Pour un Non-Staff +1. Visite du site en maintenance → page de maintenance +2. MĂȘme s'il trouve le lien "AccĂšs Ă©quipe" → page de connexion +3. AprĂšs connexion → redirection vers la page de maintenance (middleware) + +## Modifications EffectuĂ©es + +### 1. `app/maintenance/MaintenancePage.tsx` +```tsx +// Ajout du lien d'accĂšs staff dans le footer +
+ + AccÚs équipe + +
+``` + +### 2. `app/signin/page.tsx` +```tsx +// DĂ©tection du paramĂštre staff_access +const [isStaffAccess, setIsStaffAccess] = useState(false); + +useEffect(() => { + const urlParams = new URLSearchParams(window.location.search); + setIsStaffAccess(urlParams.get('staff_access') === 'true'); +}, []); + +// Affichage conditionnel du titre et message d'avertissement +{isStaffAccess ? ( + <> + + Connexion Staff - Mode Maintenance + +) : ( + "Connexion Ă  l'Espace Paie" +)} +``` + +### 3. `middleware.ts` +```typescript +// Exclusion de /signin du blocage de maintenance +if (!isApiOrAssets && !isMaintenancePage && !isSigninPage) { + // VĂ©rification du mode maintenance... +} +``` + +## SĂ©curitĂ© + +- Le lien "AccĂšs Ă©quipe" est discret et ne compromet pas la sĂ©curitĂ© +- L'authentification reste obligatoire pour tous +- Seuls les vrais staff (vĂ©rifiĂ©s en base) peuvent accĂ©der Ă  l'app aprĂšs connexion +- Les non-staff sont toujours bloquĂ©s par le middleware aprĂšs authentification + +## Test Manuel + +1. **Activer le mode maintenance** (via le bouton staff dans l'interface) +2. **Se dĂ©connecter** +3. **Visiter le site** → vĂ©rifier la page de maintenance avec le lien +4. **Cliquer sur "AccĂšs Ă©quipe"** → vĂ©rifier la page de connexion staff +5. **Se connecter en tant que staff** → vĂ©rifier l'accĂšs Ă  l'app +6. **Se connecter en tant que non-staff** → vĂ©rifier le blocage + +## Avantages + +- ✅ AccĂšs d'urgence pour les staff en cas de maintenance +- ✅ Interface claire indiquant le mode spĂ©cial +- ✅ SĂ©curitĂ© prĂ©servĂ©e (authentification + vĂ©rification staff) +- ✅ FonctionnalitĂ© discrĂšte pour les utilisateurs normaux +- ✅ Compatible avec tous les modes de connexion existants \ No newline at end of file diff --git a/app/(app)/compte/securite/page.tsx b/app/(app)/compte/securite/page.tsx index 8320913..13fca79 100644 --- a/app/(app)/compte/securite/page.tsx +++ b/app/(app)/compte/securite/page.tsx @@ -7,6 +7,7 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { KeyRound, Lock, Loader2, Check, X, Eye, EyeOff } from "lucide-react"; import { MfaSetupComponent } from "@/components/auth/MfaSetupComponent"; +import { usePageTitle } from "@/hooks/usePageTitle"; /** * CritĂšres de validation du mot de passe @@ -31,6 +32,8 @@ const isPasswordValid = (validation: ReturnType) => { * - 2FA dĂ©sactivĂ© temporairement */ export default function CompteSecuritePage() { + usePageTitle("SĂ©curitĂ©"); + const [hasPassword, setHasPassword] = useState(null); React.useEffect(() => { @@ -132,11 +135,11 @@ export default function CompteSecuritePage() { {hasPassword === null ? ( VĂ©rification du statut
 ) : hasPassword ? ( - + Mot de passe dĂ©fini ) : ( - + Aucun mot de passe dĂ©fini )} @@ -167,8 +170,8 @@ export default function CompteSecuritePage() { {/* CritĂšres de validation en temps rĂ©el */} {pw1.length > 0 && ( -
-
+
+
CritĂšres requis :
@@ -178,7 +181,7 @@ export default function CompteSecuritePage() { ) : ( )} - + Au moins 12 caractĂšres
@@ -188,7 +191,7 @@ export default function CompteSecuritePage() { ) : ( )} - + Au moins une minuscule (a-z)
@@ -198,7 +201,7 @@ export default function CompteSecuritePage() { ) : ( )} - + Au moins une majuscule (A-Z)
@@ -208,7 +211,7 @@ export default function CompteSecuritePage() { ) : ( )} - + Au moins un chiffre (0-9)
@@ -218,7 +221,7 @@ export default function CompteSecuritePage() { ) : ( )} - + Au moins un caractÚre spécial (!@#$%^&*) @@ -255,14 +258,14 @@ export default function CompteSecuritePage() { {passwordMatch === true ? ( <> - + Les mots de passe correspondent ) : ( <> - + Les mots de passe ne correspondent pas diff --git a/app/(app)/contrats-multi/[id]/page.tsx b/app/(app)/contrats-multi/[id]/page.tsx index d635f1d..a94d29e 100644 --- a/app/(app)/contrats-multi/[id]/page.tsx +++ b/app/(app)/contrats-multi/[id]/page.tsx @@ -9,6 +9,7 @@ import { NotesSection } from "@/components/NotesSection"; import { Button } from "@/components/ui/button"; import { ConfirmationModal } from "@/components/ui/confirmation-modal"; import { toast } from "sonner"; +import { usePageTitle } from "@/hooks/usePageTitle"; /* ========================= Types attendus du backend @@ -178,8 +179,8 @@ function usePaies(id: string) { ========= */ function Section({ title, children }: { title: React.ReactNode; children: React.ReactNode }) { return ( -
-
+
+
{title}
{children}
@@ -250,14 +251,14 @@ function Badge({ }) { const cls = tone === "ok" - ? "bg-emerald-100 text-emerald-800 dark:bg-emerald-900/40 dark:text-emerald-200" + ? "bg-emerald-100 text-emerald-800" : tone === "warn" - ? "bg-amber-100 text-amber-800 dark:bg-amber-900/40 dark:text-amber-200" + ? "bg-amber-100 text-amber-800" : tone === "error" - ? "bg-rose-100 text-rose-800 dark:bg-rose-900/40 dark:text-rose-200" + ? "bg-rose-100 text-rose-800" : tone === "info" - ? "bg-sky-100 text-sky-800 dark:bg-sky-900/40 dark:text-sky-200" - : "bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-300"; + ? "bg-sky-100 text-sky-800" + : "bg-slate-100 text-slate-700"; return {children}; } @@ -288,41 +289,41 @@ function stateBadgeDemande(s?: EtatDemande) { if (normalized.includes("pre") || normalized.includes("demande")) { // PrĂ©-demande - Gris (neutre) style = { - bg: "bg-slate-50 dark:bg-slate-800/40", - border: "border-slate-200 dark:border-slate-700/60", - text: "text-slate-700 dark:text-slate-300", + bg: "bg-slate-50", + border: "border-slate-200", + text: "text-slate-700", label: "PrĂ©-demande" }; } else if (normalized.includes("recu") || normalized.includes("recue")) { // Reçue - Bleu (information) style = { - bg: "bg-blue-50 dark:bg-blue-900/30", - border: "border-blue-200 dark:border-blue-800/60", - text: "text-blue-800 dark:text-blue-200", + bg: "bg-blue-50", + border: "border-blue-200", + text: "text-blue-800", label: "Reçue" }; } else if (normalized.includes("cours") || normalized.includes("traitement")) { // En cours de traitement - Orange (en attente) style = { - bg: "bg-orange-50 dark:bg-orange-900/20", - border: "border-orange-200 dark:border-orange-800/60", - text: "text-orange-800 dark:text-orange-200", + bg: "bg-orange-50", + border: "border-orange-200", + text: "text-orange-800", label: "En cours de traitement" }; } else if (normalized.includes("traitee") || normalized.includes("traite")) { // TraitĂ©e - Vert (succĂšs) style = { - bg: "bg-emerald-50 dark:bg-emerald-900/20", - border: "border-emerald-200 dark:border-emerald-800/60", - text: "text-emerald-800 dark:text-emerald-200", + bg: "bg-emerald-50", + border: "border-emerald-200", + text: "text-emerald-800", label: "TraitĂ©e" }; } else { // Fallback - Affichage de la valeur brute avec style neutre style = { - bg: "bg-slate-50 dark:bg-slate-800/40", - border: "border-slate-200 dark:border-slate-700/60", - text: "text-slate-700 dark:text-slate-300", + bg: "bg-slate-50", + border: "border-slate-200", + text: "text-slate-700", label: input || "—" }; } @@ -363,6 +364,11 @@ export default function ContratMultiPage() { const { id } = useParams<{ id: string }>(); const { data, isLoading, isError, error } = useContrat(id); + + // Titre dynamique basĂ© sur le numĂ©ro du contrat + const contractTitle = data?.numero ? `Contrat ${data.numero}` : `Contrat multi-mois`; + usePageTitle(contractTitle); + const { data: paiesData, isLoading: paiesLoading, isError: paiesError, error: paiesErrorObj } = usePaies(id); // State pour la modale de confirmation de paiement @@ -457,7 +463,7 @@ export default function ContratMultiPage() { if (isLoading) { return ( -
+
Chargement du contrat

@@ -466,11 +472,11 @@ export default function ContratMultiPage() { if (isError || !data) { return ( -
+
Impossible de charger ce contrat.
{(error as any)?.message || "Erreur inconnue"}
- + Retour aux contrats
@@ -489,10 +495,10 @@ export default function ContratMultiPage() {
-
+
{title}
-
+
CDDU · Multi-mois
@@ -593,7 +599,7 @@ export default function ContratMultiPage() { + Effectuée )} @@ -605,7 +611,7 @@ export default function ContratMultiPage() { + En cours )} @@ -654,7 +660,7 @@ export default function ContratMultiPage() { const aemPending = aemNorm.includes('a_traiter') || aemNorm.includes('a traiter') || aemNorm.includes('traiter'); const CardInner = ( -
+
{/* Bouton de paiement en position absolue */} {p.traite === 'oui' && ( - +
+ +
{/* Onglets + action */}
-
- - +
+ +
{status === "termines" && (
-
Filtrer par période :
+
Filtrer par période :
{ setYear(parseInt(e.target.value,10)); setPage(1); }} - className="px-3 py-2 rounded-lg border dark:border-slate-800 bg-white dark:bg-slate-900 text-sm" + className="px-3 py-2 rounded-lg border bg-white text-sm" > {yearOptions.map(y => )} @@ -250,11 +250,11 @@ export default function ContratsClient({ clientInfo }: { clientInfo: ClientInfo
{/* Tableau */} -
+
- + @@ -274,7 +274,7 @@ export default function ContratsClient({ clientInfo }: { clientInfo: ClientInfo ) : ( items.map((c)=> ( - + @@ -304,10 +304,10 @@ export default function ContratsClient({ clientInfo }: { clientInfo: ClientInfo {/* Pagination */} -
- +
+
Page {page}
- +
{isFetching ? 'Mise à jour
' : `${items.length} élément${items.length>1?'s':''}${hasMore ? ' (plus disponibles)' : ''}`}
diff --git a/app/(app)/contrats/[id]/edit/formulaire/page.tsx b/app/(app)/contrats/[id]/edit/formulaire/page.tsx index 2e35ce7..a656e3c 100644 --- a/app/(app)/contrats/[id]/edit/formulaire/page.tsx +++ b/app/(app)/contrats/[id]/edit/formulaire/page.tsx @@ -138,9 +138,9 @@ export default function EditFormulairePage() { return (
-
+
Modifier la demande
-
+
Contrat {data.numero || data.id}
diff --git a/app/(app)/contrats/[id]/edit/page.tsx b/app/(app)/contrats/[id]/edit/page.tsx index b81a4bc..6fdd78d 100644 --- a/app/(app)/contrats/[id]/edit/page.tsx +++ b/app/(app)/contrats/[id]/edit/page.tsx @@ -5,6 +5,7 @@ import { useParams, useRouter } from "next/navigation"; import { useQuery } from "@tanstack/react-query"; import { Loader2, RefreshCw, Info } from "lucide-react"; import { api } from "@/lib/fetcher"; +import { usePageTitle } from "@/hooks/usePageTitle"; /** Types minimalistes — adapte si besoin Ă  ta rĂ©ponse API */ type ContratDetail = { @@ -62,12 +63,12 @@ function StatusBadge({ value }: { value: string }) { const v = value.toLowerCase(); const color = v.includes("traite") || v.includes("valid") - ? "bg-emerald-100 text-emerald-800 dark:bg-emerald-900/30 dark:text-emerald-200" + ? "bg-emerald-100 text-emerald-800" : v.includes("cours") || v.includes("processing") - ? "bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-200" + ? "bg-amber-100 text-amber-800" : v.includes("attente") || v.includes("recu") || v.includes("reçue") - ? "bg-sky-100 text-sky-800 dark:bg-sky-900/30 dark:text-sky-200" - : "bg-slate-100 text-slate-800 dark:bg-slate-800/60 dark:text-slate-200"; + ? "bg-sky-100 text-sky-800" + : "bg-slate-100 text-slate-800"; return ( {value} @@ -79,6 +80,8 @@ function StatusBadge({ value }: { value: string }) { export default function ContratEtatPage() { const { id } = useParams<{ id: string }>(); const router = useRouter(); + + usePageTitle("Édition de contrat"); const [showCancelModal, setShowCancelModal] = useState(false); const [isCancelling, setIsCancelling] = useState(false); @@ -196,9 +199,9 @@ export default function ContratEtatPage() { logStep("UI loading"); return (
-
+
-
Chargement du contrat

+
Chargement du contrat

); @@ -208,20 +211,20 @@ export default function ContratEtatPage() { logStep("UI error", { error }); return (
-
+
Impossible de récupérer le contrat
-

+

Merci de réessayer un peu plus tard. Si le problÚme persiste, contacte le support.

{error && ( -
+            
               {String((error as any)?.message ?? error)}
             
)}
{/* Carte état */} -
+
-
+
Salarié
{Array.isArray((data as any).salarie?.nom) ? (data as any).salarie.nom.join(", ") : (data as any).salarie?.nom ?? "—"}
-
+
Production
{data.production ?? "—"} {data.numero_objet ? — n° d’objet {data.numero_objet} : null}
-
+
Profession
{data.profession ?? "—"}
-
+
Période
{formatDateFr(data.date_debut)} {data.date_fin ? `→ ${formatDateFr(data.date_fin)}` : ""} @@ -284,8 +287,8 @@ export default function ContratEtatPage() {
{isRecue ? ( -
-
+
+
Le traitement de cette demande par nos services n'ayant pas encore commencé, vous pouvez directement la modifier depuis l'Espace Paie. Cliquez ci-dessous pour accéder au formulaire de modification.
@@ -307,8 +310,8 @@ export default function ContratEtatPage() {
) : ( -
-
+
+
Cette demande n'est plus modifiable directement depuis l'Espace Paie car son traitement par nos services a commencé. Nous vous invitons à demander une modification manuelle via le bouton ci-dessous.
@@ -328,9 +331,9 @@ export default function ContratEtatPage() { {showCancelModal && (
setShowCancelModal(false)} /> -
+
Confirmer l'annulation ?
-

+

Vous ĂȘtes sur le point d'annuler cette demande. Le contrat sera supprimĂ© de votre Espace Paie et ne sera pas facturĂ©.

@@ -338,7 +341,7 @@ export default function ContratEtatPage() { type="button" onClick={() => setShowCancelModal(false)} disabled={isCancelling} - className="px-3 py-2 rounded-lg border dark:border-slate-800 disabled:opacity-50" + className="px-3 py-2 rounded-lg border disabled:opacity-50" > Fermer diff --git a/app/(app)/contrats/[id]/modification/page.tsx b/app/(app)/contrats/[id]/modification/page.tsx index e968991..4afda2a 100644 --- a/app/(app)/contrats/[id]/modification/page.tsx +++ b/app/(app)/contrats/[id]/modification/page.tsx @@ -22,7 +22,7 @@ type ContratDetail = { function Card({ children }: { children: React.ReactNode }) { return ( -
+
{children}
); @@ -150,7 +150,7 @@ ${message} {/* En-tĂȘte contrat */} -
+
Contrat : {data.numero} ‱{" "} {data.regime === "CDDU_MONO" @@ -212,7 +212,7 @@ ${message} value={message} onChange={(e) => setMessage(e.target.value)} placeholder="Expliquez prĂ©cisĂ©ment votre demande de modification (dates, rĂ©munĂ©ration, fonction, etc.)." - className="w-full px-3 py-2 rounded-lg border bg-white dark:bg-slate-900 dark:border-slate-800 text-sm" + className="w-full px-3 py-2 rounded-lg border bg-white text-sm" />
@@ -221,7 +221,7 @@ ${message}
Annuler @@ -246,11 +246,11 @@ ${message} {/* Overlay succĂšs */} {sent && ( -
-
+
+
Ticket créé avec succÚs
-

+

Redirection vers votre demande de support


diff --git a/app/(app)/contrats/[id]/page.tsx b/app/(app)/contrats/[id]/page.tsx index df08df4..1ba93c5 100644 --- a/app/(app)/contrats/[id]/page.tsx +++ b/app/(app)/contrats/[id]/page.tsx @@ -20,6 +20,7 @@ function formatEUR(value?: string | number | null): string | undefined { import Link from "next/link"; import Script from "next/script"; +import { usePageTitle } from "@/hooks/usePageTitle"; // ---------- Hook rĂ©cupĂ©ration fiches de paie Supabase ---------- // ...existing code... @@ -45,6 +46,54 @@ type Payslip = { }; function usePayslips(contractId: string) { + // 🎭 DĂ©tection directe du mode dĂ©mo + const isDemoMode = typeof window !== 'undefined' && window.location.hostname === 'demo.odentas.fr'; + + console.log('🔍 usePayslips debug:', { + isDemoMode, + hostname: typeof window !== 'undefined' ? window.location.hostname : 'server', + contractId + }); + + // 🎭 Mode dĂ©mo : utiliser les donnĂ©es fictives pour les contrats demo + if (isDemoMode && contractId === 'demo-cont-001') { + console.log('🎭 Demo mode detected, loading demo payslips...'); + + const DEMO_PAYSLIPS: Payslip[] = [ + { + id: "demo-payslip-001", + contract_id: "demo-cont-001", + period_start: "2024-01-15", + period_end: "2024-06-30", + period_month: "2024-06", + pay_number: 1, + gross_amount: "850.00", + net_amount: "623.45", + net_after_withholding: "623.45", + employer_cost: "1247.50", + pay_date: "2024-07-15", + processed: true, + aem_status: "valide", + transfer_done: true, + analytic_tag: "SPECTACLE-2024", + storage_path: "/demo/payslips/demo-payslip-001.pdf", + source_reference: "DEMO-PAY-001", + created_at: "2024-07-01T10:00:00Z" + } + ]; + + console.log('✅ Demo payslips loaded:', DEMO_PAYSLIPS.length); + + return { + data: DEMO_PAYSLIPS, + isLoading: false, + error: null, + isError: false, + isFetching: false + }; + } + + // Mode normal : rĂ©cupĂ©ration via API return useQuery({ queryKey: ["payslips", contractId], queryFn: async () => { @@ -217,6 +266,88 @@ type ContratDetail = { // ---------- Data hooks ---------- function useContratDetail(id: string) { + // 🎭 DĂ©tection directe du mode dĂ©mo + const isDemoMode = typeof window !== 'undefined' && window.location.hostname === 'demo.odentas.fr'; + + console.log('🔍 useContratDetail debug:', { + isDemoMode, + hostname: typeof window !== 'undefined' ? window.location.hostname : 'server', + contractId: id + }); + + // 🎭 Mode dĂ©mo : utiliser les donnĂ©es fictives pour l'ID demo + if (isDemoMode && id === 'demo-cont-001') { + console.log('🎭 Demo mode detected, loading demo contract details...'); + + const DEMO_CONTRACT_DETAIL: ContratDetail = { + id: "demo-cont-001", + numero: "DEMO-2024-001", + regime: "CDDU_MONO", + salarie: { + nom: "MARTIN Alice", + email: "alice.martin@demo.fr" + }, + salarie_matricule: "demo-sal-001", + production: "Les MisĂ©rables - TournĂ©e 2024", + objet: "PROD-2024-15", + profession: "04201 - ComĂ©dien", + categorie_prof: "Artiste interprĂšte", + type_salaire: "Forfait cachet", + salaire_demande: "850,00€", + date_debut: "2024-01-15", + date_fin: "2024-06-30", + panier_repas: "oui", + + // PDFs et documents + pdf_contrat: { available: true, url: "/demo/contrat-demo.pdf" }, + pdf_avenant: { available: false }, + pdf_paie: { available: true, url: "/demo/paie-demo.pdf" }, + + // États et statuts + etat_traitement: "termine", + virement_effectue: true, + salaire_net_avant_pas: "623,45€", + net_a_payer_rib: "623,45€", + salaire_brut: "850,00€", + cout_employeur: "1.247,50€", + precisions_salaire: "Contrat dĂ©mo - Tarif spectacle vivant", + + // Signatures et contrat + etat_demande: "TraitĂ©e", + contrat_signe_employeur: "oui", + contrat_signe_salarie: "oui", + etat_contrat: "termine", + + // DĂ©clarations + dpae: "OK", + aem: "OK", + + // Temps de travail + jours_travailles: 25, + nb_representations: 18, + nb_services_repetitions: 12, + nb_heures_repetitions: 48, + nb_heures_annexes: 8, + nb_cachets_aem: 18, + nb_heures_aem: 0, + + // MĂ©tadonnĂ©es + created_at: "2024-01-10T09:00:00Z", + updated_at: "2024-07-01T16:30:00Z" + }; + + console.log('✅ Demo contract details loaded'); + + return { + data: DEMO_CONTRACT_DETAIL, + isLoading: false, + error: null, + isError: false, + isFetching: false + }; + } + + // Mode normal : rĂ©cupĂ©ration via API return useQuery({ queryKey: ["contrat", id], queryFn: async () => { @@ -303,9 +434,9 @@ function useToggleVirement(id: string) { function Section({ title, icon: Icon, children }: { title: string; icon?: React.ElementType; children: React.ReactNode }) { return ( - + - {Icon && } + {Icon && } {title} @@ -334,11 +465,11 @@ function Badge({ tone?: "default" | "ok" | "warn" | "error" | "info"; }) { const cls = - tone === "ok" ? "bg-emerald-100 text-emerald-800 dark:bg-emerald-900/40 dark:text-emerald-200" : - tone === "warn" ? "bg-amber-100 text-amber-800 dark:bg-amber-900/40 dark:text-amber-200" : - tone === "error" ? "bg-rose-100 text-rose-800 dark:bg-rose-900/40 dark:text-rose-200" : - tone === "info" ? "bg-sky-100 text-sky-800 dark:bg-sky-900/40 dark:text-sky-200" : - "bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-300"; + tone === "ok" ? "bg-emerald-100 text-emerald-800" : + tone === "warn" ? "bg-amber-100 text-amber-800" : + tone === "error" ? "bg-rose-100 text-rose-800" : + tone === "info" ? "bg-sky-100 text-sky-800" : + "bg-slate-100 text-slate-700"; return {children}; } @@ -367,11 +498,11 @@ function stateBadgeDemande(s: EtatDemande | string) { // - En cours d'envoi: indigo type Style = { bg: string; border: string; text: string; label: string }; const styles: Record = { - "Reçue": { bg: "bg-sky-50 dark:bg-sky-900/30", border: "border-sky-200 dark:border-sky-800/60", text: "text-sky-800 dark:text-sky-200", label: "Reçue" }, - "PrĂ©-demande": { bg: "bg-slate-50 dark:bg-slate-800/40", border: "border-slate-200 dark:border-slate-700/60", text: "text-slate-700 dark:text-slate-300", label: "PrĂ©-demande" }, - "En cours de traitement": { bg: "bg-amber-50 dark:bg-amber-900/20", border: "border-amber-200 dark:border-amber-800/60", text: "text-amber-800 dark:text-amber-200", label: "En cours de traitement" }, - "TraitĂ©e": { bg: "bg-emerald-50 dark:bg-emerald-900/20", border: "border-emerald-200 dark:border-emerald-800/60", text: "text-emerald-800 dark:text-emerald-200", label: "TraitĂ©e" }, - "En cours d'envoi": { bg: "bg-indigo-50 dark:bg-indigo-900/20", border: "border-indigo-200 dark:border-indigo-800/60", text: "text-indigo-800 dark:text-indigo-200", label: "En cours d'envoi" }, + "Reçue": { bg: "bg-sky-50", border: "border-sky-200", text: "text-sky-800", label: "Reçue" }, + "PrĂ©-demande": { bg: "bg-slate-50", border: "border-slate-200", text: "text-slate-700", label: "PrĂ©-demande" }, + "En cours de traitement": { bg: "bg-amber-50", border: "border-amber-200", text: "text-amber-800", label: "En cours de traitement" }, + "TraitĂ©e": { bg: "bg-emerald-50", border: "border-emerald-200", text: "text-emerald-800", label: "TraitĂ©e" }, + "En cours d'envoi": { bg: "bg-indigo-50", border: "border-indigo-200", text: "text-indigo-800", label: "En cours d'envoi" }, }; // Normalisations alternatives depuis l'API @@ -383,7 +514,7 @@ function stateBadgeDemande(s: EtatDemande | string) { styles[normalized] || styles[alt] || // fallback gĂ©nĂ©rique - { bg: "bg-slate-50 dark:bg-slate-800/40", border: "border-slate-200 dark:border-slate-700/60", text: "text-slate-700 dark:text-slate-300", label: input || "—" }; + { bg: "bg-slate-50", border: "border-slate-200", text: "text-slate-700", label: input || "—" }; return (
@@ -401,28 +532,28 @@ function formatDateFR(iso?: string) { // ---------- Composant d'erreur d'accĂšs ---------- function AccessDeniedError({ contractId }: { contractId: string }) { return ( -
+
-
- +
+
-

+

AccÚs non autorisé

-

+

Vous n'avez pas l'autorisation de consulter ce contrat. Il est possible qu'il appartienne Ă  une autre organisation ou qu'il n'existe pas.

-
+
ID: {contractId}
Retour aux contrats @@ -438,6 +569,13 @@ export default function ContratPage() { const router = useRouter(); const { data, isLoading, isError, error } = useContratDetail(id); + + // Définir le titre basé sur les données du contrat + const contractTitle = data?.numero + ? `Contrat ${data.numero}` + : `Contrat CDDU`; + usePageTitle(contractTitle); + const payslipsQuery = usePayslips(id); const [signedPayslipUrls, setSignedPayslipUrls] = useState>({}); @@ -720,7 +858,7 @@ export default function ContratPage() { if (isLoading) { return ( -
+
Chargement du contrat

@@ -737,11 +875,11 @@ export default function ContratPage() { if (errorMessage === "not_found") { return ( -
+
Contrat introuvable
Le contrat demandé n'existe pas ou a été supprimé.
- + Retour aux contrats
@@ -751,11 +889,11 @@ export default function ContratPage() { // Erreur générique return ( -
+
Impossible de charger ce contrat.
{errorMessage || "Erreur inconnue"}
- + Retour aux contrats
@@ -970,29 +1108,29 @@ return ( router.push(`/contrats/nouveau?dupe=${encodeURIComponent(b64)}`); }} - className="inline-flex items-center gap-2 px-3 py-2 rounded-lg border dark:border-slate-800" + className="inline-flex items-center gap-2 px-3 py-2 rounded-lg border" title="Dupliquer ce contrat" > Dupliquer - + Modifier -
-
+
{/* Titre du contrat - 1 colonne */}
{title}
-
+
CDDU
@@ -1001,7 +1139,7 @@ return (
{/* Ligne de progression */} -
+
{(() => { const steps = getTimelineSteps(data.etat_demande, data.contrat_signe_salarie, payslipsQuery.data); const completedSteps = steps.filter(s => s.status === "completed").length; @@ -1018,26 +1156,26 @@ return ( {/* Étapes */}
{getTimelineSteps(data.etat_demande, data.contrat_signe_salarie, payslipsQuery.data).map((step, index) => ( -
+
{step.status === "completed" ? ( ) : step.status === "current" ? ( ) : ( - + )}
{step.label}
@@ -1208,11 +1346,11 @@ return ( label="DPAE" value={ data.dpae === "OK" ? ( - + EffectuĂ©e ) : data.dpae === "À traiter" ? ( - + En cours ) : ( @@ -1228,7 +1366,7 @@ return (
Chargement des fiches de paie

) : payslipsQuery.data && payslipsQuery.data.length > 0 ? ( payslipsQuery.data.map((slip) => ( -
+
{/* Bouton de paiement en position absolue dans le coin bas droit */} {slip.processed && (
+ + {/* Messages d'erreur et succĂšs */} + {submitError && ( +
+ {submitError} +
+ )} + {submitSuccess && ( +
+ ✅ Contrats créés avec succĂšs ! Redirection vers la liste des contrats... +
+ )} {validateTipOpen && !canSubmit && validateTipPos && createPortal( (() => { const top = validateTipPos.top - 10; // au-dessus @@ -1053,12 +1323,55 @@ export default function Page() { )}
+ {isStaff && ( +
+ + + {isStaff && !selectedOrg && ( +

+ Une organisation doit ĂȘtre sĂ©lectionnĂ©e avant de pouvoir valider la saisie. +

+ )} +
+ )}
{ + if (typeof v === 'string') { + // Quand l'utilisateur tape, on crée un objet temporaire avec le nom + setProduction({ nom: v, numero_objet: null }); + } else { + setProduction(v); + } + }} + onSelect={(v) => { + if (typeof v === 'object' && v !== null) { + setProduction(v); + } else if (typeof v === 'string' && v.trim()) { + // Si l'utilisateur tape une nouvelle production, on crée un objet temporaire + setProduction({ nom: v.trim(), numero_objet: null }); + } else { + setProduction(null); + } + }} placeholder="Rechercher ou saisir une production
" searchType="productions" className="mt-1" @@ -1147,7 +1460,7 @@ export default function Page() {
État RĂ©fĂ©rence Nom
{status==='en_cours' ? 'Aucun contrat en cours.' : 'Aucun contrat terminé.'}
{(() => { const e = safeEtat(c.etat as any); return ( {e.label} @@ -284,7 +284,7 @@ export default function ContratsClient({ clientInfo }: { clientInfo: ClientInfo
{c.reference} {(c.is_multi_mois === true || (c.regime && c.regime.toUpperCase() === "CDDU_MULTI")) && ( - Multi‑mois + Multi‑mois )}
updateCell(row.id, "salarie", v)} onSelect={(v) => updateCell(row.id, "salarie", v)} placeholder="Rechercher un salarié " @@ -1179,9 +1492,10 @@ export default function Page() { {row.role === 'ARTISTE' ? ( updateCell(row.id, 'profession', v)} + onChange={(v) => updateCell(row.id, 'profession', typeof v === 'string' ? v : String(v || ''))} onSelect={(v) => { - const label = v.includes(' — ') ? v.split(' — ')[0] : v; + const strValue = typeof v === 'string' ? v : String(v || ''); + const label = strValue.includes(' — ') ? strValue.split(' — ')[0] : strValue; updateCell(row.id, 'profession', label); }} placeholder="Rechercher une profession (Artiste)
" @@ -1192,9 +1506,10 @@ export default function Page() { ) : ( updateCell(row.id, 'profession', v)} + onChange={(v) => updateCell(row.id, 'profession', typeof v === 'string' ? v : String(v || ''))} onSelect={(v) => { - const label = v.includes(' — ') ? v.split(' — ')[0] : v; + const strValue = typeof v === 'string' ? v : String(v || ''); + const label = strValue.includes(' — ') ? strValue.split(' — ')[0] : strValue; updateCell(row.id, 'profession', label); }} placeholder="Rechercher une profession (Technicien)
" diff --git a/app/(app)/cotisations/page.tsx b/app/(app)/cotisations/page.tsx index 69b8ca8..0768d64 100644 --- a/app/(app)/cotisations/page.tsx +++ b/app/(app)/cotisations/page.tsx @@ -5,6 +5,7 @@ import { useSearchParams, usePathname, useRouter } from "next/navigation"; import { useQuery } from "@tanstack/react-query"; import { api } from "@/lib/fetcher"; import { Calendar, Loader2 } from "lucide-react"; +import { usePageTitle } from "@/hooks/usePageTitle"; /* ========================= Types attendus du backend @@ -150,9 +151,9 @@ function paymentWindowLabel(annee: number, mois: number) { function Section({ title, children, actions }: { title: string; children: React.ReactNode; actions?: React.ReactNode }) { return ( -
-
-
{title}
+
+
+
{title}
{actions ?
{actions}
: null}
{children}
@@ -164,6 +165,8 @@ function Section({ title, children, actions }: { title: string; children: React. Page ===== */ export default function CotisationsMensuellesPage() { + usePageTitle("Cotisations mensuelles"); + const now = new Date(); const handlePeriodChange = (value: Filters["period"]) => { @@ -304,9 +307,9 @@ export default function CotisationsMensuellesPage() { return (
{/* Bandeau titre + aide */} -
+
Vos cotisations mensuelles
-

+

Les tĂ©lĂ©paiements s’effectuent entre le 15 et le 30 du mois suivant les salaires concernĂ©s. Par exemple, pour les salaires de septembre, les tĂ©lĂ©paiements ont lieu entre le 15 et le 30 octobre.

@@ -318,7 +321,7 @@ export default function CotisationsMensuellesPage() {
handlePeriodChange(e.target.value as Filters["period"])} > @@ -384,7 +387,7 @@ export default function CotisationsMensuellesPage() {