create-crm-tmp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/create-crm-tmp.js +93 -0
- package/package.json +25 -0
- package/template/.prettierignore +33 -0
- package/template/.prettierrc.json +25 -0
- package/template/README.md +173 -0
- package/template/eslint.config.mjs +18 -0
- package/template/exemple-contacts.csv +11 -0
- package/template/next.config.ts +8 -0
- package/template/package.json +64 -0
- package/template/postcss.config.mjs +7 -0
- package/template/prisma/migrations/20251126144728_init/migration.sql +78 -0
- package/template/prisma/migrations/20251126155204_add_user_roles/migration.sql +5 -0
- package/template/prisma/migrations/20251128095126_add_company_info/migration.sql +19 -0
- package/template/prisma/migrations/20251128123321_add_smtp_config/migration.sql +22 -0
- package/template/prisma/migrations/20251128132303_add_status/migration.sql +23 -0
- package/template/prisma/migrations/20251201102207_add_user_active/migration.sql +75 -0
- package/template/prisma/migrations/20251201105507_add_email_signature/migration.sql +2 -0
- package/template/prisma/migrations/20251201151122_add_tasks/migration.sql +45 -0
- package/template/prisma/migrations/20251202111854_add_task_reminder/migration.sql +2 -0
- package/template/prisma/migrations/20251202135859_add_google_meet_integration/migration.sql +27 -0
- package/template/prisma/migrations/20251203103317_add_meta_lead_integration/migration.sql +20 -0
- package/template/prisma/migrations/20251203104002_add_google_ads_integration/migration.sql +18 -0
- package/template/prisma/migrations/20251203112122_add_google_sheet_integration/migration.sql +32 -0
- package/template/prisma/migrations/20251203153853_allow_multiple_integration_configs/migration.sql +20 -0
- package/template/prisma/migrations/20251205141705_update_user_roles/migration.sql +12 -0
- package/template/prisma/migrations/20251205150000_add_commercial_and_telepro_assignment/migration.sql +21 -0
- package/template/prisma/migrations/20251205160000_add_interaction_logging/migration.sql +11 -0
- package/template/prisma/migrations/20251208090314_add_automatic_interaction_types/migration.sql +12 -0
- package/template/prisma/migrations/20251208094843_mg/migration.sql +14 -0
- package/template/prisma/migrations/20251208100000_add_company_support/migration.sql +14 -0
- package/template/prisma/migrations/20251208110000_add_templates/migration.sql +26 -0
- package/template/prisma/migrations/20251208141304_add_video_conference_task_type/migration.sql +2 -0
- package/template/prisma/migrations/20251209104759_add_internal_note_to_task/migration.sql +2 -0
- package/template/prisma/migrations/20251209134803_add_company_field/migration.sql +2 -0
- package/template/prisma/migrations/20251209150000_rename_company_to_company_name/migration.sql +3 -0
- package/template/prisma/migrations/20251209150016_add_email_tracking/migration.sql +21 -0
- package/template/prisma/migrations/20251209155908_add_notify_contact_to_task/migration.sql +2 -0
- package/template/prisma/migrations/20251210110019_add_appointment_types/migration.sql +10 -0
- package/template/prisma/migrations/20251210113928_add_contact_files/migration.sql +26 -0
- package/template/prisma/migrations/20251212132339_add_custom_roles/migration.sql +24 -0
- package/template/prisma/migrations/20251215104448_add_file_interaction_types/migration.sql +11 -0
- package/template/prisma/migrations/20251215145616_add_closing_reasons/migration.sql +12 -0
- package/template/prisma/migrations/20251216140850_add_log_users/migration.sql +25 -0
- package/template/prisma/migrations/20251216151000_rename_perdu_to_ferme/migration.sql +8 -0
- package/template/prisma/migrations/20251216162318_add_column_mappings_to_google_sheet/migration.sql +2 -0
- package/template/prisma/migrations/20251216185127_add_workflows/migration.sql +80 -0
- package/template/prisma/migrations/20251216192237_add_scheduled_workflow_actions/migration.sql +32 -0
- package/template/prisma/migrations/migration_lock.toml +3 -0
- package/template/prisma/schema.prisma +582 -0
- package/template/prisma.config.ts +14 -0
- package/template/src/app/(auth)/invite/[token]/page.tsx +200 -0
- package/template/src/app/(auth)/layout.tsx +3 -0
- package/template/src/app/(auth)/reset-password/complete/page.tsx +213 -0
- package/template/src/app/(auth)/reset-password/page.tsx +146 -0
- package/template/src/app/(auth)/reset-password/verify/page.tsx +183 -0
- package/template/src/app/(auth)/signin/page.tsx +166 -0
- package/template/src/app/(dashboard)/agenda/page.tsx +3051 -0
- package/template/src/app/(dashboard)/automatisation/[id]/page.tsx +24 -0
- package/template/src/app/(dashboard)/automatisation/_components/workflow-editor.tsx +905 -0
- package/template/src/app/(dashboard)/automatisation/new/page.tsx +20 -0
- package/template/src/app/(dashboard)/automatisation/page.tsx +337 -0
- package/template/src/app/(dashboard)/closing/page.tsx +1052 -0
- package/template/src/app/(dashboard)/contacts/[id]/page.tsx +6028 -0
- package/template/src/app/(dashboard)/contacts/page.tsx +3713 -0
- package/template/src/app/(dashboard)/dashboard/page.tsx +186 -0
- package/template/src/app/(dashboard)/layout.tsx +30 -0
- package/template/src/app/(dashboard)/settings/page.tsx +4070 -0
- package/template/src/app/(dashboard)/templates/page.tsx +567 -0
- package/template/src/app/(dashboard)/users/list/page.tsx +507 -0
- package/template/src/app/(dashboard)/users/page.tsx +457 -0
- package/template/src/app/(dashboard)/users/permissions/page.tsx +181 -0
- package/template/src/app/(dashboard)/users/roles/page.tsx +434 -0
- package/template/src/app/api/audit-logs/route.ts +57 -0
- package/template/src/app/api/auth/[...all]/route.ts +4 -0
- package/template/src/app/api/auth/check-active/route.ts +31 -0
- package/template/src/app/api/auth/google/callback/route.ts +94 -0
- package/template/src/app/api/auth/google/disconnect/route.ts +32 -0
- package/template/src/app/api/auth/google/route.ts +34 -0
- package/template/src/app/api/auth/google/status/route.ts +32 -0
- package/template/src/app/api/closing-reasons/route.ts +27 -0
- package/template/src/app/api/contacts/[id]/files/[fileId]/route.ts +94 -0
- package/template/src/app/api/contacts/[id]/files/route.ts +269 -0
- package/template/src/app/api/contacts/[id]/interactions/[interactionId]/route.ts +91 -0
- package/template/src/app/api/contacts/[id]/interactions/route.ts +103 -0
- package/template/src/app/api/contacts/[id]/meet/route.ts +296 -0
- package/template/src/app/api/contacts/[id]/route.ts +322 -0
- package/template/src/app/api/contacts/[id]/send-email/route.ts +254 -0
- package/template/src/app/api/contacts/export/route.ts +270 -0
- package/template/src/app/api/contacts/import/route.ts +381 -0
- package/template/src/app/api/contacts/route.ts +283 -0
- package/template/src/app/api/dashboard/stats/route.ts +299 -0
- package/template/src/app/api/email/track/[id]/route.ts +68 -0
- package/template/src/app/api/integrations/google-sheet/sync/route.ts +526 -0
- package/template/src/app/api/invite/complete/route.ts +88 -0
- package/template/src/app/api/invite/validate/route.ts +55 -0
- package/template/src/app/api/reminders/route.ts +95 -0
- package/template/src/app/api/reset-password/complete/route.ts +73 -0
- package/template/src/app/api/reset-password/request/route.ts +84 -0
- package/template/src/app/api/reset-password/validate/route.ts +49 -0
- package/template/src/app/api/reset-password/verify/route.ts +74 -0
- package/template/src/app/api/roles/[id]/route.ts +183 -0
- package/template/src/app/api/roles/route.ts +140 -0
- package/template/src/app/api/send/route.ts +282 -0
- package/template/src/app/api/settings/change-password/route.ts +95 -0
- package/template/src/app/api/settings/closing-reasons/[id]/route.ts +84 -0
- package/template/src/app/api/settings/closing-reasons/route.ts +74 -0
- package/template/src/app/api/settings/company/route.ts +121 -0
- package/template/src/app/api/settings/google-ads/[id]/route.ts +117 -0
- package/template/src/app/api/settings/google-ads/route.ts +122 -0
- package/template/src/app/api/settings/google-sheet/[id]/route.ts +230 -0
- package/template/src/app/api/settings/google-sheet/auto-map/route.ts +196 -0
- package/template/src/app/api/settings/google-sheet/route.ts +254 -0
- package/template/src/app/api/settings/meta-leads/[id]/route.ts +123 -0
- package/template/src/app/api/settings/meta-leads/route.ts +132 -0
- package/template/src/app/api/settings/profile/route.ts +42 -0
- package/template/src/app/api/settings/smtp/route.ts +130 -0
- package/template/src/app/api/settings/smtp/test/route.ts +121 -0
- package/template/src/app/api/settings/statuses/[id]/route.ts +101 -0
- package/template/src/app/api/settings/statuses/route.ts +83 -0
- package/template/src/app/api/statuses/route.ts +25 -0
- package/template/src/app/api/tasks/[id]/attendees/route.ts +76 -0
- package/template/src/app/api/tasks/[id]/route.ts +728 -0
- package/template/src/app/api/tasks/meet/route.ts +240 -0
- package/template/src/app/api/tasks/route.ts +417 -0
- package/template/src/app/api/templates/[id]/route.ts +140 -0
- package/template/src/app/api/templates/route.ts +91 -0
- package/template/src/app/api/users/[id]/route.ts +168 -0
- package/template/src/app/api/users/list/route.ts +45 -0
- package/template/src/app/api/users/me/route.ts +48 -0
- package/template/src/app/api/users/route.ts +250 -0
- package/template/src/app/api/webhooks/google-ads/route.ts +208 -0
- package/template/src/app/api/webhooks/meta-leads/route.ts +258 -0
- package/template/src/app/api/workflows/[id]/route.ts +192 -0
- package/template/src/app/api/workflows/process/route.ts +293 -0
- package/template/src/app/api/workflows/route.ts +124 -0
- package/template/src/app/favicon.ico +0 -0
- package/template/src/app/globals.css +1416 -0
- package/template/src/app/layout.tsx +31 -0
- package/template/src/app/page.tsx +32 -0
- package/template/src/components/dashboard/activity-chart.tsx +67 -0
- package/template/src/components/dashboard/contacts-chart.tsx +63 -0
- package/template/src/components/dashboard/recent-activity.tsx +164 -0
- package/template/src/components/dashboard/sales-analytics-chart.tsx +81 -0
- package/template/src/components/dashboard/stat-card.tsx +61 -0
- package/template/src/components/dashboard/status-distribution-chart.tsx +45 -0
- package/template/src/components/dashboard/tasks-pie-chart.tsx +88 -0
- package/template/src/components/dashboard/top-contacts-list.tsx +129 -0
- package/template/src/components/dashboard/upcoming-tasks-list.tsx +126 -0
- package/template/src/components/editor.tsx +856 -0
- package/template/src/components/email-template.tsx +35 -0
- package/template/src/components/header.tsx +320 -0
- package/template/src/components/invitation-email-template.tsx +79 -0
- package/template/src/components/meet-cancellation-email-template.tsx +120 -0
- package/template/src/components/meet-confirmation-email-template.tsx +156 -0
- package/template/src/components/meet-update-email-template.tsx +209 -0
- package/template/src/components/page-header.tsx +61 -0
- package/template/src/components/reset-password-email-template.tsx +79 -0
- package/template/src/components/sidebar.tsx +294 -0
- package/template/src/components/skeleton.tsx +380 -0
- package/template/src/components/ui/commands.tsx +396 -0
- package/template/src/components/ui/components.tsx +150 -0
- package/template/src/components/ui/theme.tsx +5 -0
- package/template/src/components/view-as-banner.tsx +45 -0
- package/template/src/components/view-as-modal.tsx +186 -0
- package/template/src/contexts/mobile-menu-context.tsx +31 -0
- package/template/src/contexts/sidebar-context.tsx +107 -0
- package/template/src/contexts/task-reminder-context.tsx +239 -0
- package/template/src/contexts/view-as-context.tsx +84 -0
- package/template/src/hooks/use-user-role.ts +82 -0
- package/template/src/lib/audit-log.ts +45 -0
- package/template/src/lib/auth-client.ts +16 -0
- package/template/src/lib/auth.ts +35 -0
- package/template/src/lib/check-permission.ts +193 -0
- package/template/src/lib/contact-duplicate.ts +112 -0
- package/template/src/lib/contact-interactions.ts +371 -0
- package/template/src/lib/encryption.ts +99 -0
- package/template/src/lib/google-calendar.ts +300 -0
- package/template/src/lib/google-drive.ts +372 -0
- package/template/src/lib/permissions.ts +412 -0
- package/template/src/lib/prisma.ts +32 -0
- package/template/src/lib/roles.ts +120 -0
- package/template/src/lib/template-variables.ts +76 -0
- package/template/src/lib/utils.ts +46 -0
- package/template/src/lib/workflow-executor.ts +482 -0
- package/template/src/proxy.ts +91 -0
- package/template/tsconfig.json +34 -0
- package/template/vercel.json +8 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require("fs-extra");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const chalk = require("chalk");
|
|
6
|
+
const prompts = require("prompts");
|
|
7
|
+
|
|
8
|
+
async function main() {
|
|
9
|
+
const args = process.argv.slice(2);
|
|
10
|
+
const projectName = args[0];
|
|
11
|
+
|
|
12
|
+
if (!projectName) {
|
|
13
|
+
console.error(chalk.red("❌ Veuillez spécifier un nom de projet"));
|
|
14
|
+
console.log(chalk.yellow("\nUsage: pnpm create crm-tmp@latest mon-crm\n"));
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const targetDir = path.resolve(process.cwd(), projectName);
|
|
19
|
+
|
|
20
|
+
// Vérifier si le dossier existe déjà
|
|
21
|
+
if (fs.existsSync(targetDir)) {
|
|
22
|
+
const { overwrite } = await prompts({
|
|
23
|
+
type: "confirm",
|
|
24
|
+
name: "overwrite",
|
|
25
|
+
message: `Le dossier "${projectName}" existe déjà. Voulez-vous le remplacer ?`,
|
|
26
|
+
initial: false,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
if (!overwrite) {
|
|
30
|
+
console.log(chalk.yellow("❌ Opération annulée"));
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
fs.removeSync(targetDir);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.log(chalk.blue(`\n🚀 Création du projet CRM "${projectName}"...\n`));
|
|
38
|
+
|
|
39
|
+
// Copier le template
|
|
40
|
+
const templateDir = path.join(__dirname, "..", "template");
|
|
41
|
+
fs.copySync(templateDir, targetDir);
|
|
42
|
+
|
|
43
|
+
// Lire le package.json du template
|
|
44
|
+
const packageJsonPath = path.join(targetDir, "package.json");
|
|
45
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
46
|
+
|
|
47
|
+
// Mettre à jour le nom du projet
|
|
48
|
+
packageJson.name = projectName;
|
|
49
|
+
packageJson.version = "0.1.0";
|
|
50
|
+
|
|
51
|
+
// Écrire le package.json modifié
|
|
52
|
+
fs.writeFileSync(
|
|
53
|
+
packageJsonPath,
|
|
54
|
+
JSON.stringify(packageJson, null, 2) + "\n"
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
// Créer un fichier .env
|
|
58
|
+
const envExamplePath = path.join(targetDir, ".env");
|
|
59
|
+
if (!fs.existsSync(envExamplePath)) {
|
|
60
|
+
const envExample = `# Database
|
|
61
|
+
DATABASE_URL="" # Supabase Prod
|
|
62
|
+
DIRECT_URL="" # Supabase Migration
|
|
63
|
+
|
|
64
|
+
BETTER_AUTH_SECRET=""
|
|
65
|
+
BETTER_AUTH_URL="http://localhost:3000"
|
|
66
|
+
NEXT_PUBLIC_APP_URL="http://localhost:3000"
|
|
67
|
+
|
|
68
|
+
APP_NAME="${projectName}"
|
|
69
|
+
|
|
70
|
+
ENCRYPTION_KEY="" #openssl rand -base64 32
|
|
71
|
+
|
|
72
|
+
GOOGLE_CLIENT_ID=""
|
|
73
|
+
GOOGLE_CLIENT_SECRET=""
|
|
74
|
+
GOOGLE_REDIRECT_URI=""
|
|
75
|
+
|
|
76
|
+
CRON_SECRET="" #openssl rand -base64 32
|
|
77
|
+
`;
|
|
78
|
+
fs.writeFileSync(envExamplePath, envExample);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
console.log(chalk.green("✅ Projet créé avec succès !\n"));
|
|
82
|
+
console.log(chalk.yellow("📝 Prochaines étapes :\n"));
|
|
83
|
+
console.log(chalk.white(` cd ${projectName}`));
|
|
84
|
+
console.log(chalk.white(" pnpm install"));
|
|
85
|
+
console.log(chalk.white(" # Configurez votre fichier .env"));
|
|
86
|
+
console.log(chalk.white(" # Migrer la base de données et générer le schéma Prisma"));
|
|
87
|
+
console.log(chalk.white(" pnpm dev\n"));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
main().catch((error) => {
|
|
91
|
+
console.error(chalk.red("❌ Erreur :"), error);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-crm-tmp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Créer un nouveau projet CRM basé sur le template",
|
|
5
|
+
"bin": {
|
|
6
|
+
"create-crm-tmp": "./bin/create-crm-tmp.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin",
|
|
10
|
+
"template"
|
|
11
|
+
],
|
|
12
|
+
"keywords": [
|
|
13
|
+
"crm",
|
|
14
|
+
"template",
|
|
15
|
+
"nextjs",
|
|
16
|
+
"prisma"
|
|
17
|
+
],
|
|
18
|
+
"author": "Younes – HALL-IA",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"fs-extra": "^11.2.0",
|
|
22
|
+
"chalk": "^4.1.2",
|
|
23
|
+
"prompts": "^2.4.2"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules
|
|
3
|
+
.pnp
|
|
4
|
+
.pnp.js
|
|
5
|
+
|
|
6
|
+
# Production
|
|
7
|
+
build
|
|
8
|
+
dist
|
|
9
|
+
.next
|
|
10
|
+
out
|
|
11
|
+
|
|
12
|
+
# Misc
|
|
13
|
+
.DS_Store
|
|
14
|
+
*.pem
|
|
15
|
+
|
|
16
|
+
# Debug
|
|
17
|
+
npm-debug.log*
|
|
18
|
+
yarn-debug.log*
|
|
19
|
+
yarn-error.log*
|
|
20
|
+
pnpm-debug.log*
|
|
21
|
+
|
|
22
|
+
# Local env files
|
|
23
|
+
.env*.local
|
|
24
|
+
.env
|
|
25
|
+
|
|
26
|
+
# Vercel
|
|
27
|
+
.vercel
|
|
28
|
+
|
|
29
|
+
# Lock files
|
|
30
|
+
package-lock.json
|
|
31
|
+
pnpm-lock.yaml
|
|
32
|
+
yarn.lock
|
|
33
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"semi": true,
|
|
3
|
+
"singleQuote": true,
|
|
4
|
+
"trailingComma": "all",
|
|
5
|
+
"printWidth": 100,
|
|
6
|
+
"tabWidth": 2,
|
|
7
|
+
"useTabs": false,
|
|
8
|
+
"arrowParens": "always",
|
|
9
|
+
"bracketSpacing": true,
|
|
10
|
+
"bracketSameLine": false,
|
|
11
|
+
"endOfLine": "lf",
|
|
12
|
+
"proseWrap": "preserve",
|
|
13
|
+
"htmlWhitespaceSensitivity": "ignore",
|
|
14
|
+
"embeddedLanguageFormatting": "auto",
|
|
15
|
+
"plugins": ["prettier-plugin-tailwindcss"],
|
|
16
|
+
"overrides": [
|
|
17
|
+
{
|
|
18
|
+
"files": ["*.md", "*.mdx"],
|
|
19
|
+
"options": {
|
|
20
|
+
"printWidth": 80,
|
|
21
|
+
"proseWrap": "always"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# 📊 CRM Template
|
|
2
|
+
|
|
3
|
+
Un CRM moderne, orienté **prospection & suivi commercial**, construit avec **Next.js 16**,
|
|
4
|
+
**Better Auth**, **Prisma**, **PostgreSQL** et une UI inspirée d’un template CRM pro.
|
|
5
|
+
|
|
6
|
+
## ✨ Fonctionnalités
|
|
7
|
+
|
|
8
|
+
- 🔐 **Authentification sécurisée** avec Better Auth
|
|
9
|
+
- Connexion par email / mot de passe
|
|
10
|
+
- Sessions sécurisées côté serveur
|
|
11
|
+
- Protection des routes via proxy Next.js
|
|
12
|
+
- 👥 **Rôles & permissions**
|
|
13
|
+
- Rôles natifs : USER, ADMIN, MANAGER, COMMERCIAL, TELEPRO, COMPTABLE
|
|
14
|
+
- Rôles personnalisés avec permissions granulaires
|
|
15
|
+
- Gestion des utilisateurs (activation / désactivation, invitation par email)
|
|
16
|
+
- 📊 **Tableau de bord**
|
|
17
|
+
- Vue synthétique de l’activité (contacts, tâches, rendez-vous, etc.)
|
|
18
|
+
- 📇 **Contacts**
|
|
19
|
+
- Vue tableau et cartes façon CRM moderne
|
|
20
|
+
- Recherche, filtres (statut, origine, commercial, télépro…)
|
|
21
|
+
- Tri (statut, commerciaux, dates de création / mise à jour)
|
|
22
|
+
- Import CSV / Excel avec mapping intelligent
|
|
23
|
+
- Export CSV / Excel (contacts + notes + fichiers liés, réservé aux admins)
|
|
24
|
+
- Gestion des statuts & motifs de fermeture
|
|
25
|
+
- 📆 **Agenda**
|
|
26
|
+
- Vues **Mois / Semaine / Jour**
|
|
27
|
+
- Barre temporelle en temps réel
|
|
28
|
+
- Différenciation visuelle des types (tâches, rendez-vous, visio)
|
|
29
|
+
- Filtres par type et priorité
|
|
30
|
+
- ⚙️ **Paramètres**
|
|
31
|
+
- Paramètres généraux (profil, sécurité, informations d’entreprise)
|
|
32
|
+
- Configuration SMTP + signature email
|
|
33
|
+
- Gestion des statuts & motifs de fermeture
|
|
34
|
+
- Intégrations (Google Calendar & Drive, Google Sheets, Meta Lead Ads, Google Ads…)
|
|
35
|
+
- 🤖 **Automatisations (Workflows)**
|
|
36
|
+
- Workflows avec actions chaînées
|
|
37
|
+
- Exécution planifiée via **Vercel Cron**
|
|
38
|
+
- 🎨 **UI moderne**
|
|
39
|
+
- Design type CRM (fond `bg-crms-bg`, cartes blanches, headers structurés)
|
|
40
|
+
- Tailwind CSS v4
|
|
41
|
+
- 📱 Design entièrement responsive
|
|
42
|
+
|
|
43
|
+
## 🛠️ Stack technique
|
|
44
|
+
|
|
45
|
+
- **Framework**: Next.js 16 (App Router)
|
|
46
|
+
- **React**: React 19
|
|
47
|
+
- **Base de données**: PostgreSQL avec Prisma ORM
|
|
48
|
+
- **Authentification**: Better Auth
|
|
49
|
+
- **Styling**: Tailwind CSS v4
|
|
50
|
+
- **Langage**: TypeScript
|
|
51
|
+
- **Gestionnaire de paquets**: pnpm
|
|
52
|
+
|
|
53
|
+
## 🚀 Installation
|
|
54
|
+
|
|
55
|
+
1. **Cloner le projet**
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
git clone <votre-repo>
|
|
59
|
+
cd crm-template
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
2. **Installer les dépendances**
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pnpm install
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
3. **Configurer les variables d'environnement**
|
|
69
|
+
|
|
70
|
+
Créez un fichier `.env` à la racine du projet :
|
|
71
|
+
|
|
72
|
+
```env
|
|
73
|
+
# Database
|
|
74
|
+
DATABASE_URL="postgresql://postgres:password@localhost:5432/crm_db"
|
|
75
|
+
|
|
76
|
+
# Better Auth (générer avec: openssl rand -base64 32)
|
|
77
|
+
BETTER_AUTH_SECRET="votre-clé-secrète-minimum-32-caractères"
|
|
78
|
+
BETTER_AUTH_URL="http://localhost:3000"
|
|
79
|
+
|
|
80
|
+
# Application
|
|
81
|
+
NEXT_PUBLIC_APP_URL="http://localhost:3000"
|
|
82
|
+
NODE_ENV="development"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
4. **Créer la base de données**
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
createdb crm_db
|
|
89
|
+
# Ou: psql -U postgres -c "CREATE DATABASE crm_db;"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
5. **Appliquer les migrations**
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pnpm prisma migrate deploy
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
6. **Lancer le serveur de développement**
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
pnpm dev
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Ouvrez [http://localhost:3000](http://localhost:3000) pour voir l'application.
|
|
105
|
+
|
|
106
|
+
7. **Créer votre premier admin**
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# Ouvrir Prisma Studio
|
|
110
|
+
pnpm prisma studio
|
|
111
|
+
|
|
112
|
+
# Modifier le champ "role" de votre utilisateur en "ADMIN"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## 📁 Structure du projet
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
src/
|
|
119
|
+
├── app/
|
|
120
|
+
│ ├── (auth)/ # Groupe de routes d'authentification
|
|
121
|
+
│ ├── (dashboard)/ # Espace connecté (protégé)
|
|
122
|
+
│ │ ├── dashboard/ # Tableau de bord
|
|
123
|
+
│ │ ├── contacts/ # Gestion des contacts
|
|
124
|
+
│ │ ├── agenda/ # Agenda (mois / semaine / jour)
|
|
125
|
+
│ │ ├── automatisation/ # Workflows / automatisations
|
|
126
|
+
│ │ ├── templates/ # Templates d’emails
|
|
127
|
+
│ │ ├── settings/ # Paramètres (profil, entreprise, intégrations…)
|
|
128
|
+
│ │ ├── users/ # Gestion des utilisateurs & rôles (admin)
|
|
129
|
+
│ │ └── layout.tsx # Layout avec sidebar
|
|
130
|
+
│ ├── api/ # API (contacts, workflows, intégrations, users, etc.)
|
|
131
|
+
│ └── page.tsx # Page d'accueil (redirection)
|
|
132
|
+
├── components/ # Composants UI (sidebar, headers, skeletons…)
|
|
133
|
+
├── lib/ # Auth, Prisma, rôles, intégrations Google, workflows…
|
|
134
|
+
└── proxy.ts # Protection des routes (proxy)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## 🔒 Protection & rôles
|
|
138
|
+
|
|
139
|
+
- **Proxy Next.js** (`src/proxy.ts`) pour protéger les routes côté serveur :
|
|
140
|
+
- Pages dans `(dashboard)/` automatiquement protégées
|
|
141
|
+
- Redirection vers `/signin` si non authentifié
|
|
142
|
+
- Redirection vers le dashboard si déjà connecté sur les pages d’auth
|
|
143
|
+
- **Rôles & permissions** :
|
|
144
|
+
- Rôles techniques + rôles personnalisés configurables via l’interface
|
|
145
|
+
- Vérification des permissions côté API (ex : `users.view`, `users.manage_roles`, intégrations…)
|
|
146
|
+
- Certaines sections (informations d’entreprise, paramètres d’application / système, intégrations, export contacts) sont réservées aux administrateurs
|
|
147
|
+
|
|
148
|
+
## 🎨 Personnalisation
|
|
149
|
+
|
|
150
|
+
### Ajouter une nouvelle page protégée
|
|
151
|
+
|
|
152
|
+
1. Créez votre page dans `src/app/(dashboard)/ma-page/page.tsx`
|
|
153
|
+
2. Ajoutez-la dans la navigation (`src/components/sidebar.tsx`)
|
|
154
|
+
3. (Optionnel) Protégez-la par rôle dans `src/proxy.ts`
|
|
155
|
+
|
|
156
|
+
### Modifier le thème
|
|
157
|
+
|
|
158
|
+
Les couleurs principales sont configurées avec Tailwind. Modifiez les classes
|
|
159
|
+
dans les composants pour personnaliser le thème.
|
|
160
|
+
|
|
161
|
+
## 📝 Scripts disponibles
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
pnpm dev # Lancer le serveur de développement
|
|
165
|
+
pnpm build # Build de production
|
|
166
|
+
pnpm start # Lancer le serveur de production
|
|
167
|
+
pnpm lint # Linter le code
|
|
168
|
+
pnpm format # Formater le code avec Prettier
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## 📄 Licence
|
|
172
|
+
|
|
173
|
+
MIT
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineConfig, globalIgnores } from "eslint/config";
|
|
2
|
+
import nextVitals from "eslint-config-next/core-web-vitals";
|
|
3
|
+
import nextTs from "eslint-config-next/typescript";
|
|
4
|
+
|
|
5
|
+
const eslintConfig = defineConfig([
|
|
6
|
+
...nextVitals,
|
|
7
|
+
...nextTs,
|
|
8
|
+
// Override default ignores of eslint-config-next.
|
|
9
|
+
globalIgnores([
|
|
10
|
+
// Default ignores of eslint-config-next:
|
|
11
|
+
".next/**",
|
|
12
|
+
"out/**",
|
|
13
|
+
"build/**",
|
|
14
|
+
"next-env.d.ts",
|
|
15
|
+
]),
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
export default eslintConfig;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Civilité,Prénom,Nom,Téléphone,Téléphone secondaire,Email,Adresse,Ville,Code postal,Origine
|
|
2
|
+
M,Jean,Dupont,0612345678,0698765432,jean.dupont@example.com,123 Rue de la République,Paris,75001,Google Ads
|
|
3
|
+
MME,Marie,Martin,0623456789,,marie.martin@example.com,45 Avenue des Champs,Paris,75008,Facebook
|
|
4
|
+
M,Pierre,Bernard,0634567890,0645678901,pierre.bernard@example.com,78 Boulevard Saint-Germain,Lyon,69001,LinkedIn
|
|
5
|
+
MME,Sophie,Dubois,0645678901,,sophie.dubois@example.com,12 Place Bellecour,Lyon,69002,Recommandation
|
|
6
|
+
M,Thomas,Moreau,0656789012,0667890123,thomas.moreau@example.com,56 Rue de la Paix,Marseille,13001,Google Ads
|
|
7
|
+
MME,Julie,Lefebvre,0667890123,,julie.lefebvre@example.com,89 Cours Mirabeau,Marseille,13002,Facebook
|
|
8
|
+
M,Michel,Petit,0678901234,,michel.petit@example.com,34 Rue du Commerce,Toulouse,31000,LinkedIn
|
|
9
|
+
MME,Isabelle,Roux,0689012345,0690123456,isabelle.roux@example.com,67 Avenue Jean Jaurès,Toulouse,31001,Recommandation
|
|
10
|
+
M,David,Simon,0690123456,,david.simon@example.com,23 Place du Capitole,Bordeaux,33000,Google Ads
|
|
11
|
+
MME,Claire,Laurent,0612345679,,claire.laurent@example.com,45 Rue Sainte-Catherine,Bordeaux,33001,Facebook
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "crm-template",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "next dev",
|
|
7
|
+
"build": "next build",
|
|
8
|
+
"start": "next start",
|
|
9
|
+
"postinstall": "prisma generate",
|
|
10
|
+
"lint": "eslint",
|
|
11
|
+
"format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,css,md}\"",
|
|
12
|
+
"format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,css,md}\""
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@hookform/resolvers": "^5.2.2",
|
|
16
|
+
"@lexical/html": "^0.38.2",
|
|
17
|
+
"@lexical/list": "^0.38.2",
|
|
18
|
+
"@lexical/markdown": "^0.38.2",
|
|
19
|
+
"@lexical/react": "^0.38.2",
|
|
20
|
+
"@lexical/rich-text": "^0.38.2",
|
|
21
|
+
"@lexical/selection": "^0.38.2",
|
|
22
|
+
"@lexical/utils": "^0.38.2",
|
|
23
|
+
"@lexkit/editor": "^0.0.38",
|
|
24
|
+
"@prisma/adapter-pg": "^7.1.0",
|
|
25
|
+
"@prisma/client": "^7.1.0",
|
|
26
|
+
"@react-email/render": "^2.0.0",
|
|
27
|
+
"bcryptjs": "^3.0.3",
|
|
28
|
+
"better-auth": "^1.4.6",
|
|
29
|
+
"clsx": "^2.1.1",
|
|
30
|
+
"dotenv": "^17.2.3",
|
|
31
|
+
"iron-session": "^8.0.4",
|
|
32
|
+
"lexical": "^0.38.2",
|
|
33
|
+
"lucide-react": "^0.555.0",
|
|
34
|
+
"next": "16.0.10",
|
|
35
|
+
"nodemailer": "^7.0.11",
|
|
36
|
+
"papaparse": "^5.5.3",
|
|
37
|
+
"pg": "^8.16.3",
|
|
38
|
+
"react": "19.2.3",
|
|
39
|
+
"react-dom": "19.2.3",
|
|
40
|
+
"react-hook-form": "^7.68.0",
|
|
41
|
+
"recharts": "^2.15.4",
|
|
42
|
+
"tailwind-merge": "^3.4.0",
|
|
43
|
+
"xlsx": "^0.18.5",
|
|
44
|
+
"zod": "^4.1.13"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@tailwindcss/postcss": "^4.1.18",
|
|
48
|
+
"@types/node": "^20.19.26",
|
|
49
|
+
"@types/nodemailer": "^7.0.4",
|
|
50
|
+
"@types/papaparse": "^5.5.1",
|
|
51
|
+
"@types/pg": "^8.16.0",
|
|
52
|
+
"@types/react": "^19.2.7",
|
|
53
|
+
"@types/react-dom": "^19.2.3",
|
|
54
|
+
"babel-plugin-react-compiler": "1.0.0",
|
|
55
|
+
"eslint": "^9.39.1",
|
|
56
|
+
"eslint-config-next": "16.0.7",
|
|
57
|
+
"prettier": "^3.7.4",
|
|
58
|
+
"prettier-plugin-tailwindcss": "^0.7.2",
|
|
59
|
+
"prisma": "^7.1.0",
|
|
60
|
+
"tailwindcss": "^4.1.18",
|
|
61
|
+
"tsx": "^4.21.0",
|
|
62
|
+
"typescript": "^5.9.3"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
-- CreateTable
|
|
2
|
+
CREATE TABLE "user" (
|
|
3
|
+
"id" TEXT NOT NULL,
|
|
4
|
+
"name" TEXT NOT NULL,
|
|
5
|
+
"email" TEXT NOT NULL,
|
|
6
|
+
"emailVerified" BOOLEAN NOT NULL DEFAULT false,
|
|
7
|
+
"image" TEXT,
|
|
8
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
9
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
10
|
+
|
|
11
|
+
CONSTRAINT "user_pkey" PRIMARY KEY ("id")
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
-- CreateTable
|
|
15
|
+
CREATE TABLE "session" (
|
|
16
|
+
"id" TEXT NOT NULL,
|
|
17
|
+
"expiresAt" TIMESTAMP(3) NOT NULL,
|
|
18
|
+
"token" TEXT NOT NULL,
|
|
19
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
20
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
21
|
+
"ipAddress" TEXT,
|
|
22
|
+
"userAgent" TEXT,
|
|
23
|
+
"userId" TEXT NOT NULL,
|
|
24
|
+
|
|
25
|
+
CONSTRAINT "session_pkey" PRIMARY KEY ("id")
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
-- CreateTable
|
|
29
|
+
CREATE TABLE "account" (
|
|
30
|
+
"id" TEXT NOT NULL,
|
|
31
|
+
"accountId" TEXT NOT NULL,
|
|
32
|
+
"providerId" TEXT NOT NULL,
|
|
33
|
+
"userId" TEXT NOT NULL,
|
|
34
|
+
"accessToken" TEXT,
|
|
35
|
+
"refreshToken" TEXT,
|
|
36
|
+
"idToken" TEXT,
|
|
37
|
+
"accessTokenExpiresAt" TIMESTAMP(3),
|
|
38
|
+
"refreshTokenExpiresAt" TIMESTAMP(3),
|
|
39
|
+
"scope" TEXT,
|
|
40
|
+
"password" TEXT,
|
|
41
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
42
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
43
|
+
|
|
44
|
+
CONSTRAINT "account_pkey" PRIMARY KEY ("id")
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
-- CreateTable
|
|
48
|
+
CREATE TABLE "verification" (
|
|
49
|
+
"id" TEXT NOT NULL,
|
|
50
|
+
"identifier" TEXT NOT NULL,
|
|
51
|
+
"value" TEXT NOT NULL,
|
|
52
|
+
"expiresAt" TIMESTAMP(3) NOT NULL,
|
|
53
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
54
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
55
|
+
|
|
56
|
+
CONSTRAINT "verification_pkey" PRIMARY KEY ("id")
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
-- CreateIndex
|
|
60
|
+
CREATE UNIQUE INDEX "user_email_key" ON "user"("email");
|
|
61
|
+
|
|
62
|
+
-- CreateIndex
|
|
63
|
+
CREATE INDEX "session_userId_idx" ON "session"("userId");
|
|
64
|
+
|
|
65
|
+
-- CreateIndex
|
|
66
|
+
CREATE UNIQUE INDEX "session_token_key" ON "session"("token");
|
|
67
|
+
|
|
68
|
+
-- CreateIndex
|
|
69
|
+
CREATE INDEX "account_userId_idx" ON "account"("userId");
|
|
70
|
+
|
|
71
|
+
-- CreateIndex
|
|
72
|
+
CREATE INDEX "verification_identifier_idx" ON "verification"("identifier");
|
|
73
|
+
|
|
74
|
+
-- AddForeignKey
|
|
75
|
+
ALTER TABLE "session" ADD CONSTRAINT "session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
76
|
+
|
|
77
|
+
-- AddForeignKey
|
|
78
|
+
ALTER TABLE "account" ADD CONSTRAINT "account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
-- CreateTable
|
|
2
|
+
CREATE TABLE "company" (
|
|
3
|
+
"id" TEXT NOT NULL DEFAULT 'company',
|
|
4
|
+
"name" TEXT,
|
|
5
|
+
"address" TEXT,
|
|
6
|
+
"city" TEXT,
|
|
7
|
+
"postalCode" TEXT,
|
|
8
|
+
"country" TEXT,
|
|
9
|
+
"phone" TEXT,
|
|
10
|
+
"email" TEXT,
|
|
11
|
+
"website" TEXT,
|
|
12
|
+
"siret" TEXT,
|
|
13
|
+
"vatNumber" TEXT,
|
|
14
|
+
"logo" TEXT,
|
|
15
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
16
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
17
|
+
|
|
18
|
+
CONSTRAINT "company_pkey" PRIMARY KEY ("id")
|
|
19
|
+
);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
-- CreateTable
|
|
2
|
+
CREATE TABLE "smtp_config" (
|
|
3
|
+
"id" TEXT NOT NULL,
|
|
4
|
+
"userId" TEXT NOT NULL,
|
|
5
|
+
"host" TEXT NOT NULL,
|
|
6
|
+
"port" INTEGER NOT NULL,
|
|
7
|
+
"secure" BOOLEAN NOT NULL DEFAULT false,
|
|
8
|
+
"username" TEXT NOT NULL,
|
|
9
|
+
"password" TEXT NOT NULL,
|
|
10
|
+
"fromEmail" TEXT NOT NULL,
|
|
11
|
+
"fromName" TEXT,
|
|
12
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
13
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
14
|
+
|
|
15
|
+
CONSTRAINT "smtp_config_pkey" PRIMARY KEY ("id")
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
-- CreateIndex
|
|
19
|
+
CREATE UNIQUE INDEX "smtp_config_userId_key" ON "smtp_config"("userId");
|
|
20
|
+
|
|
21
|
+
-- AddForeignKey
|
|
22
|
+
ALTER TABLE "smtp_config" ADD CONSTRAINT "smtp_config_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
-- CreateTable
|
|
2
|
+
CREATE TABLE "status" (
|
|
3
|
+
"id" TEXT NOT NULL,
|
|
4
|
+
"name" TEXT NOT NULL,
|
|
5
|
+
"color" TEXT NOT NULL,
|
|
6
|
+
"order" INTEGER NOT NULL DEFAULT 0,
|
|
7
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
8
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
9
|
+
|
|
10
|
+
CONSTRAINT "status_pkey" PRIMARY KEY ("id")
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
-- CreateIndex
|
|
14
|
+
CREATE UNIQUE INDEX "status_name_key" ON "status"("name");
|
|
15
|
+
|
|
16
|
+
-- Insert default statuses (ignore if already exist)
|
|
17
|
+
INSERT INTO "status" ("id", "name", "color", "order", "createdAt", "updatedAt") VALUES
|
|
18
|
+
(gen_random_uuid()::text, 'Nouveau', '#3B82F6', 1, NOW(), NOW()),
|
|
19
|
+
(gen_random_uuid()::text, 'À rappeler', '#F59E0B', 2, NOW(), NOW()),
|
|
20
|
+
(gen_random_uuid()::text, 'RDV pris', '#8B5CF6', 3, NOW(), NOW()),
|
|
21
|
+
(gen_random_uuid()::text, 'Converti (contrat signé)', '#10B981', 4, NOW(), NOW()),
|
|
22
|
+
(gen_random_uuid()::text, 'Perdu', '#EF4444', 5, NOW(), NOW())
|
|
23
|
+
ON CONFLICT ("name") DO NOTHING;
|